πŸ“‹ Overview

The Laravel Gemini AI Translation Synchronization Tool is a powerful Artisan command that revolutionizes the localization workflow for modern Laravel applications, including those built with nwidart/laravel-modules. It goes beyond simple key extraction by discovering keys from all sourcesβ€”your code, existing language files, and the frameworkβ€”and then intelligently synchronizes them across all target languages using Gemini AI.

Full Module Support

Scans are correctly isolated between the main app and modules. A new feature also lets you choose to keep module translations separate or consolidate them into the main `lang` directory.

AI-First Translation

Trusts the AI to interpret both namespaced keys (e.g., messages.welcome) and literal strings to generate accurate, context-aware translations.

True Synchronization

Discovers keys from your code and existing files to create a complete picture, ensuring no translation is left behind. Use --skip-existing to only add missing keys.

High Performance

Features a fork driver for lightning-fast parallel processing on Linux/macOS/WSL, and a stable sync driver for Windows.

πŸš€ Installation

Step 1: Install via Composer

composer require jayesh/laravel-gemini-translator

Step 2: Publish Configuration

php artisan vendor:publish --provider="Gemini\Laravel\GeminiServiceProvider"

This creates the config/gemini.php file.

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"
Offline Mode: If you run the command without a 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

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

Free Tier Limits: The free tier has limits on requests per minute/day. For large projects, consider using smaller chunk sizes or processing in batches to stay within these limits.

Configuration File

The config/gemini.php file allows you to customize:

  • AI model selection (e.g., Gemini 2.5 Flash-Lite)
  • Request timeout settings
  • Default language preferences
  • Retry logic parameters

🎯 Usage

Basic Command

Simply run the command to start the interactive process. It will guide you through selecting which parts of your application and which files to process.

php artisan translations:extract-and-generate

Platform-Specific Recommendations

For the best experience, specify a driver based on your operating system.

# Linux/macOS/WSL (fastest performance)
php artisan translations:extract-and-generate --driver=fork

# Windows (maximum stability)
php artisan translations:extract-and-generate --driver=sync

Incremental Updates (Synchronization)

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

🧩 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. Consolidating Module Translations (New Feature)

If you select any modules, you'll be asked a new question. This gives you full control over your project's structure.

 β”Œ 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 high-performance `fork` driver shows a real-time progress bar for all keys being processed concurrently.

βœ… Key discovery complete! Found 451 unique keys from all sources combined.

Press the 'q' key at any time to gracefully stop the process.
πŸ“Š 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)

Sync Driver Output (Windows)

The stable `sync` driver processes chunks sequentially and provides feedback for each one, which is ideal for environments that don't support forking.

βœ… 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

 β”Œ 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 <span v-t="'buttons.submit'"> Vue.js & Alpine.js Directives

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 sent to Gemini is specially designed to handle two types 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."

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).
--driver Concurrency driver: fork (fast, for Linux/macOS) or sync (stable, for Windows).
--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.
--source (Deprecated) The root directory to scan. The interactive prompt is now preferred. Default: ".".
--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.
# When prompted, choose NOT to consolidate.
# This will find and update missing keys only within Modules/Settings/lang/
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."

πŸ”§ 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.
"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()" You are trying to use --driver=fork on an environment without the pcntl PHP extension (like standard Windows). Use the stable driver: --driver=sync.
Script hangs or runs very slowly Processing a very large number of keys with the sync driver. Be patient, as sync mode is sequential. For large projects, use the fork driver on Linux/macOS/WSL if possible.
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.

❓ Frequently Asked Questions

Q: How does this handle translations in subdirectories like lang/web/en.json?

A: Perfectly. The tool recursively scans your `lang` directories (in both the main app and modules). It will detect `web/en.json` and present it in the selection prompt as a distinct file group, for example: `Main Application: JSON File (web/*.json)`. When it writes the files, it will place the translated versions in the correct subdirectories (`lang/web/ru.json`, etc.), preserving your structure.

Q: Can I use this with existing translation files?

A: Yes, and that's its biggest strength! The tool is designed to work with existing projects. It reads all your current language files to build a complete list of every key your application knows about. When you run it with --skip-existing, it will only translate what's missing, perfectly synchronizing your language files while preserving all your existing work.

Q: How accurate are the AI translations?

A: Gemini AI provides high-quality translations, but we always recommend a final review before deploying to production, especially for critical user-facing content or nuanced language. Using the --context flag dramatically improves accuracy for domain-specific projects.

Q: What happens if the AI translation fails for a chunk?

A: The command has built-in retry logic with exponential backoff. If a chunk of keys consistently fails after all retries, it is logged in failed_translation_keys.json in your project root, so you can review the problematic keys and handle them manually or retry with a smaller chunk size.

πŸ“‹ Contributing & Support

This project is open-source and maintained by Jayesh Mepani. Contributions are welcome!

How to Contribute

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.

Security: If you discover a security vulnerability, please email the maintainer directly rather than creating a public issue.