Lokalise graphic of Laravel localization

Laravel localization: A step-by-step guide with examples

In this tutorial, you will learn using examples how to perform Laravel localization and present your application in multiple languages. We’ll talk about working with translation files, performing pluralization, creating a language switcher, and more with Laravel localization and multi-language examples.

Localization is the second phase of the Laravel internationalization (i18n) process. In Laravel i18n, an application is designed to fit various languages and cultures. Localization is adapting said internationalized applications to a specific language through translation.

So, shall we begin?

Huge thanks go to my colleague Andrejs Bekešs, who prepared the code samples and technical explanations for this article.

The source code is available on GitHub.

Check out how our translation management system can help you translate your Laravel apps faster.

    Prerequisites and assumptions

    • We will be using Laravel version 10.x in this tutorial.
    • You have basic PHP knowledge.
    • Your domain is localhost. If not, then replace localhost with your domain name or IP address (depending on your installation).

    To get started, let’s also create a new Laravel application by running:

    composer create-project laravel/laravel i18n-demo

    Simple translations

    Now, let’s head over to the resources/views/welcome.blade.php file and replace the contents of the body tag with our own, like so:

    <body class="antialiased">
        <div class="relative flex items-top justify-center min-h-screen bg-gray-100 sm:items-center py-4 sm:pt-0">
            <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
                <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
                    Welcome to our website!
                </div>
            </div>
        </div>
    </body>

    As you can see, here we have a hard-coded text which is not optimal for internationalization.

    Therefore, let’s begin by preparing our welcome message for localization, which is really easy in Laravel. All you need to do is replace the text with the following code:

    {{ __('Welcome to our website!') }}

    This will instruct Laravel to display “Welcome to our website!” by default and look for translations of this string if a non-English language is set (we’ll get to this later on).

    English will be set as our app’s default language.

    Locales in Laravel

    But how does Laravel know what the current language is or what languages are available in the application? Actually, it looks up the locale setup in the config/app.php app.

    Open this file and look for these two keys:

    /*
    |--------------------------------------------------------------------------
    | Application Locale Configuration
    |--------------------------------------------------------------------------
    |
    | The application locale determines the default locale that will be used
    | by the translation service provider. You are free to set this value
    | to any of the locales which will be supported by the application.
    |
    */
    
    'locale' => 'en',
    
    /*
    |--------------------------------------------------------------------------
    | Application Fallback Locale
    |--------------------------------------------------------------------------
    |
    | The fallback locale determines the locale to use when the current one
    | is not available. You may change the value to correspond to any of
    | the language folders that are provided through your application.
    |
    */
    
    'fallback_locale' => 'en',

    The descriptions shown above the keys should be self-explanatory, but in short, the locale key contains the default locale of your application (at least, if no other locale was set in the code).

    The fallback_locale is the one being activated if a non-existent locale has been requested in our application.

    Let’s add a new key into this file to provide the list of all the supported locales:

    /*
    |--------------------------------------------------------------------------
    | Available locales
    |--------------------------------------------------------------------------
    |
    | List all locales that your application works with
    |
    */
    
    'available_locales' => [
      'English' => 'en',
      'Russian' => 'ru',
      'French' => 'fr',
    ],

    Great! Now our application supports three languages: English, Russian, and French. Of course you can pick any other languages.

    Translation files

    General information

    So, in Laravel, just like in many other frameworks, you will store translations for different languages in separate files. There are two ways to organize Laravel translation files:

    • An old approach, which involves storing your files under the following path: resources/lang/{en,fr,ru}/{myfile.php}.
    • A new approach of having resources/lang/{fr.json, ru.json} files.

    In this article, we will focus on the second approach, but the same applies to the first one (with the exception of how translation keys are named and retrieved).

    For languages that differ by territory, you should name the language directories/files according to ISO 15897. For example, for British English you would use en_GB rather than en-gb.

    Creating translation files

    Let’s start by adding new localization files in the resources/lang folder. First, create a resources/lang/ru.json file and add the corresponding translations, as follows:

    Now that we have configured all the locales to work with, let’s go ahead and move on to translating our default welcome message.

    {
      "Welcome to our website!": "Добро пожаловать на наш сайт!"
    }

    Next, create a resources/lang/fr.json file:

    {
      "Welcome to our website!": "Bienvenue sur notre site !"
    }

    As you can see, we’re always referencing the default message that we added in the welcome.blade.php file (which was {{ __('Welcome to our website') }}). We don’t have to create an en.json file because Laravel already knows that messages we pass in by default to the __() function are for our default locale, which is en (as explained in the section above).

    Switching locales in Laravel

    At this point, Laravel does not know how to switch locales, so let’s perform translations directly inside the route for now. Edit the default welcome route within the routes/web.php file as follows:

    Route::get('/{locale?}', function ($locale = null) {
        if (isset($locale) && in_array($locale, config('app.available_locales'))) {
            app()->setLocale($locale);
        }
        
        return view('welcome');
    });

    Here we are trying to read an optional locale GET parameter and set the current locale accordingly (if the requested locale is supported, that is).

    We can now visit our website, specifying any of the available languages as the first route segment; for example, for localhost/ru or localhost/fr, you should see the localized content. If you specify a non-supported locale or don’t specify a locale at all, Laravel will use en by default.

    Middleware

    Passing the locale for every site link might not be what you want and could look not so clean esthetically. That’s why we’ll perform language setup via a special language switcher and use the user session to display the translated content. Therefore, create new middleware in the app/Http/Middleware/Localization.php file or by running artisan make:middleware Localization.

    Paste the following code inside:

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\App;
    use Illuminate\Support\Facades\Session;
    
    class Localization
    {
        /**
        * Handle an incoming request.
        *
        * @param  \Illuminate\Http\Request  $request
        * @param  \Closure  $next
        * @return mixed
        */
        public function handle(Request $request, Closure $next)
        {
            if (Session::has('locale')) {
                App::setLocale(Session::get('locale'));
            }
            return $next($request);
        }
    }

    This middleware will instruct Laravel to utilize the locale selected by the user if this selection is present in the session.

    Since we need this operation to be run on every request, we also need to add it to the default middleware stack at app/http/Kernel.php for the web middleware group:

    /**
    * The application's route middleware groups.
    *
    * @var array<string, array<int, class-string|string>>
    */
    protected $middlewareGroups = [
      'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \App\Http\Middleware\Localization::class, // <--- add this
    ],

    Changing routes

    Next, we have to add a route to change the locale within the routes/web.php file. We’re using a closure route, but you can use exactly the same code inside your controller if you wish:

    Route::get('language/{locale}', function ($locale) {
        app()->setLocale($locale);
        session()->put('locale', $locale);
    
        return redirect()->back();
    });

    Also, don’t forget to remove the locale switching previously added in our default welcome route. Make your root route look like this:

    Route::get('/', function () {
        return view('welcome');
    });

    With this done, the only way for the user to switch the currently set language is by entering localhost/language/{locale}. The locale selection will be stored inside the session and redirect users back to where they came from (check the Localization middleware). To test it out, head over to localhost/language/ru (as long as your session cookie is present in your browser), and you will see the translated content. You can freely move around the website or try to refresh the page and see that the selected language is being preserved.

    The actual switcher

    Now we need to create something that the user can click on to change the language instead of entering locale codes into the URL manually. To do this, we’ll add a very simple language control. Therefore, create a new resources/views/partials/language_switcher.blade.php file with the following code:

    <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
        @foreach($available_locales as $locale_name => $available_locale)
            @if($available_locale === $current_locale)
                <span class="ml-2 mr-2 text-gray-700">{{ $locale_name }}</span>
            @else
                <a class="ml-1 underline ml-2 mr-2" href="language/{{ $available_locale }}">
                    <span>{{ $locale_name }}</span>
                </a>
            @endif
        @endforeach
    </div>

    Include the newly created switcher in the “welcome” view:

    <body class="antialiased">
        <div class="relative flex items-top justify-center min-h-screen bg-gray-100 sm:items-center py-4 sm:pt-0">
            <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
                @include('partials/language_switcher')
    
                <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
                    {{ __('Welcome to our website!') }}
                </div>
            </div>
        </div>
    </body>

    Open the app/Providers/AppServiceProvider.php file and add the code to be shared when our language switcher is composed. Specifically, we’ll share the current locale that can be accessed as {{ $current_locale }}.

    * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        view()->composer('partials.language_switcher', function ($view) {
            $view->with('current_locale', app()->getLocale());
            $view->with('available_locales', config('app.available_locales'));
        });
    }

    Advanced translation options in PHP Laravel

    We will mostly be working with resources/views/welcome.blade.php, so everything needs to happen in our welcome view unless specified otherwise.

    Parameters in translation strings

    For instance, let’s greet our imaginary user (Amanda) instead of simply displaying a generic message:

    {{ __('Welcome to our website, :Name', ['name' => 'amanda']) }}

    Please note that we have used the name with a lowercase first letter, but the placeholder with an uppercase first letter. In this way, Laravel can help you capitalize the actual word automatically. This will occur if the placeholder starts with a capital letter, i.e., :Name produces “Amanda” or a full uppercase word,  :NAME, produces “AMANDA”.

    Additionally, let’s update our resources/lang/fr.json and resources/lang/ru.json translation files, since right now we’ll only see the English version everywhere as the translation keys do not match the translations.

    French:

    {
      "Welcome to our website, :Name": "Bienvenue sur notre site, :Name"
    }

    Russian:

    {
      "Welcome to our website, :Name": "Добро пожаловать на наш сайт, :Name"
    }

    Great job!

    Pluralization

    To see pluralization in action, let’s add a new paragraph of text. To perform pluralization, you have to use the trans_choice function instead of __(), for instance:

    {{ __('Welcome to our website, :Name', ['name' => 'amanda']) }}
    <br>
    {{ trans_choice('There is one apple|There are many apples', 2) }}

    As you can see, the plural forms are separated with a |.

    Now, what if we need more plural forms? This is possible as well:

    {{ trans_choice('{0} There :form no apples|{1} There :form just :count apple|[2,19] There :form :count apples', 24) }}

    In this case, we allow the numbers 0, 1, from 2 to 19, and finally from 20 onwards. Of course, you can add as many rules as you need.

    Next, what if we want placeholders in our plural forms? That’s not a problem either:

    {{ trans_choice('{0} There :form no apples|{1} There :form just :count apple|[2,19] There :form :count apples', 24, ['form' => 'is']) }}

    We can also use count passed into `trans_choice`, if needed, by using a special :count placeholder:

    {{ trans_choice('{0} There :form no apples|{1} There :form just :count apple|[2,19] There :form :count apples', 1, ['form' => 'is']) }}

    Finally, don’t forget to update your translation files with all the changes we made to the base translation.

    Russian:

    {
      "Welcome to our website, :Name": "Добро пожаловать на наш сайт, :Name",
      "{0} There :form no apples|{1} There :form just :count apple|[2,19] There :form :count apples": "{0} Нет яблок|{1} Только :count яблоко|[2,19] :count яблок"
    }

    French:

    {    
      "Welcome to our website, :Name": "Bienvenue sur notre site, :Name",
      "{0} There :form no apples|{1} There :form just :count apple|[2,19] There :form :count apples": "{0} Il n'y a pas de pommes|{1} Il n'y :form :count pomme|[2,19] Il y :form :count pommes"
    }

    Working with localized dates in Laravel

    To localize dates, we will be leveraging the power of Carbon, which is shipped with Laravel by default. Take a look at the Carbon documentation; you can do lots of cool things with it. For instance, we could invent our own locale with rules for date and time localization.

    For our simple example, we will display the current date localized for the selected language. In our routes/web.php, let’s update the welcome page route and pass the localized date message over to our welcome view as shown here:

    <?php
    Route::get('/', function () {
        $today = \Carbon\Carbon::now()
            ->settings(
                [
                    'locale' => app()->getLocale(),
                ]
            );
    
        // LL is macro placeholder for MMMM D, YYYY (you could write same as dddd, MMMM D, YYYY)
        $dateMessage = $today->isoFormat('dddd, LL');
    
        return view('welcome', [
            'date_message' => $dateMessage
        ]);
    });

    Let’s update resources/views/welcome.blade.php and add our date message, like so:

    {{ __('Welcome to our website, :Name', ['name' => 'amanda']) }}
    <br>
    {{ trans_choice('{0} There :form :count apples|{1} There :form just :count apple|[2,19] There :form :count apples', 1, ['form' => 'is']) }}
    <br>
    {{ $date_message }}

    Try switching languages on the localhost homepage — you’ll see that the dates are now localized.

    Formatting numbers and currencies with NumberFormatter

    In different countries, people use different formats to represent numbers; for example:

    • The United States → 123,123.12
    • France → 123 123,12

    Thus, to reflect these differences in your Laravel app, you could use NumberFormatter in the following way:

    <?php
    $num = NumberFormatter::create('en_US', NumberFormatter::DECIMAL);
    
    $num2 = NumberFormatter::create('fr', NumberFormatter::DECIMAL);

    You can even spell out the number in a particular language and display something like “one hundred twenty-three thousand one hundred twenty-three point one two”:

    <?php
    $num = NumberFormatter::create('en_US', NumberFormatter::SPELLOUT);
    
    $num2 = NumberFormatter::create('fr', NumberFormatter::SPELLOUT);

    On top of that, NumberFormatter enables you to localize currencies with ease; for example:

    <?php
    $currency1 = NumberFormatter::create('fr', NumberFormatter::CURRENCY);
    
    $currency2 = NumberFormatter::create('en_US', NumberFormatter::CURRENCY);
    

    So, for fr you will see euros, while for en_US the currency will be US dollars.

    Simplifying the translation process with Lokalise

    The actual translation of all your text is probably the most time-consuming process of Laravel localization. However, finding a good translation management solution like Lokalise can rescue you from ending up swamped with work. With Lokalise, the translation process can be carried out in just a few steps. Here are some basic guidelines on how to do it:

    • Grab your free trial to continue.
    • Next, install the Lokalise CLI. You can use it to create projects and upload and download translation files. Of course, there’s also a GUI available.
    • Open the Lokalise website and proceed to the “API tokens” section on your personal profile page. Generate a new read/write token.
    • Create a new translation project and set English as the base language.
    • Open project settings and copy the project ID.
    • Next, to upload your Laravel translation files to Lokalise, run: lokalise2 file upload --token <token> --project-id <project_id> --lang_iso fr --file PATH/TO/PROJECT/resources/lang/fr.json.
    • Return to the newly created Lokalise project. All your translation keys and values should be there. You can modify them as much as you like by editing or deleting, and adding new ones. You can filter the keys; for instance, you can find any untranslated ones which is very convenient.
    • When you are ready, download the edited translations back to Laravel by running: lokalise2 file download --token <token> --project-id <project_id> --format json --dest PATH/TO/PROJECT/resources/lang.

    Lokalise supports many platforms and formats. With its multiple features, you have the ability to order translations from professionals, upload screenshots in order to read text from them, and lots more. So, integrate your applications with Lokalise today and make your life a whole lot easier.

    Conclusion

    In this article, we have seen how to get started with Laravel localization. We’ve discussed how to perform translations, use placeholders, take advantage of pluralization, and how to add a language switcher. Hopefully, you found this article interesting and useful. Thank you for dropping by today and until next time!

    Further reading

    Talk to one of our localization specialists

    Book a call with one of our localization specialists and get a tailored consultation that can guide you on your localization path.

    Get a demo

    Related posts

    Learn something new every two weeks

    Get the latest in localization delivered straight to your inbox.

    Related articles
    Localization made easy. Why wait?
    The preferred localization tool of 3000+ companies