Laravel Gemini AI Translation Synchronization Tool Documentation
Overview
The Laravel Gemini AI Translation Synchronization Tool is an Artisan command and a browser
Translation Manager for modern Laravel apps, including those built with
nwidart/laravel-modules.
It discovers keys from your code, existing language files, registered custom lang directories,
and the framework, then synchronizes them across target languages with Gemini AI. Extra lang
folders such as lang/app3/ and
lang/web/ are handled as separate packs. Linux/macOS use
Laravel Prompts and spatie/fork; Windows uses matching
kernel32 menus and Symfony Process workers for parallel
chunks.
--refresh or
--refresh-clean for bad rows. The
AI-First Translation section explains what the guard
catches and what it does not.
-
Full Module Support
Scans stay isolated between the main app and modules. You can keep module files separate or consolidate them into the root
lang/directory. Packs such aslang/app3/andlang/web/are selected on their own. -
AI-First Translation
Prompts send source text and per-locale script rules. Namespaced keys and literal strings are both supported. Mixed-script or leftover-English output is reduced, not eliminated. Remaining AI mistakes still need a human review.
-
True Synchronization
Discovers keys from your code and existing files to create a complete picture, ensuring no translation is left behind. Use
--skip-existingto only add missing keys,--refreshto re-translate from file wording, or--refresh-cleanfor a key-only rebuild. -
High Performance
Unix uses the
forkdriver (pcntl+ Spatie). Windows uses theprocessdriver (Symfony Process workers) for the same kind of parallel chunks.syncis sequential on every OS. Non-interactive CI/CD skips prompts. -
Production-Ready Safety
Atomic file writes with path validation, framework translation bootstrapping, mixed-script checks that reduce (not eliminate) bad Gemini rows, and
--dry-runpreviews. -
Translation Manager
Open
/translations-managerto browse, edit, save, scan, and add languages. Filters cover type, module, pack, scope, PHP files, language, missing keys, and script faults. Auth follows the host app.
What's new in 5.1.x
Documentation updated for v5.1.1 (28 August 2026). The complete release history lives in the CHANGELOG.md.
v5.1.1 Modernization Update (2026-08-28)
- PHP 8.3 & Laravel Refactoring: Integrated native
#[Override]annotations and optimized exceptions usingthrow_if/throw_unlesshelpers. - Cross-Platform npm Tooling: Introduced the package's first JS quality tooling (
package.json,eslint.config.js, Prettier) to format and lint Blade-embedded JS, utilizing a file crawler written in pure Node.js for native cross-platform Windows, macOS, and Linux support. - Robust Testing Environment: Bypassed Testbench dummy application paths in vendor scanning, added standard app key configurations to phpunit, and added fallback catalog resolution logic.
Added
- Translation Manager at
/translations-manager(prefix, enable flag, and middleware are configurable). Routes and JSON APIs register automatically. Guests must sign in when the host has login/register routes; otherwise the page is open. Publish tags:gemini-translator-config,gemini-translator-views,gemini-translator-assets,gemini-translator-manager. Filters: Type, Module, Pack, Scope, PHP files, Language, show-only-missing, Highlight script faults. Theme switch, sticky KEY column, pagination (default 5 rows, last two pages always shown). - Manager script-fault highlighter: optional checkbox (off by default,
localStoragekeygemini-translator-malform-detector). Marks cells whose letters do not belong to that locale, including leftover Latin in Gujarati or Hindi. Placeholders are ignored. Amber outline plus a tooltip of the unexpected scripts. Powered byLocaleHelper::malformReasons(). - Lang packs in the manager and the CLI. Extra folders such as
lang/app3/andlang/web/stay separate fromlang/. Custom directories registered withloadJsonTranslationsFrom()/loadTranslationsFrom()and publishedresources/lang/modules/{name}are scanned. Pack PHP writes tolang/{pack}/{locale}/…. - 249-language catalog with fallbacks such as
zh→zh_CN. - Multi-OS adapters: Unix Prompts + fork; Windows
kernel32menus + Symfony Process.--driver=processon every OS. Hiddentranslations:run-payloadfor child jobs. --refresh-cleanrebuilds existing keys from the key (official Laravel English for auth / pagination / passwords / validation).- Dated quota snapshot in
config/gemini-translator.php(as of 2026-08-13), plusGEMINI_TRANSLATOR_APPLY_FREE_TIER_CAPS. - Configurable model:
--model,GEMINI_MODEL/GEMINI_TRANSLATOR_MODEL, then config. Defaultgemini-3.5-flash-lite. - Per-locale writing-system map (Tibetan, N’Ko, Tifinagh, Ol Chiki, Meitei, extra
Devanagari/Cyrillic/Arabic codes, and script tags such as
_latn/_arab) plus checks that reduce mixed-script / leftover-English output. They do not eliminate it.
Changed
--refreshuses current file wording as the source.- On Windows,
--driver=forkruns Process workers. FFI menus match Laravel Prompts. - JSON keeps dotted PHP-style keys. Blank literal JSON values fill from the key.
- Command summary records
processed_chunks. - CLI writing-system check reduces mixed-script Gemini output (for example Gujarati +
Kannada
ೋ). It does not stop every mixed or leftover-English row.
Fixed (relative to 5.0.1 CLI)
- JSON-only selection does not re-translate PHP
file.subkeykeys. - Selecting a single CLI file group returns that file.
Installation
Step 1: Install via Composer
composer require jayesh/laravel-gemini-translator
Step 2: Publish Configuration
php artisan vendor:publish --tag=gemini-translator-config
That publishes config/gemini-translator.php (model, quota
snapshot, Translation Manager). The API client’s
config/gemini.php comes from
google-gemini-php/laravel, not this tag.
The manager is already registered at
/translations-manager. You do not have to publish views for it
to work. Publish only if you want to edit those files in the app.
Publishable tags
| Tag | Copies | Required? |
|---|---|---|
gemini-translator-config |
config/gemini-translator.php (model, quota
snapshot, manager prefix / enable / middleware) |
No. Package config is merged without publishing. Publish to edit quotas or the manager URL. |
gemini-translator-views |
resources/views/vendor/gemini-translator/
(manager.blade.php,
partials/workspace.blade.php,
errors/unauthenticated.blade.php) |
No. Laravel then prefers these copies over the package. Delete the folder to load the package views again. |
gemini-translator-assets |
public/vendor/gemini-translator/
(css/manager.css,
js/manager.js) |
No. CSS/JS are inlined from the package unless you pass
$assetCss /
$assetJs. |
gemini-translator-manager |
Views and assets (both destinations above) | No. Convenience tag for customizing the manager UI. |
# Config only
php artisan vendor:publish --tag=gemini-translator-config
# Views only
php artisan vendor:publish --tag=gemini-translator-views
# Public CSS/JS only
php artisan vendor:publish --tag=gemini-translator-assets
# Views + CSS/JS
php artisan vendor:publish --tag=gemini-translator-manager
# Overwrite after a package upgrade
php artisan vendor:publish --tag=gemini-translator-manager --force
php artisan view:clear
config/gemini.php is published by
google-gemini-php/laravel, not by these tags. PHP from this
package is never published.
Step 3: Add API Key
Add your Gemini API key to your .env file. You can get a key
from
the Google AI Studio.
GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
GEMINI_API_KEY, it will automatically switch to Offline Mode,
generating placeholder files so your application doesn't break.
Configuration
Environment Variables
Add these variables to your .env file:
GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
GEMINI_REQUEST_TIMEOUT=600
# Model (CLI --model wins, then these, then config)
GEMINI_MODEL="gemini-3.5-flash-lite"
# GEMINI_TRANSLATOR_MODEL="gemini-3.5-flash-lite"
# Free-tier snapshot caps (set false for paid keys)
# GEMINI_TRANSLATOR_APPLY_FREE_TIER_CAPS=true
# Translation Manager
# GEMINI_TRANSLATOR_MANAGER=true
# GEMINI_TRANSLATOR_MANAGER_PREFIX=translations-manager
# GEMINI_TRANSLATOR_MANAGER_MIDDLEWARE=web
Getting Your API Key
1. Visit Google AI
Studio
2. Sign in with your Google account
3. Create a new API key
4. Copy and paste it into your .env file
config/gemini-translator.php (dated 2026-08-13). They are not
an API contract. Publish the file and edit a row, add a model, or set
0 when Google changes quotas. Default concurrency follows a
recorded positive RPM unless you pass --concurrency or set
GEMINI_TRANSLATOR_APPLY_FREE_TIER_CAPS=false.
Configuration Files
config/gemini.php (from
google-gemini-php/laravel) holds the API key and request
timeout. Cast timeout to int:
'request_timeout' => (int) env('GEMINI_REQUEST_TIMEOUT', 600),
config/gemini-translator.php (this package) holds:
- Default Gemini model (
gemini-3.5-flash-lite) - Dated free-tier quota snapshot
- Translation Manager enable flag, URL prefix, and middleware
Usage
Basic Command
Run the command to start the interactive process. It asks for targets, then packs (only if extra lang folders exist), then JSON/PHP files.
php artisan translations:extract-and-generate
Pick a model for one run with --model:
php artisan translations:extract-and-generate --model=gemini-2.5-pro --langs=en,hi
Platform-Specific Recommendations
For the best experience, specify a driver based on your operating system.
# Linux/macOS/WSL (Prompts + fork)
php artisan translations:extract-and-generate --driver=fork
# Windows (same flag; 5.1.x runs Symfony Process workers)
php artisan translations:extract-and-generate --driver=fork --concurrency=10
# Sequential on any OS
php artisan translations:extract-and-generate --driver=sync
Four Operational Modes
5.1.x offers four operational modes. --refresh,
--refresh-clean, and
--skip-existing cannot be combined.
1. Normal Mode (Default)
Translates all keys, both existing and new. This is the default behavior.
2. Skip-Existing Mode
To add new translations without overwriting existing ones, use the --skip-existing flag. This is the recommended workflow for
ongoing
projects.
# Find and translate only the keys that are missing in Spanish and German
php artisan translations:extract-and-generate --skip-existing --langs=es,de
3. Refresh Mode
Re-translate only existing keys, using the current file wording as the source. Does not add new keys.
php artisan translations:extract-and-generate --refresh --langs=es,de
4. Refresh-Clean Mode
Re-translate only existing keys, ignoring stale file text. Source is the key itself, except
official Laravel English for auth,
pagination, passwords, and
validation.
php artisan translations:extract-and-generate --refresh-clean --langs=es,de
Translation Manager
The package registers the page and JSON APIs. Open:
/translations-manager
Example: http://127.0.0.1:8000/translations-manager
Auth
- If the host already has login/register/sign-in routes, guests are redirected (JSON APIs return 401).
- If the host has no auth routes, the manager is open.
What it scans
App lang/, Modules/*/lang,
published resources/lang/modules/{name} (published wins), and
extra directories registered with
loadJsonTranslationsFrom() /
loadTranslationsFrom().
Filters
Type → Module (when Type is Module) → Pack (only if that source has more than
lang/) → Scope → PHP files (when Scope is PHP) → Language →
show only missing → Highlight script faults. A selected language lists only
keys that exist in that locale file.
Script faults
The highlighter is off until you check it. The choice is stored as
gemini-translator-malform-detector. Rules come from
LocaleHelper::detectorCatalog() (same 249-language map as the
CLI). A cell is marked when it contains a writing system that locale does not use — for example
Devanagari or Latin inside gu, or Gujarati inside
en. Laravel placeholders
(:name, {0},
[2,*]) and Common marks such as danda
। are ignored. Faulty editors use an amber outline and a
tooltip of the unexpected scripts. This is review-only; it does not rewrite files. The CLI
hasDisallowedScript() check still allows Latin tokens in native
scripts; the highlighter is stricter.
UI
Light/dark theme (stored as gemini-translator-theme), sticky KEY
column, pagination (default 5 rows, last two pages always shown), Add Languages, Sync, and Save.
CSS is semantic component CSS, not Tailwind or Bootstrap utilities.
Optional publish (only if you want to edit Blade/CSS/JS):
php artisan vendor:publish --tag=gemini-translator-manager
After a package upgrade, either overwrite those copies or go back to the package files:
# Overwrite published views + assets
php artisan vendor:publish --tag=gemini-translator-manager --force
php artisan view:clear
# Or delete the published views so Laravel loads the package copies again
rm -rf resources/views/vendor/gemini-translator
php artisan view:clear
There is no unpublish command. PHP from the package is never
published; composer update always uses the new code. Stale UI
almost always means an old published Blade.
Or drop the workspace into an existing layout:
@include('gemini-translator::partials.workspace')
Module Support (nwidart/laravel-modules)
The tool is designed from the ground up to support modular applications. Scans are now correctly isolated, so selecting the "Main Application" will not incorrectly scan `Modules/` directories, preventing key contamination.
1. Select Scan Targets
First, you will be prompted to choose which parts of your application to scan.
┌ Which parts of the application would you like to scan and process? ┐
│ Module: Settings │
└────────────────────────────────────────────────────────────────────┘
2. Language packs
If the selected module (or the main app) has extra lang folders such as
lang/app3/ or lang/web/, the
next prompt is a multi-select for those packs. The file list then only includes JSON and PHP
from the packs you chose. Modules with only lang/ skip this
step. The Translation Manager uses the same pack filter.
┌ Which language packs would you like to process? ┐
│ lang/ │
│ lang/app3/ │
│ lang/web/ │
└─────────────────────────────────────────────────┘
3. Consolidating Module Translations
If you select any modules, you are asked whether to consolidate writes into the main
lang/ directory.
┌ Consolidate all module translations into the main application's `lang` directory? ┐
│ No │
└───────────────────────────────────────────────────────────────────────────────────┘
- Default (No): Translations are kept separate. A key from the `Settings` module is written to `Modules/Settings/lang/en.json`.
- Consolidate (Yes): All translations, regardless of origin, are written to the main application's `lang` directory (e.g., `lang/en.json`).
You can bypass this prompt for automated scripts using the --consolidate-modules flag.
Advanced Usage & Output
Fork Driver Output (Linux/macOS)
The fork driver (pcntl +
Spatie) shows a real-time progress bar. Cooperative stop
(--stop-key, default q) does
not interrupt an in-flight fork or Process batch. Use
sync if you need to stop between chunks.
✅ Key discovery complete! Found 451 unique keys from all sources combined.
📊 Total keys needing translation: 7
📦 Total chunks to process: 2
⚡ Using 'fork' driver for high-performance concurrency.
🚀 7/7 [============================] 100% -- ✅ Chunk 2/2 - SUCCESS (5 keys) ⏱️ 3 s
╔═ 💾 Phase 3: Writing Language Files ════════════════════════════════════╗
✅ Updated: lang/en/auth.php (8 total keys)
✅ Updated: lang/ru/auth.php (8 total keys)
✅ Updated: lang/uz/auth.php (8 total keys)
Process Driver (Windows parallel)
On Windows, --driver=fork and
--driver=process run Symfony Process workers. That is not
Spatie fork (pcntl is Unix-only). You still get parallel
chunks, ordered results, inherited env, and the same “cannot stop mid-flight” behaviour as
Unix fork. Menus use kernel32 FFI when
ext-ffi is loaded; otherwise Symfony
choice() / confirm().
php artisan translations:extract-and-generate --driver=fork --concurrency=10 --langs=gu,hi
Sync Driver Output (sequential)
The sync driver processes chunks one after another on any OS.
Use it when you want cooperative stop, or when Process/fork is unavailable.
✅ Key discovery complete! Found 222 unique keys from all sources combined.
🐌 Running in synchronous mode - this will be slower but more stable!
-> Processing chunk 1/3... ✓ Done
-> Processing chunk 2/3... ✓ Done
-> Processing chunk 3/3... ✗ Failed
Error: File: __MAIN__::__JSON__, Keys: An unknown error occurred.,... - Error: Syntax error
╔═ 💾 Phase 3: Writing Language Files ═══════════════════════════════════════╗
✅ Wrote: lang/es.json (222 total keys)
✅ Wrote: lang/fr.json (222 total keys)
Example 1: Updating a Single Module (Separate)
This output shows the workflow for updating only the `Settings` module and keeping its translations separate.
Scanning 1 target(s): Module: Settings
┌ Which language packs would you like to process? ┐
│ lang/web/ │
└─────────────────────────────────────────────────┘
┌ Consolidate all module translations into the main application's `lang` directory? ┐
│ No │
└───────────────────────────────────────────────────────────────────────────────────┘
Found missing translations needing synchronization:
File: Module: Settings -> JSON File (web/*.json)
-> Language 'en' is missing 36 keys.
...
⚡ Using 'fork' driver for high-performance concurrency.
🚀 36/36 [============================] 100% -- ✅ Chunk 1/1 - SUCCESS (36 keys) ⏱️ 12 s
╔═ 💾 Phase 3: Writing Language Files ════════════════════════════════════╗
✅ Updated: /.../Modules/Settings/lang/web/en.json (104 total keys)
✅ Updated: /.../Modules/Settings/lang/web/ru.json (104 total keys)
✅ Updated: /.../Modules/Settings/lang/web/uz.json (104 total keys)
Example 2: Updating Only the Main Application
Notice how the scan is now correctly isolated to the main application, even though modules exist in the project.
Scanning 1 target(s): Main Application
Reading existing language files from selected targets...
...
Found missing translations needing synchronization:
File: Main Application -> JSON File (web/*.json)
-> Language 'en' is missing 34 keys.
...
🚀 34/34 [============================] 100% -- ✅ Chunk 1/1 - SUCCESS (34 keys) ⏱️ 12 s
╔═ 💾 Phase 3: Writing Language Files ════════════════════════════════════╗
✅ Updated: /.../lang/web/en.json (485 total keys)
✅ Updated: /.../lang/web/ru.json (485 total keys)
✅ Updated: /.../lang/web/uz.json (485 total keys)
Supported Functions & Patterns
The scanner is pre-configured to find keys used in the most common Laravel and JavaScript localization functions.
| Pattern | Example | Typical Context |
|---|---|---|
__(), trans(), @lang() |
__('messages.welcome') |
PHP & Blade |
trans_choice(), @choice() |
trans_choice('messages.apples', 5) |
PHP & Blade (Pluralization) |
$t(), i18n.t() |
$t('user.profile') |
Vue.js, JavaScript (i18n Libraries) |
v-t, x-text, :v-t, :x-text |
<span v-t="'buttons.submit'"> |
Vue.js & Alpine.js (including bound attributes) |
Lang::get(), Lang::choice() |
Lang::get('messages.welcome') |
Laravel facade |
Logging & Debugging
For transparency and debugging, the command creates two log files in your project root:
translation_extraction_log.json: A detailed log of every unique key found in your *code* and the file paths where it was located.failed_translation_keys.json: A list of any keys that failed to be translated by the AI, so you can easily retry them.
AI-First Translation
This tool adopts an "AI-First" philosophy. Instead of trying to pre-process translation keys into human-readable text (which can be buggy), it trusts the AI to correctly interpret the keys based on Laravel conventions.
How the AI Interprets Keys
The prompt includes source text and script rules. Gemini is asked to handle two kinds of keys:
| Key Type | Example Key | AI's Interpretation |
|---|---|---|
| Namespaced (PHP-style) | auth.failed |
"Based on Laravel conventions, this means an authentication failure. I will generate 'These credentials do not match our records.'" |
| Literal (JSON-style) | Save Changes |
"This is a literal string. I will translate 'Save Changes' directly." |
Writing-system guard
Each locale is mapped to one Unicode writing system (sc=, so
shared marks such as danda । are Common). Gemini output that
mixes neighbor scripts (for example Gujarati plus Kannada
ೋ) or leaves English sentences in an Indic locale is
sometimes rejected and the source is kept. The check reduces how often that happens; it
does not catch every mixed or leftover-English row. The prompt also sends source text and script
rules. In the Translation
Manager, Highlight script faults uses the same map (plus leftover Latin in
native-script locales) to outline bad cells while you edit.
The guard only looks at writing systems. It does not judge meaning. A Gujarati string that is grammatically odd, too free, or mixes Hindi words in Gujarati script will usually pass. Brand names, leftover Latin in a native-script locale, and mixed neighbor scripts are the cases the manager highlighter is meant to surface. Human review is still required for production copy.
The catalog covers 249 language codes, including
zh_CN / zh_TW,
pt_BR / pt_PT, and Punjabi
Gurmukhi/Shahmukhi. Short codes such as zh resolve to the
primary variant.
Intelligent Fallbacks
The tool no longer generates useless placeholders like "NEEDS TRANSLATION". If the AI fails to provide a translation for
a
key, the system now uses this intelligent fallback logic:
- It first looks for an existing English translation for that key in your language files.
- If none is found, it uses the key itself as the placeholder text.
This ensures your generated files are always usable and immediately show you the source text that needs attention, dramatically speeding up the manual review process.
Placeholder Mismatch Detection
Added robust placeholder validation with count checking instead of just presence. This ensures:
- Source text placeholders like
:attribute,:count,{0},{name}are preserved in translations - Count mismatches are detected and the source text is used as fallback to prevent runtime errors
- Translation quality is maintained across all language families
Improving Accuracy with the --context Flag
For domain-specific terms, you can guide the AI with the --context
flag. This is crucial for avoiding ambiguity.
# For a financial trading app, this context ensures accuracy.
php artisan translations:extract-and-generate \
--context="A SaaS platform for financial trading. 'Position' and 'security' refer to financial assets."
Command Options
| Option | Description |
|---|---|
--langs |
Comma-separated language codes to translate to (e.g., es,fr,de).
Default is en (used as
source).
|
--driver |
Concurrency driver: fork (Unix Prompts +
pcntl; on Windows 5.1.x maps to Process),
process (Symfony Process workers), or
sync (sequential, any OS).
|
--skip-existing |
A flag to only find and translate keys that are missing from target language files. Ideal for updates. |
--context |
Provide a project-specific description to Gemini for more accurate, domain-aware translations. |
--chunk-size |
Number of keys to send to the AI in a single request. Default: 25. |
--concurrency |
Number of concurrent workers for the fork and
process drivers. Default: 15 (capped to a
recorded free-tier RPM unless you override or turn caps off). |
--refresh |
Re-translate only existing keys, using current file wording as the source. Does not add new keys. |
--refresh-clean |
Like --refresh, but ignores stale file wording.
Official Laravel English is used for
auth,
pagination,
passwords, and
validation. |
--model |
Gemini model id. Overrides GEMINI_MODEL /
config. Default: gemini-3.5-flash-lite. |
--dry-run |
Run full extraction + mapping but show what files would be modified without writing anything. |
--stop-key |
Key that requests a graceful stop (default q).
Honoured between chunks on sync. Fork and
Process do not stop a batch that is already running. |
--consolidate-modules |
Consolidate all module translations into the main application's lang directory, skipping the interactive prompt. |
--target-dir |
Root directory for the translation files within the app/modules. Default: "lang". |
--exclude |
Comma-separated directories to exclude from the scan. |
--extensions |
Comma-separated file extensions to search within. |
--max-retries |
Maximum number of retries for a failed API call. Default: 5. |
--retry-delay |
Base delay in seconds between retries (uses exponential backoff). Default: 3. |
Usage Examples
Fresh Translation for a Modular Project
# Generate all translations for the main app and all modules
php artisan translations:extract-and-generate --driver=fork --langs=es,fr
Synchronize a Single Module (Separate)
# Interactively select only the 'Settings' module.
# If it has lang/app3 or lang/web, pick those packs next.
# When prompted, choose NOT to consolidate.
php artisan translations:extract-and-generate --driver=fork --skip-existing
Translate All Modules into Main `lang` Directory
# Scan all targets (main app + all modules).
# The --consolidate-modules flag ensures all translations land in the root lang/ dir.
php artisan translations:extract-and-generate --driver=fork --consolidate-modules
Update Only the Main Application (Ignoring Modules)
# Interactively select ONLY the "Main Application".
# The scanner will now correctly ignore the Modules/ directory.
php artisan translations:extract-and-generate --driver=fork --skip-existing
High-Performance Synchronization
# Use fork driver with smaller chunks to find and fill only missing keys
php artisan translations:extract-and-generate --driver=fork --chunk-size=25 --skip-existing
Context-Aware Update for a Specific Module
# Select the 'ECommerce' module, then provide context for its specific terminology
php artisan translations:extract-and-generate --skip-existing \
--context="An e-commerce module. 'Shipment' refers to a package delivery."
Automated Run in a CI/CD Pipeline
# This command runs non-interactively, great for a GitHub Action.
# It skips prompts, consolidates modules, and only syncs missing Spanish keys.
php artisan translations:extract-and-generate --langs=es --skip-existing --consolidate-modules
Open the Translation Manager
# No extra route registration. Visit:
# http://127.0.0.1:8000/translations-manager
Dry Run to Preview Changes
# Preview what would be changed without writing any files
php artisan translations:extract-and-generate --dry-run --langs=es,fr
Refresh Only Existing Translations
# Current file wording is the source
php artisan translations:extract-and-generate --refresh --langs=es,fr
# Key-only rebuild (official Laravel English for auth/pagination/passwords/validation)
php artisan translations:extract-and-generate --refresh-clean --langs=es,fr
Custom Concurrency
# Unix fork, or Windows Process (same flag)
php artisan translations:extract-and-generate --driver=fork --concurrency=20
# Explicit Process driver
php artisan translations:extract-and-generate --driver=process --concurrency=10 --langs=gu,hi
# One-off model
php artisan translations:extract-and-generate --model=gemini-2.5-pro --langs=en,hi
Troubleshooting
| Error / Issue | Common Cause | Solution |
|---|---|---|
| "Invalid API key" | The GEMINI_API_KEY in your .env file is incorrect or missing. |
Verify the key is correct and has no extra spaces. Run php artisan config:cache after changes. |
| "Configuration value must be an integer, string given" | The config/gemini.php file has incorrect type
casting for request_timeout. |
Edit config/gemini.php and change:'request_timeout' => (int) env('GEMINI_REQUEST_TIMEOUT', 600),Not: env('GEMINI_REQUEST_TIMEOUT', 600)
(without cast)
|
| "Syntax error" from Gemini / Low success rate | The AI's safety filters may have been triggered by a batch of keys, or the model returned a malformed response. | Reduce the chunk size significantly. Try --chunk-size=20 or lower and re-run. Check
`failed_translation_keys.json` for problematic keys. |
| "Quota exceeded" or "Rate limit" errors | The free tier API limit has been reached. | Lower the chunk size with --chunk-size=25
and/or
wait a few minutes before retrying. The tool has built-in exponential backoff.
|
| "Call to undefined function pcntl_fork()" | The pcntl extension is missing. In 5.1.x,
--driver=fork on Windows uses Process workers
instead of pcntl. |
On Windows, use --driver=fork or
--driver=process. On Unix without
pcntl, use
--driver=sync or
--driver=process. |
| Script hangs or runs very slowly | Processing a very large number of keys with the sync driver. |
Use --driver=fork on Linux/macOS, or the same
flag (Process workers) on Windows. Use
sync only when you need sequential runs. |
| Keys from a module appear in the main app's files. | You selected to consolidate translations either via the prompt or the
--consolidate-modules flag.
|
This is intended behavior when consolidation is active. If you want separate files, re-run the command and choose "No" when prompted about consolidation. |
| The command ran automatically without asking me any questions. | You are running in a non-interactive environment (like a CI/CD pipeline, Docker, or Git Bash on Windows). | This is the new intended behavior for automation. The tool automatically
proceeds
without prompts. If you need to limit the scope, use flags like --skip-existing. |
| All my PHP translation arrays were reordered after running the command. | This is an intentional, one-time change introduced in v4.0.1 (retained in v4.0.2 and v4.0.3) | The tool now performs a recursive sort on all PHP arrays to ensure a stable, deterministic order. This makes future code reviews (diffs) much cleaner. You should commit this one-time change. |
| "You cannot use --refresh and --skip-existing together" | You combined --refresh,
--refresh-clean, and/or
--skip-existing. |
Those three flags are mutually exclusive. Pick one mode per run. |
| Manager redirects to login / JSON 401 | The host app already has login or register routes. | Sign in with the host app. If the app has no auth routes, the manager is open. |
| Published manager Blade is missing Pack / theme / rows 5 | Laravel prefers
resources/views/vendor/gemini-translator over
the package views. |
Republish with --force:
php artisan vendor:publish --tag=gemini-translator-manager --force
and php artisan view:clear. Or delete
resources/views/vendor/gemini-translator so
the package views load again. Laravel has no unpublish command. |
| No “Highlight script faults” checkbox | Published manager Blade/JS is older than the package. | Republish --tag=gemini-translator-manager or
delete resources/views/vendor/gemini-translator. |
| Mixed-script, other language, or unexpected wording | Gemini is a generative model. It can mix neighbor scripts, include another language, or return wording that is not literal. The writing-system guard rejects some mixed-script rows and keeps the source; it does not catch meaning or tone. | Treat generated files as a draft. In the manager, turn on
Highlight script faults. Re-run
--refresh or
--refresh-clean for that language. Use
--context for domain terms. |
| “Cannot write … PHP process cannot create files” | The user that runs PHP (php-fpm, Apache, nginx, or
artisan serve) cannot write that path. That
user is not always www-data. |
The error names the process user when POSIX is available. Give that user write
access on lang/ (and module lang trees), for
example
sudo chown -R USER:USER lang && sudo chmod -R u+w lang. |
Frequently Asked Questions
How does this handle translations in subdirectories like lang/web/en.json?
- Question
- Those folders are packs. After you pick a module (or the main app) that has
lang/,lang/app3/, andlang/web/, the CLI asks which packs to process, then lists JSON/PHP from those packs only. The manager Pack filter does the same. PHP under a pack writes tolang/web/{locale}/messages.php, notlang/{locale}/web/messages.php.
Can I use this with existing translation files?
- Question
- Yes. The tool reads existing language files to build a complete key map. With
--skip-existing, it only fills missing entries, so your existing translations remain intact.
What are the different operation modes available?
- Question
- Four modes:
- Normal: translates all keys (new and existing).
- Skip Existing: translates only missing keys (
--skip-existing). - Refresh: re-translates existing keys from current file
wording (
--refresh). - Refresh-clean: re-translates existing keys from the key
only (
--refresh-clean).
--refresh,--refresh-clean, and--skip-existingare mutually exclusive.
How do I pick up package UI changes after I already published the views?
- Question
- Laravel keeps your copies in
resources/views/vendor/gemini-translator. Overwrite them withphp artisan vendor:publish --tag=gemini-translator-manager --forceandphp artisan view:clear, or delete that folder so the package views load. There is no unpublish command. PHP is never published;composer updatealways uses the new package code.
How do I open the Translation Manager?
- Question
- Visit
/translations-manager. You do not register routes yourself. If the app has a login route, sign in first. Publish the Blade only if you want to customize it:php artisan vendor:publish --tag=gemini-translator-manager.
Does Windows run translations in parallel?
- Question
- Yes. Windows does not use Spatie fork (
pcntl).--driver=forkor--driver=processstarts Symfony Process workers so chunks run in parallel, similar to Unix fork. Menus usekernel32when FFI is available.
How accurate are the AI translations?
- Question
- Gemini translations are generally high quality. The package sends source text,
per-locale script rules, and a writing-system check so most output matches the
language you asked for. Gemini can still mix writing systems, slip in another
language, or return wording that is not literal or not what you expected (for
example Gujarati mixed with Hindi, Kannada, or English). The check reduces those
cases; it does not catch every mistake. Human review is still recommended for
production-critical text. Turn on
Highlight script faults in the manager, re-run
--refreshor--refresh-cleanfor leftover faults, and use--contextfor domain-specific terminology.
What is dry-run mode?
- Question
--dry-runsimulates the full process and previews changes without writing files.
What happens if AI translation fails for a chunk?
- Question
- The tool applies intelligent fallback logic by using existing English source text,
or the key itself if no source text exists. Persistently failing keys are logged to
failed_translation_keys.json.
Contributing & Support
This project is open-source and maintained by Jayesh Mepani. Contributions are welcome!
How to Contribute
- Report bugs and request features on the GitHub Issues page.
- Submit pull requests to the repository for improvements.
- Star the repository on GitHub if you find it useful!
Support
If you encounter issues, please check the troubleshooting section first. If the problem persists, create a new issue on the GitHub repository with detailed information, including your OS, PHP version, and the exact command used.