Tutorials

Laravel localization: a step-by-step guide

In this tutorial, you will learn how to perform Laravel localization and present your application in multiple languages. You will discover how...

Shanika Wickramasinghe Written by Shanika Wickramasinghe · 14 min read >

In this tutorial, you will learn how to perform Laravel localization and present your application in multiple languages. You will discover how to work with translation strings, datetime, numerical representations, and switch between locales. 

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.

All you have to do is store every string of the other language in a file within the resources\lang directory. You need to create a subdirectory for every language supported by your application. 

Let’s start by creating a simple Laravel app. For this application, we will be using Laravel version 7.x and PHP version 7.4.6. Please note that this tutorial has been created with the assumption that you have the necessary knowledge of the PHP programming language and the Laravel framework.

The complete source code belonging to this project can be found on GitHub.

Setting up Laravel

First, you need to have Composer to install Laravel.

Composer is a package manager designed for the PHP programming language. It can be used to manage PHP software dependencies. You can download Composer here. Once you have successfully downloaded Composer, run the following command in the command prompt:

composer global require laravel/installer

Next, you have to create your Laravel application, like so:

laravel new laravel_localization

After that, open your application in a text editor of your choice, like Visual Code, and start your application with:

php artisan serve

Finally, you can visit the application in your browser via localhost:8080. That’s all in terms of setting the environment of the application. Let’s move on to Laravel localization.

Getting Started with Laravel Localization

So, your default application language should be English. This is visible in the app.php file, which can be found in the config directory.

 'locale' => 'en',

Therefore, if you want to use multiple languages, you will have to add them to the resources. 

Resources Directory

Navigate to the resources directory and then create a new folder in the lang subdirectory. Name the folder according to the language you are choosing. Create a new lang.php file within the folder you have just created and in the en folder. The en folder is automatically created for you when you set up Laravel. 

As you can see, you are able to add as many languages as you want. Each language should have a separate subdirectory. Apart from English, I have used German, Chinese, Spanish, and French in the tutorial application we are building.

Next let’s check the content of the lang.php file in the en folder:

<?php
 return [ 'welcome' => 'Welcome',
 'title'=> ‘Laravel Localization’,
 'message'=> "Let's learn Laravel Localization"]
?>

Then we’ll look at the content of the lang.php file in the de folder. You can change the value in the welcome field to a language of your choice. Here, I have used German for demonstration purposes: 

<?php
 return [ 'welcome' => 'Herzlich willkommen',
 'message'=> 'Laravel-Lokalisierung',
 'message'=> 'Lernen wir die Lokalisierung mit Laravel']
?>

You may prepare translations for other languages using Google Translate. However, if you are building a commercial app, Google translations won’t be good enough as they are known to have many issues. In this case, you should definitely hire a professional translator, and this can be done easily using Lokalise!

This current method of defining the translations is known as using short keys. Note that you must have the same keys in all the translation files of the languages you use. You only have to change the values to respectively match the languages you chose to translate.

Home Page

Next, navigate to the home.blade.php in the views subdirectory within the resources directory and add the following content:

<body>
    <div class="container">
        <h1 class="display-4 text-center" style="font-size: 3.0rem">{{ __('lang.welcome')}}</h1>
        <h3 class="display-4 text-center" style="font-size: 2.0rem">{{ __('lang.title') }}</h3>
        <h4 class="display-4 text-center" style="font-size: 1.5rem">{{ __('lang.message')}}</h4>
        <br><br>
    </div>
</body>
</html>

Here, we have used {{ __(‘filename.arraykey’)}} to represent the translation strings.

There are several methods to add the strings other than what is shown here. For instance:

  • Use echo inside a php tag:
<?php
    echo __('lang.welcome')
?>
  • Use the @lang directive to achieve the same thing if you are using a blade template engine:
@lang('lang.welcome')

Configuring the Locale

Now, in order to use the language you specified, you have to change the default language configuration. Go to the app.php file in the config main directory.

There, you will find a field called locale. In my example, its value will be de as I am using German. This is the name of the directory within which the language strings of your choice can be found. 

There’s also a fallback_locale option, which is the language to use when the active language does not have a translation string for a key. You can configure this too, if needed.

'locale' => 'de',

And here’s the output:

Determining the Locale From URI

