Laravel's localization system is elegant. You drop language files in lang/, use __('messages.welcome') in your views, and for a while, life is good.
Then the second language arrives. Then the third. Then you're at eight languages, 2,000 strings, three translators asking you to export spreadsheets, and a staging deploy that broke because someone deleted a key from lang/fr/validation.php and nobody noticed until a French user hit a 500 error.
We've seen this arc on enough client projects to know it's not a skill issue. It's a tooling gap. Laravel gives you great primitives for localization. It doesn't give you a workflow.
Where Laravel's built-in system works fine
Let's be clear about when you don't need anything else.
If your app has 2-3 languages, fewer than 200 strings, and the developers themselves handle the translations (or copy them from Google Translate, we're not judging), the lang/ directory works. Edit the files, commit them, deploy. Done.
This covers a surprising number of applications. If this is your situation, stop reading. You're fine.
Where it falls apart
The problems start when any of these become true:
Non-developers need to translate. Your translator doesn't have a GitHub account. They definitely don't want to learn the difference between a PHP array file and a JSON file. Emailing spreadsheets back and forth is what happens next, and it's exactly as error-prone as you'd expect.
You have a review step. Machine translation is good enough for a draft, but someone needs to review it before it goes live. Laravel's language files have no concept of "draft," "reviewed," or "approved." A string is either in the file or it isn't.
Strings change after translation. You update the English source string. The French, German, and Japanese translations are now stale, but nothing tells you that. The old translation keeps serving. It might be subtly wrong, or completely wrong, and nobody catches it until a user reports it.
Multiple people work on translations. Two translators open the same language file. Both edit it. One commits. The other commits on top. Git merge conflict in a PHP array. Congratulations, your French translation file now has a syntax error.
You need translation memory. You translated "Save changes" in your settings page. Now you need it on the profile page too. Without a system that tracks previously translated strings, your translator does it again. Maybe they translate it slightly differently this time. Now your UI is inconsistent.
The options
You have roughly three paths forward.
Database packages
Packages like barryvdh/laravel-translation-manager move translations into the database and give you a web UI to edit them. Others like spatie/laravel-translatable make Eloquent models translatable by storing translations as JSON columns. Both solve parts of the problem.
But they create new ones. Your translations now live in the database, not your codebase. Deployments need database seeds or imports. You can't grep your translations. You can't review them in a pull request. And these packages don't do workflows, translation memory, or machine translation. They're database-backed storage with a CRUD interface.
Enterprise TMS platforms
Crowdin, Lokalise, Phrase. Full-featured translation management systems with review workflows, machine translation, and API access.
The pricing models vary. Crowdin charges by hosted words (not per seat), starting at $50/month. Lokalise bills by processed words with plans from $144/month. Phrase charges per user. Each has a different model, but the common thread is that costs scale quickly once you're past the free or starter tier. The paid tiers that include review workflows, branching, and API access typically land in the $150-500/month range for a team with 10+ languages.
They're also designed for content-heavy organizations (marketing teams, documentation teams, product teams managing thousands of pages). If you're a development team with code-driven strings, half the features are irrelevant and the onboarding overhead isn't worth it.
A developer-first TMS
This is the category we think is underserved. A platform that fits into the developer workflow (push from code, translate, review, pull back) without the seat-based pricing that makes the big platforms painful.
We built Stringhive for exactly this.
How we handle it
Stringhive is a translation management platform built for developer teams. It has a native Laravel SDK, so the workflow stays inside your codebase and your terminal.
Here's what the day-to-day looks like.
Push your source strings
php artisan stringhive:push
This scans your lang/ directory and pushes all source strings to Stringhive. New strings get created, changed strings get flagged (so translators know the source changed and the translation might be stale), deleted strings get marked for cleanup. You can use --dry-run to preview what would change before committing.
The command understands Laravel's localization formats: PHP arrays, JSON files, nested keys, pluralization rules. You don't need to export to a specific format or flatten your key structure. It reads what Laravel already has.
Translate and review
Translators work in Stringhive's editor. Each string has context (which file it came from, what the key path is), translation memory suggestions from previously approved translations, and optional machine translation drafts from DeepL, Google, Azure, OpenAI, ModernMT, or Mistral (bring your own API key, no markup from us).
Every string goes through states: untranslated, translated, reviewed, approved. You decide how strict the workflow is. For some projects, "translated" is good enough. For regulated industries or public-facing content, you want the full review chain.
If a source string changes after a translation was approved, the translation gets flagged as stale. The translator sees exactly what changed and can update it. No more silently serving outdated translations.
Pull approved translations back
php artisan stringhive:pull
This writes approved translations back to your lang/ directory in the exact format Laravel expects. PHP array files come back as PHP arrays. JSON files come back as JSON. Your codebase stays the source of truth for file structure and key organization.
Commit the pulled translations, push to your repo, deploy. The translations are in your codebase, version-controlled, reviewable in PRs, and greppable.
Automate it
Once the workflow is established, you can automate the push/pull cycle in CI. Push source strings on every merge to main:
# .github/workflows/translations.yml
name: Sync translations
on:
push:
branches: [main]
paths: ['lang/**']
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- run: composer install --no-interaction
- run: php artisan stringhive:push
env:
STRINGHIVE_TOKEN: ${{ secrets.STRINGHIVE_TOKEN }}
STRINGHIVE_HIVE: ${{ vars.STRINGHIVE_HIVE }}
Pull approved translations on a schedule or via webhook when translations are approved. The exact setup depends on your deployment pipeline, but the pieces are standard: a cron trigger or a webhook, stringhive:pull, commit, deploy.
What about the cost?
Stringhive prices per string, not per seat or per word. Translators are unlimited on every plan. Team seats (admins, developers) are capped per tier, but you're not paying per head the way enterprise TMS platforms charge. The cost scales with your content, not your team size.
The free tier gives you 1,500 strings, which covers a lot of small-to-medium applications. Paid plans start at $16/month (annual) for 10,000 strings. For a typical Laravel app with 500-2,000 strings across several languages, the free tier or the lowest paid tier covers it. No usage-based surprises.
One more thing worth mentioning: Stringhive runs entirely on EU infrastructure. No US-based cloud providers, no data crossing borders. If your team or your clients care about GDPR compliance and actual EU data residency (not just an "EU region" checkbox on a US platform), this matters.
When you don't need this
If your app has fewer than 200 strings and 2-3 languages, stick with language files. The overhead of any external tool isn't worth it at that scale.
If you're a solo developer doing your own translations, a TMS adds process where you don't need it. Edit the files, commit, ship.
If your translations are user-generated content (like a CMS where users create content in multiple languages), you need a different solution entirely. Stringhive is for application strings (UI labels, messages, validation, emails), not for free-form content.
When you absolutely need this
More than 500 strings. More than 3 languages. At least one person doing translations who isn't a developer. Any requirement for review or approval before translations go live. Any history of broken deploys caused by bad translation files.
If two or more of those are true, you're past the point where language files alone can keep up.
Try it
composer require stringhive/laravel
php artisan stringhive:push --dry-run
Stringhive has a free tier that covers most early-stage projects. The Laravel SDK is open source. There's also stringhive:audit to catch missing or orphaned keys before they become broken deploys. Set it up in 10 minutes, push your strings, and see what a real translation workflow feels like.
Need help with your localization setup?
We've built multilingual applications for clients across 10+ countries. Setting up the right localization architecture early saves enormous pain later. If you want help with your Laravel i18n setup, translation workflow, or multilingual infrastructure, get in touch. First 30 minutes, free.