How to Create Multilingual Website using Laravel Localization

Laravel’s localization features provide a convenient way to retrieve strings in various languages, allowing us to support the multiple languages within your application quickly. Language strings are stored in the files within the resources/lang directory. In addition, there should be a subdirectory for each language supported by the application within that directory.

We will create the middleware to check if the locale is set, then change the application’s locale.

Step 1: Install Laravel

laravel new localization

or

composer create-project laravel/laravel localization

Laravel Localization

By default, our application’s local language is English or en.

You can find it on the config >> app.php file.

// app.php 

/*
    |--------------------------------------------------------------------------
    | 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',

Here, the locale is set to en.

Now, if you check the resources >> lang folder, there is one folder called en, with some files like auth.php, pagination.php, passwords.php, and validation.php.

So, these files are translation files. But first, let’s check the auth.php file.

<?php

// auth.php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines are used during authentication for various
    | messages that we need to display to the user. You are free to modify
    | these language lines according to your application's requirements.
    |
    */

    'failed' => 'These credentials do not match our records.',
    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',

];

This file returns an array that has key-value pair. So, here you can define any key you like, but the values are on your language-specific statements.

An app.php file’s root folder name is en, so translation to these file’s statement is in English or en. If the root folder is fr means French translation, then the app.php file might look like below.

<?php

// auth.php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines are used during authentication for various
    | messages that we need to display to the user. You are free to modify
    | these language lines according to your application's requirements.
    |
    */

    'failed' => 'Ces informations d'identification ne correspondent pas à nos enregistrements',
    'throttle' => 'Trop de tentatives de connexion. Veuillez réessayer dans: secondes secondes.',

];

Remember, you need to add the same keys for all your translation files across the different languages and change the values respective to your languages.

In the above code, we have written two items: failed and throttle, and the values are translated into French.

So, when the user changes the languages, these statements will be displayed because, in the view file, we will call it by its key, and due to middleware, it will map to the correct languages.

Step 2: Creating Translation Files

Now we will go for three languages. English is already there.

  1. French
  2. Spanish
  3. Japanese

Let’s create a folder inside the resources >> lang folder.

Website using Laravel Localization

Create a file called sentence.php and add the following code inside all four folders.

For en >> sentence.php file,

<?php

// sentence.php

return [
  'welcome' => 'Welcome Friend'
];

For es >> sentence.php file,

<?php

// sentence.php

return [
  'welcome' => 'Bienvenido amigo'
];

For fr >> sentence.php file,

<?php

// sentence.php

return [
  'welcome' => 'Bienvenue mon ami'
];

And last, jp >> sentence.php file.

<?php

// sentence.php

return [
  'welcome' => 'ようこそ友達'
];

You can put as many texts as you want. I have just taken one text for this demo.

Now that we have placed all translations, let’s start working on the views and make our application load the above translations.

Step 3: Set up the view

Create a layouts folder inside the resources >> views folder, and inside that layouts folder, create an app.blade.php file and add the following code.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <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>
                                Language <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')}}" width="30px" height="20x"> English</a>
                                <a class="dropdown-item" href="lang/fr"><img src="{{asset('img/fr.png')}}" width="30px" height="20x"> French</a>
                                <a class="dropdown-item" href="lang/es"><img src="{{asset('img/es.png')}}" width="30px" height="20x"> Spanish</a>
                                <a class="dropdown-item" href="lang/jp"><img src="{{asset('img/jp.png')}}" width="30px" height="20x"> Japanese</a>
                            </div>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

Open your welcome.blade.php file, which is by default inside the resources >> views folder, and place below code, including a markup just for a languages dropdown.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-body">
                    hello
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Step 4: Setup route, controller, and middleware

Inside routes >> web.php, add the following route.

Route::get('lang/{locale}', 'HomeController@lang');

Define a lang method inside the HomeController.php file. For example, you can create a controller using the following command.

php artisan make:controller HomeController

Write the following code inside that file.

<?php

// HomeController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App;

class HomeController extends Controller
{
    public function lang($locale)
    {
        App::setLocale($locale);
        session()->put('locale', $locale);
        return redirect()->back();
    }
}

This function will see the locale and put it into the session.

Now, create a middleware using the following command.

php artisan make:middleware Localization

It will create a file inside the app >> Http >> Middleware folder.

Now open your newly created middleware file called Localization.php and update the handle method with the below code. So, our final file looks like below.

<?php

// Localization.php

namespace App\Http\Middleware;

use Closure;
use App;

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);
    }
}

Step 5: Register the Localization Middleware

Now add the middleware in App\Http\Kernel’s $ middlewareGroup’s array like so.

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,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

Now, one final thing is remaining. Add the following code inside the resources >> views >> layouts >> app.blade.php file.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <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>
                                Language <span class="caret"></span>
                            </a>
                            @switch($locale)
                                @case('fr')
                                <img src="{{asset('img/fr.png')}}" width="30px" height="20x"> French
                                @break
                                @case('es')
                                <img src="{{asset('img/jp.png')}}" width="30px" height="20x"> Spain
                                @break
                                @case('jp')
                                <img src="{{asset('img/jp.png')}}" width="30px" height="20x"> Japanese
                                @break
                                @default
                                <img src="{{asset('img/us.png')}}" width="30px" height="20x"> English
                            @endswitch
                            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                <a class="dropdown-item" href="lang/en"><img src="{{asset('img/us.png')}}" width="30px" height="20x"> English</a>
                                <a class="dropdown-item" href="lang/fr"><img src="{{asset('img/fr.png')}}" width="30px" height="20x"> French</a>
                                <a class="dropdown-item" href="lang/es"><img src="{{asset('img/es.png')}}" width="30px" height="20x"> Spanish</a>
                                <a class="dropdown-item" href="lang/jp"><img src="{{asset('img/jp.png')}}" width="30px" height="20x"> Japanese</a>
                            </div>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

If we change the language in the above code, the dropdown still shows Language instead of the current locale. We can fix this by adding a switch statement and checking what locale we have, and based on that, we will show the current locale with a flag icon like below.

Finally, add the following code inside the resources >> views >> welcome.blade.php file.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-body">
                <p>{{ trans('sentence.welcome')}}</p>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

I have used the trans() function, which takes one argument, the filename.array_key_name.

If you have done all correctly till now, go to the root route inside the browser, and you will see the dropdown with the four languages.

Select the language, and you will see the welcome message in a different language.

That’s it for this tutorial.

14 thoughts on “How to Create Multilingual Website using Laravel Localization”

  1. how setup this in web.php route file

    localhost:8000 which get lang strings from resources/lang/en directory

    localhost:8000/en default which get lang strings from resources/lang/en directory
    localhost:8000/ar which get lang strings from resources/lang/ar directory

    Reply
  2. One issue I had and have since fixed, is that the links for changing the language need to be absolute to work on every page. I had some pages with URLs like ‘example.com/one/two’, and when i clicked on the link to change the language it redirected me to the URL ‘example.com/one/lang/en’ – which does not exist and just gives a 404. To fix this just make your language changing links absolute.

    Before (doesn’t work on all pages)
    English

    After (notice the forward slash before ‘lang’) – WORKS
    English

    Other than that thank you for an amazing tutorial!

    Reply
  3. I having a problem making a CRUD with the multilanguage website

    when i make the edit button it say missing parameters, i doing that way:
    {{ route(‘user.edit’, $user->id, App()->getLocale()) }}

    Reply
  4. your solution works but how do we make changes to URL when we switch the language? Eg: when we switch from English ‘en’ to Hindi ‘hi’ the URL should also change. like /en/posts to /hi/posts !

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.