Next, let’s see how we choose the view we want to see in the browser, in terms of language. We’ll try to infer the language from the URI. For this, we have to handle the routes of the application. Therefore, navigate to the routes directory and open the web.php file. The routes/web.php file defines routes that are intended for your web interface.

Add a new $lang variable. Use the App::setlocale(); function to change the active language at runtime. 

Route::get('/{lang}', function ($lang) {
    App::setlocale($lang);
    return view('home');
});

Now you can select the language in which you want to view the application using the address bar. For example, if your URI is 127.0.0.1:8000/es, you will see:

Likewise, if your URI is 127.0.0.1:8000/in you will see:

Add one of the languages you have included at the end of the URL and you will see the homepage in that language!

How to Work with Translation Strings in Laravel Localization

So, in the previous processes, we used short keys as our reference point. Now, assume you are building an application that requires heavy translation. Using short keys in such an application could prove very confusing when you have to reference them in the views. To avoid this, Laravel recommends the use of the default translation as the key. You have to create JSON files in order to achieve this.

In our application, we have a welcome message that says, “Let’s learn Laravel Localization.” Now, let’s create a translation file as resources/lang/de.json, which contains the following: 

de.json file:

{
    "Let's learn Laravel Localization" : "Lernen wir die Laravel-Lokalisierung"
}

es.json file:

{
"Let's learn localization with Laravel" : "Aprendamos localización con Laravel"
}

Now you can include the string in the default language (English) in the home.blade.php file. You only have to choose what language you want in the URL. 

<div class="container">
        <h1 class="display-4 text-center" style="font-size: 3.0rem">{{ __('lang.welcome')}}</h1>
        <h3 class="display-4 text-center" style="font-size: 2.0rem">{{ __('lang.title') }}</h3>
        <h4 class="display-4 text-center" style="font-size: 1.5rem">{{ __("Let's learn localization with Laravel")}}</h4>
        <br><br>
</div>

Let’s go to 127.0.0.1/es again:

Also, note that if the particular translation string is not found, the __ function will show the translation string key you added in the content.

Defining Placeholders in Translation Strings

Also, you can make localization more interesting by placing parameters in the strings. Every placeholder should begin with a : (colon) as demonstrated in the following example:

<?php
return [
    'welcome'=>'Herzlich Willkommen :Name',
];
?>

Make sure the relevant changes are reflected in the home.blade.php file, as below:

<div class="container">
        <h1 class="display-4 text-center" style="font-size: 3.0rem">{{ __('lang.welcome', ['Name' => 'Toni'])}}</h1>
 </div>

Then your application will display the welcome message with your name in it:

Remember that the translated value is case sensitive to the placeholder. If your placeholder is in all capital letters or only the first letter is a capital, the same will be reflected in the value, for instance:

<?php
return [
    'welcome'=>'Herzlich willkommen :NAME',
]
?>
<div class="container">
        <h1 class="display-4 text-center" style="font-size: 3.0rem">{{ __('lang.welcome', ['NAME' => 'Toni'])}}</h1>
 </div>

Pluralization with Laravel Localization

So, different languages have different rules to make words or sentences plural. Of course, this can be a difficult task to accomplish. The singular and plural forms of a certain string can be identified using a “pipe” | character. There are two ways to achieve pluralization in Laravel localization. 

The first one is to have two choices; one for singular and one for plural, separated with the pipe character. You can choose what form you want by specifying 1 or 2. See the below example:

lang.php

<?php
return [
    'welcome'=>'Welcome, :Name',
    'title' => 'Laravel Localization',
    'languages'=> 'There is one language|There are many languages',
];
?>

home.blade.php

<?php echo trans_choice('lang.languages', 2)?>

Output:

The second option is to use some complex pluralization rules. You can define strings for number ranges as shown below: 

lang.php

<?php
return [
    'welcome'=>'Welcome, :Name',
    'title' => 'Laravel Localization',
    'languages'=> 'There is one language|There are many languages',
    'languagesRange' => '{0} There are no languages|[1,19] There are some languages|[20,*] There are many languages',
];
?>

You must use the function trans_choice with translation strings that have pluralization options. This is used to retrieve the line for a particular count.

home.blade.php

<?php echo trans_choice('lang.languagesRange', 0)?>

As you can see, the count is 0, so it will produce this output:

 

We can specify the pluralization using a :count placeholder.

