Laravel Magazine
Localizing a Laravel Application: Translations and Locale Middleware

Localizing a Laravel Application: Translations and Locale Middleware

Eric Van Johnson ·

Adding a second language to an app is far easier if the groundwork is there from the start, but Laravel makes retrofitting it manageable too. In this tutorial we will localize an app for PHP Architect, moving hardcoded strings into translation files, handling pluralization, and wiring up a middleware that picks the right locale for each request.

Step 1: Translation Files

Laravel supports two styles of translation files. The classic style uses PHP arrays under lang/{locale}, keyed by a short identifier. Create lang/en/messages.php and lang/es/messages.php:

// lang/en/messages.php
return [
    'welcome' => 'Welcome to PHP Architect',
    'dashboard' => 'Dashboard',
];
// lang/es/messages.php
return [
    'welcome' => 'Bienvenido a PHP Architect',
    'dashboard' => 'Panel',
];

Retrieve them with the __() helper, using dot notation of file.key:

<h1>{{ __('messages.welcome') }}</h1>

Step 2: JSON Translation Files

For string-heavy interfaces, the JSON style is often nicer because the key is the default-language string itself. Create lang/es.json:

{
    "Save changes": "Guardar cambios",
    "Your profile has been updated.": "Tu perfil ha sido actualizado."
}

Then use the natural English string as the key:

<button>{{ __('Save changes') }}</button>

If a key is missing from the target language, Laravel returns the key itself, so the English text shows through as a sensible fallback.

Step 3: Placeholders and Pluralization

Placeholders are prefixed with a colon and replaced by passing an array:

echo __('messages.greeting', ['name' => $user->name]);
// with 'greeting' => 'Hello, :name'

Pluralization uses trans_choice() with pipe-separated forms and ranges:

// 'apples' => '{0} No apples|[1,19] Some apples|[20,*] Many apples'
echo trans_choice('messages.apples', $count);

Step 4: A Locale Middleware

To honor a per-user language preference, set the locale early in the request with a middleware. Generate one and set the active locale with App::setLocale():

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\App;

class SetLocale
{
    public function handle($request, Closure $next)
    {
        $locale = $request->user()?->locale
            ?? $request->getPreferredLanguage(['en', 'es'])
            ?? config('app.locale');

        App::setLocale($locale);

        return $next($request);
    }
}

This checks the authenticated user's stored preference first, then falls back to the browser's Accept-Language header via getPreferredLanguage(), and finally to the app default. Register it in the appropriate middleware group so it runs on web requests.

Step 5: Setting the Fallback Locale

Make sure config/app.php defines both the default and the fallback locale. The fallback is used whenever a translation is missing in the active locale:

'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),

That is the full loop: strings live in translation files, the middleware chooses a locale per request, and the fallback catches anything you have not translated yet. Adding a third language is now just another set of files.

Further Reading

Stay Updated

Subscribe to our newsletter

Get latest news, tutorials, community articles and podcast episodes delivered to your inbox.

Weekly articles
We send a new issue of the newsletter every week on Friday.
No spam
We'll never share your email address and you can opt out at any time.