Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Translation Laravel Package

illuminate/translation

Laravel's translation library offers a clean, fluent API for retrieving localized strings, handling pluralization and parameter replacement across PHP language files and JSON. It supports locale fallbacks, runtime locale switching, and works standalone or within the full framework.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a subtree split of laravel/framework — you don't install it directly. It's already present in any Laravel application. If you see it in composer.json of a non-Laravel project, treat that as a mistake unless you're deliberately wiring up the container and config yourself.

Where to look first:

  1. config/app.php — confirm locale and fallback_locale are set correctly.
  2. resources/lang/{locale}/ (or lang/{locale}/ on Laravel 11+) — this is where translation files live.
  3. vendor/laravel/framework/src/Illuminate/Translation/ — read Translator.php and FileLoader.php if you need to understand resolution order.

Your first use case is almost always UI strings:

// lang/en/messages.php
return [
    'welcome' => 'Welcome, :name',
    'items'   => '{0} No items|[1,*] :count items',
];
echo __('messages.welcome', ['name' => 'Alex']); // Welcome, Alex
echo trans_choice('messages.items', 5, ['count' => 5]); // 5 items

Then wire up a switcher: app()->setLocale($request->user()->locale) in middleware, or App::setLocale('fr') early in a service provider if the choice is fixed per environment.

Implementation Patterns

Group by feature, not by page. lang/en/auth.php, lang/en/billing.php, lang/en/admin/users.php. Nested paths (admin/users) map to subdirectories and stay readable at scale. Avoid one giant messages.php.

Use the __() helper in views and controllers; Lang::get() for bulk. They're the same underlying call, but the helper is shorter and works in Blade without imports. Reserve the facade for cases where you need Lang::has(), Lang::choice(), or Lang::get('messages') (the whole array).

Dynamic locale in middleware. Typical pattern for multi-tenant or user-preference apps:

public function handle($request, Closure $next)
{
    $locale = $request->user()?->locale
        ?? $request->getPreferredLanguage(array_keys(config('app.locales')))
        ?? config('app.locale');

    app()->setLocale($locale);
    return $next($request);
}

Vendor overrides. Packages like Spatie, Filament, and Laravel Breeze ship translations under lang/vendor/{package}/{locale}/{file}.php. Copy the file from vendor/{package}/resources/lang/, override only the keys you need, and Laravel's vendor loader resolves yours first.

JSON translations for user-facing copy. If you're translating application text (not grouped keys), lang/fr.json with {"Save": "Enregistrer"} lets you pass English strings directly: __('Save'). Good for rapid onboarding of a translation vendor; less good for large apps because it's a flat namespace.

Pluralization with explicit ranges. Laravel's trans_choice uses |-separated forms with optional ranges:

'files' => '{0} No files|{1} One file|[2,*] :count files',

Always include the :count placeholder or the message won't interpolate the number.

Fallback layering. config('app.fallback_locale') catches missing keys. For deeper fallbacks (e.g., pt-BR → pt → en), you either add a custom loader or use Lang::setFallback() at runtime before rendering.

Testing. Use $this->get('/')->assertSee(__('messages.welcome', ['name' => 'Alex'])) — but set the locale explicitly in the test (app()->setLocale('en')) to avoid CI surprises from session or config drift.

Custom loaders for database- or API-backed translations: implement Illuminate\Translation\LoaderInterface, register it via Lang::extend() or by rebinding the translator singleton in a service provider. This is the escape hatch for CMS-driven content; leave it alone unless you actually need it.

Gotchas and Tips

Don't composer require illuminate/translation. It's a read-only split. Requiring it explicitly can conflict with laravel/framework and produce class-resolution surprises. If you genuinely need it standalone, you also need illuminate/support, illuminate/config, and to bind the container manually — at which point you've rebuilt half a framework.

Missing keys look like messages.welcome in output when APP_DEBUG=false, and ???messages.welcome??? in debug. That's the tell. Grep output in staging with debug on to bulk-find gaps before launch.

Never mix .php and .json files with the same key root. lang/en/auth.php and lang/en/auth.json don't merge predictably; the loader picks one path and ignores the other depending on call shape (__('auth.failed') vs __('Failed')).

Locale casing is not normalized. en-US ≠ en-us ≠ en_US. Accept whatever the client sends (headers, user prefs), then normalize:

app()->setLocale(Str::lower(str_replace('_', '-', $locale)));

trans_choice requires :count. If you write '{1} One|[2,*] Many' without :count, the number won't appear even though selection works. Also, [0] is not {0} — use braces for exact matches, brackets for ranges.

Performance: pre-load groups, not keys. Calling __('messages.a') inside a foreach over 10,000 rows re-resolves on each call. Fetch Lang::get('messages') once into an array and index into it, or accept that the loader memoizes per-request (it does, mostly — but the resolution cost is not zero).

Caching in production. Laravel caches compiled views and config, not translation files. If you're on Laravel 11+ and using php artisan lang:publish or third-party export commands, check what actually runs in your deploy script — there's no built-in translation:export in all versions, so verify before adding it to CI.

__() in config files is a footgun. Config is cached before the translator is reliably available in some boot orders, and config:cache will freeze whatever the locale was at cache time. Keep translations out of config/.

Extending Translator is fine, but bind against the translator alias in the container, not the class name — Laravel resolves by alias in many internal callers, and rebinding only the concrete class silently leaves the facade pointing at the original.

Debugging a specific key: Lang::has('messages.welcome', 'fr') tells you whether it's a missing-file problem or a missing-key problem. Lang::get('messages.welcome', [], 'fr') bypasses the current locale without changing global state — useful in tinker.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules
loco/loco
starfederation/datastar-php
yupmin/modern-phystrix
stephenjude/extended-artisan-commands
lumphp/laravel-macroable
lumphp/laravel-collections
lumphp/laravel-contracts
sermepa/sermepa
arkecosystem/fortify