lang.php

<?php
return [
    'welcome'=>'Welcome, :Name',
    'title' => 'Laravel Localization',
    'languages'=> 'There is one language|There are many languages',
    'languagesRange' => '{0} There are no languages|[1,19] There are some languages|[20,*] There are many languages',
    'count'=> '{0} There are no languages explained in this tutorial|{1} There is one language explained in this tutorial|[2,*] There are :count languages explained in this tutorial'
];
?>

home.blade.php

<?php 
       echo trans_choice('lang.count',1);
?>

 

Output:

These pluralization options can be further extended to have placeholder attributes. You can pass a third argument; an array to the trans_choice function. For instance:

'minutes_ago' => '{1} :value minute ago|[2,*] :value minutes ago'
echo trans_choice('time.minutes_ago', 5, ['value' => 5]);

Extending Laravel Localization with Middleware and Controllers

Now you know the basics of Laravel localization, let’s set up middleware and a controller to make the localization of the application we are building more manageable. The middleware checks the current locale stored in the session and sets it accordingly for every incoming request. 

With the use of middleware, instead of configuring a locale in the config/app.php or in the URI, the user just has to select the locale in the application, and the middleware will handle setting the locale.

First, we have to create a controller to be able to change the language, like so:

php artisan make: controller LocalizationController

The controller you just created can be found in the app/Http/Controllers directory. Write the function shown below to keep the selected language. The locale is stored in the session so that the middleware can register it.

<?php
namespace App\Http\Controllers;

use App;
use Illuminate\Http\Request;

class LocalizationController extends Controller {
    public function index($locale){
        App::setlocale($locale);
        session()->put('locale', $locale);
        return redirect()->back();
    }
}

Next, let’s create the middleware. Use the following command to do this:

php artisan make: middleware Localization

The middleware you just created can be found in the app\Http\Middleware directory. It should contain content similar to the following:

<?php
namespace App\Http\Middleware;

use App;
use Closure;

class Localization {
    /**
     * Handle an incoming request.
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */

    public function handle($request, Closure $next) {
        if (session()->has('locale')) {
            App::setlocale(session()->get('locale'));
        }
        return $next($request);
    }
}

Finally, add the middleware you created to kernel.php under the middlewareGroups array.

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\Localization::class, // Localization Middleware
        ],

Determining the Locale from a Dropdown Menu

Now let’s make this more interesting by adding a dropdown menu to select the language. Here, you can view the desired language by selecting an option from the menu. 

home.blade.php contains the content shown in the web browser:

@extends('layout')
@section('content')
    <div class="container">
        <h1 class="display-4 text-center" style="font-size: 3.0rem">{{ __('lang.welcome', ['Name' => 'Toni'])}}</h1>
        <h3 class="display-4 text-center" style="font-size: 2.0rem">{{ __('lang.title') }}</h3>
        <h4 class="display-4 text-center" style="font-size: 1.5rem">{{ __("Let's learn Laravel Localization")}}</h4>
        <br><br>
        <h4 class="display-4 text-center" style="font-size: 1.5rem">{{ __("Pluralization Rules")}}</h4>
        <h5 class="display-4 text-center" style="font-size: 1.0rem"><b>Two options: </b><?php echo trans_choice('lang.languages', 2)?></h6>
        <h5 class="display-4 text-center" style="font-size: 1.0rem"><b>Ranges: </b><?php echo trans_choice('lang.languagesRange', 5)?></h6>
        <h5 class="display-4 text-center" style="font-size: 1.0rem"><b>Count Placeholder: </b><?php echo trans_choice('lang.count', 5)?></h6>
    </div>
@endsection

layout.blade.php contains the source code for the layout of the homepage:

