· 6 min read

Migrate Your Existing Laravel Translations in Minutes

You've spent months building your translation files. Hundreds of keys across multiple languages. You're not starting over.

Good news: you don't have to.

LangSyncer imports standard Laravel PHP lang files and JSON files directly, keeps your keys and groups intact, and exports back out whenever you want. Your __() calls don't change at all. Here's what a real migration looks like, start to finish.

The Problem in a Real Laravel App

A mature Laravel app's lang/ directory looks something like this:

lang/
├── en/
│   ├── auth.php
│   ├── messages.php
│   └── validation.php
├── es/
│   ├── auth.php
│   ├── messages.php
│   └── validation.php
└── fr/
    └── messages.php      <- where did auth.php go?

The files themselves are fine. This is exactly the format Laravel wants:

// lang/en/messages.php
return [
    'welcome' => 'Welcome to our application',
    'greeting' => 'Hello, :name!',
    'nested' => [
        'deep' => [
            'value' => 'Deeply nested value',
        ],
    ],
];

The problems are everything around them. French is missing an entire file and nobody noticed. es/messages.php has 40 fewer keys than its English counterpart. Every copy tweak needs a commit, a review, and a deploy. And answering "which keys are still untranslated in Spanish?" means writing a throwaway script. If that sounds familiar, we wrote a full comparison of LangSyncer vs plain Laravel lang files.

The Migration, Step by Step

  1. Create a project and grab your API key. Follow the getting started guide: create a project, open its Actions modal, and copy the API key from the API Key & CDN tab. Make sure every language your app supports is assigned to the project, because you can only import into languages the project is configured for.

  2. Import your English files. In the project's Actions modal, find the Import section. Drag and drop en/messages.php, en/auth.php, and the rest (or click to browse), select English as the language, and click Import Translations. Files are validated for syntax errors before anything is written, new keys are created, existing keys are updated, and an import log records what happened.

    Nested arrays flatten to dot notation automatically:

    | Group | Key | Value | |-------|-----|-------| | messages | welcome | Welcome to our application | | messages | greeting | Hello, :name! | | messages | nested.deep.value | Deeply nested value |

  3. Repeat for each language. Import the es/ files selecting Spanish, then fr/ selecting French. LangSyncer matches keys automatically, so after three imports you have a single view across languages:

    | Key | EN | ES | FR | |-----|----|----|-----| | messages.welcome | Welcome | Bienvenido | Bienvenue |

  4. JSON files work too. If you use __('Sign In') style strings backed by en.json, import those the same way. JSON is converted to PHP format internally and lands under the _json group:

    {
        "Welcome": "Welcome",
        "Sign In": "Sign In"
    }
    

    | Group | Key | Value | |-------|-----|-------| | _json | Welcome | Welcome | | _json | Sign In | Sign In |

  5. Review and fill the gaps. This is where migrating pays off immediately. Filter by status to see what's pending, and those silently missing French keys become visible instead of invisible. Then use Translate Missing to AI-translate every empty value in bulk: it shows you how many translations will be processed and the estimated quota cost before you click Start Translation. Everything saves as drafts, so nothing goes live until you review and hit Publish.

  6. Connect your app. Install the client package and point it at your project:

    composer require headwires/translator-client
    php artisan vendor:publish --tag=translator-client-config
    
    CLI_TRANSLATOR_API_KEY=your-api-key-here
    CLI_TRANSLATOR_CDN_URL=https://cdn.langsyncer.com
    CLI_TRANSLATOR_SYNC_STRATEGY=merge
    

    Then pre-cache everything with php artisan translator:warmup. Note the sync strategy: merge combines CDN translations with your local files and preserves local-only keys, with CDN values taking precedence. That's the safe setting mid-migration, when a key might exist locally but not in LangSyncer yet. Once you're confident everything is imported, switch to overwrite and let LangSyncer be the single source of truth.

From legacy files to live, managed translations in an afternoon.

After the Import: Clean House

An import is also the best moment to fix years of accumulated drift, because for the first time you can see everything side by side. Two cleanups worth doing while it's all fresh:

  • Deduplicate common UI strings. If "Save", "Cancel", and "Delete" show up in three different groups, select them and use Bulk Actions > Promote to Global to move them into your account's global translations library. They become available to every project, and one edit updates them everywhere.
  • Consolidate groups. Spot translations that landed in the wrong group? Select them and use Bulk Actions > Change Group to move them. Both operations create drafts, so nothing changes in production until you publish.

The Reverse: Export

Worried about lock-in? Don't be. From the same Actions modal you can export at any time:

  • PHP format: Standard Laravel resources/lang/{locale}/*.php structure
  • JSON format: A single {locale}.json file per language
  • Both: Generates both formats in one go

Download links appear in the Recent Exports section and expire after 7 days, so grab them when you generate them. Your data is never locked in.

When Not to Migrate

Honest limits, because migration isn't free:

  • Single locale, stable copy, solo developer. If your app ships in one language and strings change once a quarter, plain lang files are simpler and add zero dependencies. Migrate when translations become a workflow, not a file.
  • Import updates existing keys. Re-importing a file after you've refined values inside LangSyncer will update those keys with the file's values. Decide which side is the source of truth before importing the same files twice.
  • Git-centered review culture. If your team requires every string change to go through a pull request, a managed platform changes that process. Get buy-in first; the export options above are your escape hatch either way.

Gotchas

  • Import failing? Check the file format (.php or .json), fix any syntax errors in the file, confirm the target locale is assigned to the project, and keep files under 10MB. More in troubleshooting.
  • JSON keys land in the _json group. If you filter, export, or organize by group, plan for that.
  • Seeing stale strings after connecting your app? Run php artisan translator:sync, and clear Laravel's cache with php artisan cache:clear.

Related Posts


Start importing →

We use cookies to improve your experience and analyze site traffic. Cookie Policy

Cookie Preferences

Essential

Required for the site to work

Analytics

Help us improve the site

Marketing

Personalized ads and content