In this tutorial, you’ll learn how to perform Laravel localization and present your application in multiple languages. We’ll cover working with translation files, performing pluralization, creating a language switcher, and more using 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.
Prerequisites and assumptions for Laravel multi-language app
Before jumping into software internationalization, let’s cover some necessary prerequisites:
- We will be using Laravel version 10.x in this tutorial.
- You have basic PHP knowledge.
- Your domain is
localhost
. If not, then replacelocalhost
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 Laravel 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.
So, let’s prepare our welcome message for localization, which is easy in Laravel. Replace the text with the following code:
{{ __('Welcome to our website!') }}
This instructs 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 multi-language app
But how does Laravel know the current language or what languages are available in the application? It looks up the locale setup in the config/app.php
file.
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 above the keys should be self-explanatory. In short, the locale
key contains the default locale of your application (if no other locale was set in the code).
The fallback_locale
is activated if a non-existent locale is requested in your application.
Let’s add a new key to this file to provide the list of all 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.
Laravel translation files
General information
In Laravel, like many other frameworks, you 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, which involves 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 Laravel translation files
Now that we have configured all the locales to work with, let’s go ahead and move on to translating our default welcome message.
Let’s start by adding new localization files in JSON format in the resources/lang
folder. First, create a resources/lang/ru.json
file and add the corresponding translations:
{ "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 ({{ __('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 are in English.
Switching locales in Laravel multi-language app
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).
You can now visit your website and specify 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 English (en
) by default.
Middleware for Laravel localization
Passing the locale for every site link might not be what you want and could look aesthetically unpleasing. That’s why we’ll perform language setup via a special language switcher and use the user session to display the translated content. 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 run on every request, add it to the default middleware stack in 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, add a route to change the locale within the routes/web.php
file. We’re using a closure route, but you can use 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, add a simple language control. 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
Let’s greet our imaginary user (Amanda) instead of displaying a generic message:
{{ __('Welcome to our website, :Name', ['name' => 'amanda']) }}
Note that we used the name with a lowercase first letter, but the placeholder with an uppercase first letter. This allows Laravel to capitalize the actual word automatically. If the placeholder starts with a capital letter, :Name
produces “Amanda” or a full uppercase word, :NAME
, produces “AMANDA”.
Additionally, update our resources/lang/fr.json
and resources/lang/ru.json
translation files. Otherwise, we’ll only see the English version since 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. 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 |
.
If we need more plural forms, we can do that 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. You can add as many rules as needed.
If we want placeholders in our plural forms, it’s not a problem:
{{ 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']) }}
Update your translation files with all the changes 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
Properly handling dates and times is very important for localization process. To localize dates, we will leverage the power of Carbon, which is shipped with Laravel by default. Check the Carbon documentation for more cool features.
For our example, we will display the current date localized for the selected language. Update the welcome
page route in routes/web.php
and pass the localized date message to our welcome view:
<?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 ]); });
Update resources/views/welcome.blade.php
to add our date message:
{{ __('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
Different countries use different formats to represent numbers. For example:
- The United States → 123,123.12
- France → 123 123,12
To reflect these differences in your Laravel app, use NumberFormatter
:
<?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:
<?php $num = NumberFormatter::create('en_US', NumberFormatter::SPELLOUT); $num2 = NumberFormatter::create('fr', NumberFormatter::SPELLOUT);
NumberFormatter
also enables you to localize currencies with ease:
<?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 Laravel localization 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 can order translations from professionals, upload screenshots to read text from them, and more. 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!