<body>
<div id="app">
    <div style="background-color: #343a40;">
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark container">
            <div class="collapse navbar-collapse" id="navbarToggler">
                <ul class="navbar-nav ml-auto">
                    @php $locale = session()->get('locale'); @endphp
                    <li class="nav-item dropdown">
                        <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button"
                           data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                            @switch($locale)
                                @case('us')
                                <img src="{{asset('img/us.png')}}"> English
                                @break
                                @case('de')
                                <img src="{{asset('img/de.png')}}"> German
                                @break
                                @case('in')
                                <img src="{{asset('img/in.png')}}"> Hindi
                                @break
                                @case('fr')
                                <img src="{{asset('img/fr.png')}}"> French
                                @break
                                @case('es')
                                <img src="{{asset('img/es.png')}}"> Spanish
                                @break
                                @case('ch')
                                <img src="{{asset('img/ch.png')}}"> Chinese
                                @break
                                @default
                                <img src="{{asset('img/us.png')}}"> English
                            @endswitch
                            <span class="caret"></span>
                        </a>
                        <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                            <a class="dropdown-item" href="lang/en"><img src="{{asset('img/us.png')}}"> English</a>
                            <a class="dropdown-item" href="lang/de"><img src="{{asset('img/de.png')}}"> German</a>
                            <a class="dropdown-item" href="lang/in"><img src="{{asset('img/in.png')}}"> Hindi</a>
                            <a class="dropdown-item" href="lang/fr"><img src="{{asset('img/fr.png')}}"> French</a>
                            <a class="dropdown-item" href="lang/es"><img src="{{asset('img/es.png')}}"> Spanish</a>
                            <a class="dropdown-item" href="lang/ch"><img src="{{asset('img/ch.png')}}"> Chinese</a>
                        </div>
                    </li>
                </ul>
            </div>
        </nav>
    </div>
    <main class="py-4">
        @yield('content')
    </main>
</div>  
</body>
</html>

Here are the final lang.php files for English and German, containing the relevant translations.

resources/lang/en/lang.php

<?php
return [
    'welcome'=>'Welcome, :Name',
    'title' => 'Laravel Localization',
    'languages'=> 'There is one language|There are many languages',
    'languagesRange' => '{0} There are no languages|[1,19] There are some languages|[20,*] There are many languages',
    'count'=> '{0} There are no languages explained in this tutorial|{1} There is one language explained in this tutorial|[2,*] There are :count languages explained in this tutorial'
];
?>

resources/lang/de/lang.php

<?php
return [
    'welcome'=>'Herzlich willkommen, :Name',
    'title' => 'Laravel-Lokalisierung',
    'languages'=> 'Es gibt eine Sprache|Es gibt viele Sprachen',
    'languagesRange' => '{0} Es gibt keine Sprachen|[1,19] Es gibt einige Sprachen|[20,*] Es gibt viele Sprachen',
    'count'=> '{0} In diesem Tutorial werden keine Sprachen erklärt|{1} In diesem Tutorial wird eine Sprache erklärt|[2,*] In diesem Tutorial werden :count Sprachen erklärt'
];

Output:

Date, Time, and Other Settings in Laravel Localization

Date and Time

Setting the date and time according to the user’s time zone might be a bit different from what we have done so far. You can use the “Laravel Timezone” third-party package for this task. It sets the time zone of a particular user logged in to the application and shows the date and time according to their local time zone. 

So, how does it work? It listens to the user login events and sets the time zone stored in the database. Laravel GeoIP is used to check the location-specific data of users. Said data can be used to determine the currency, ISO code, and time zone details of logged-in users. 

You can display the localized time zone with a Timezone facade, like so:

Timezone::convertToLocal($post->created_at)

You can also use the blade directive as shown below:

@displayDate($post->created_at)
@displayDate($post->created_at, 'Y-m-d g:i', true)

Moreover, here is how to convert your localized date to UTC:

Timezone::convertFromLocal($request->get('publish_at'));

There’s another popular library, named Laravel-Date, used by developers to localize date and time. You can set it up by running the following command:

composer require jenssegers/date

If you are using a version of Laravel below 5.5, you have to register the service provider in the config/app.php once the package is successfully downloaded.

Jenssegers\Date\DateServiceProvider::class,

Let’s see an example of using the Laravel-Date library:

<?php
use Jenssegers\Date\Date;

Date::setLocale('de');
echo Date::now()->format('j F Y H:i:s'); 
echo Date::parse('-1 day')->diffForHumans(); 
?>

Here is a list of definitions of the abbreviations used in the code:

j – The day of the month with no leading zeros 

F – Full textual representation of the month

Y – Full representation of the year with four digits

See the full list of format characters here.

Output:

11 Juni 2020 04:32:09

vor 1 Tag

Numerical Representations with NumberFormatter

Your applications behave in a locale-specific format when it comes to number representations as well. Although the programs store and operate on numbers with a binary representation, independent of the locale, that’s not the case when displaying the numbers. 

For instance, different countries use different formats to represent the number 123123.12, as demonstrated below:

The United States → 123,123.12

Germany → 123.123,12

France → 123 123,12

<?php
$num = NumberFormatter::create('en_US', NumberFormatter::DECIMAL); 
echo  "US: ".$num->format(123123.12);
$num1 = NumberFormatter::create('de', NumberFormatter::DECIMAL); 
echo  "Germany: ".$num1->format(123123.12);
$num2 = NumberFormatter::create('fr', NumberFormatter::DECIMAL); 
echo "France: ".$num2->format(123123.12);        
?>

Output:

US : 123,123.12

Germany: 123.123,12

France: 123 123, 12

 

You can even spell out the number in a particular language.

<?php
  $num = NumberFormatter::create('en_US', NumberFormatter::SPELLOUT); 
  echo  "US: ".$num->format(123123.12);
  $num1 = NumberFormatter::create('de', NumberFormatter::SPELLOUT); 
  echo  "Germany: ".$num1->format(123123.12);  
?>

Output:

US: one hundred twenty-three thousand one hundred twenty-three point one two

Germany: einhundertdreiundzwanzigtausendeinhundertdreiundzwanzig Komma eins zwei

The NumberFormatter class in PHP provides methods not only to handle numbers, but currencies and percentages as well, according to the locale of your choice. The only issue with NumberFormatter is that it is locale-sensitive, and you may need to create new NumberFormatters for each locale.

Currency Formatting

It’s convenient when the specific currencies for countries are present when handling payments in your web applications. For instance:

$currency1 = NumberFormatter::create('de_DE', NumberFormatter::CURRENCY); 
        echo "Germany: ".$currency1->format(123123);
$currency2 = NumberFormatter::create('en_US', NumberFormatter::CURRENCY); 
        echo "US: ".$currency2->format(123123);

Output:

US: $123,123.00

Germany: 123.123,00 €

The local currency changes to the Euro when you use de_DE and likewise it changes to the US dollars when en_US is used.

How to Override Package Language Files

Some of the packages come with their own language files. All you have to do is place the relevant files in the resources/lang/vendor/{package}/{locale} directory.

For Example:

Assume that you want to override the US English translation in text.php in the example/blog package. You just have to place your language file in resources/lang/vendor/blog/en/text.php. Make sure you only define the translation strings that you want to override. Other strings will be loaded from the original language files in the package. 

Simplifying the Translation Process with Lokalise

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

  1. Enter a free trial to continue
  2. Next, install the Lokalise CLI. You can use it to create projects, upload, and download translation files.
  3. Go to the “API tokens” section on your personal profile page and generate a read/write token.
  4. Create a new project and set English as the base language.
  5. Go to Settings by clicking on “More” on the project page.
  6. Copy the project ID and the generated token and then run: lokalise2 file upload --token <token> --project-id <project_id> --lang_iso en --file PATH/TO/PROJECT/resources/lang/en.json
  7. The above step should upload English translations to Lokalise. To upload further languages run the same command for the other JSON files.
  8. Head over to the project overview page. All your translation keys and values will be there. You can modify them as much as you like by editing, deleting, and adding new ones. You can filter the keys, for example, you can find the untranslated ones which is really convenient.
  9. Download the edited translations and run: lokalise2 file download --token <token> --project-id <project_id> --format json --dest PATH/TO/PROJECT/resources/lang

A more detailed guide on how to integrate your applications with Lokalise can be found here.

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

Laravel localization is an exciting task with which you can create multilingual applications. There are several ways in which you can configure multiple languages with your application. If you follow the above steps correctly, you should be able to build an application in Laravel with localization enabled. However, there is still a lot to learn about localization. Here is a useful article that lists important technical terms that you will come across in Laravel. You can also read about PHP language files here.

I hope you enjoyed my article. See you next time!

Written by Shanika Wickramasinghe
Shanika is a software engineer by profession and a Graduate in Information Technology. Her fortes are Web and Mobile Development. Shanika considers writing as the best medium to learn and share her knowledge at the same time. She is passionate about everything she does and loves to travel and enjoy nature whenever she takes a break from her busy work schedule. You can connect with her at https://www.linkedin.com/in/shanikawickramasinghe/ Profile