How to create multilingual translated routes in Laravel

Marcin Nabiałek picture Marcin Nabiałek · Aug 1, 2014 · Viewed 51.4k times · Source

I would like to create application with many translated routes depending on selected language. I've once described it at 3 methods of creating URLs in multilingual websites.

In this case it should be the first method from mentioned topic so:

  1. I have one default language
  2. I can have many other languages
  3. Current language should be calculated only by URL (without cookies/sessions) to make it really friendly also for search engines
  4. For default language there should be no prefix in URL, for other languages should be language prefix after domain
  5. Each part of url should be translated according to the current language.

Let's assume I have set default language pl and 2 other languages en and fr. I have only 3 pages - mainpage, contact page and about page.

Urls for site should look then this way:

/
/[about]
/[contact]
/en
/en/[about]
/en/[contact]
/fr
/fr/[about]
/fr/[contact]

whereas [about] and [contact] should be translated according to selected language, for example in English it should be left contact but for Polish it should be kontakt and so on.

How can it be done as simple as possible?

Answer

Marcin Nabiałek picture Marcin Nabiałek · Aug 1, 2014

First step:

Go to app/lang directory and create here translations for your routes for each language. You need to create 3 routes.php files - each in separate language directory (pl/en/fr) because you want to use 3 languages

For Polish:

<?php

// app/lang/pl/routes.php

return array(

    'contact' => 'kontakt',
    'about'   => 'o-nas'
);

For English:

<?php

// app/lang/en/routes.php

return array(
    'contact' => 'contact',
    'about'   => 'about-us'
);

For French:

<?php

// app/lang/fr/routes.php

return array(
    'contact' => 'contact-fr',
    'about'   => 'about-fr'
);

Second step:

Go to app/config/app.php file.

You should find line:

'locale' => 'en',

and change it into language that should be your primary site language (in your case Polish):

'locale' => 'pl',

You also need to put into this file the following lines:

/**
 * List of alternative languages (not including the one specified as 'locale')
 */
'alt_langs' => array ('en', 'fr'),

/**
 *  Prefix of selected locale  - leave empty (set in runtime)
 */
'locale_prefix' => '',

In alt_langs config you set alternative languages (in your case en and fr) - they should be the same as file names from first step where you created files with translations.

And locale_prefix is the prefix for your locale. You wanted no prefix for your default locale so it's set to empty string. This config will be modified in runtime if other language than default will be selected.

Third step

Go to your app/routes.php file and put their content (that's the whole content of app/routes.php file):

<?php

// app/routes.php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/


/*
 *  Set up locale and locale_prefix if other language is selected
 */
if (in_array(Request::segment(1), Config::get('app.alt_langs'))) {

    App::setLocale(Request::segment(1));
    Config::set('app.locale_prefix', Request::segment(1));
}


/*
 * Set up route patterns - patterns will have to be the same as in translated route for current language
 */
foreach(Lang::get('routes') as $k => $v) {
    Route::pattern($k, $v);
}


Route::group(array('prefix' => Config::get('app.locale_prefix')), function()
{
    Route::get(
        '/',
        function () {
            return "main page - ".App::getLocale();
        }
    );


    Route::get(
        '/{contact}/',
        function () {
            return "contact page ".App::getLocale();
        }
    );



    Route::get(
        '/{about}/',
        function () {
            return "about page ".App::getLocale();

        }
    );

});

As you see first you check if the first segment of url matches name of your languages - if yes, you change locale and current language prefix.

Then in tiny loop, you set requirements for your all route names (you mentioned that you want have about and contact translated in URL) so here you set them as the same as defined in routes.php file for current language.

At last you create Route group that will have prefix as the same as your language (for default language it will be empty) and inside group you simply create paths but those parameters about and contact you treat as variables so you use {about} and {contact} syntax for them.

You need to remember that in that case {contact} in all routes will be checked if it's the same as you defined it in first step for current language. If you don't want this effect and want to set up routes manually for each route using where, there's alternative app\routes.php file without loop where you set contact and about separately for each route:

<?php

// app/routes.php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/

/*
 *  Set up locale and locale_prefix if other language is selected
 */
if (in_array(Request::segment(1), Config::get('app.alt_langs'))) {

    App::setLocale(Request::segment(1));
    Config::set('app.locale_prefix', Request::segment(1));
}


Route::group(array('prefix' => Config::get('app.locale_prefix')), function()
{
    Route::get(
        '/',
        function () {
            return "main page - ".App::getLocale();
        }
    );


    Route::get(
        '/{contact}/',
        function () {
            return "contact page ".App::getLocale();
        }
    )->where('contact', Lang::get('routes.contact'));



    Route::get(
        '/{about}/',
        function () {
            return "about page ".App::getLocale();

        }
    )->where('about', Lang::get('routes.about'));


});

Fourth step:

You haven't mentioned about it, but there's one extra thing you could consider. If someone will use url /en/something where something isn't correct Route, I think the best solution to make redirection. But you should make redirection not to / because it's default language but to /en.

So now you can open app/start/global.php file and create here 301 redirection for unknown urls:

// app/start/global.php

App::missing(function()
{
   return Redirect::to(Config::get('app.locale_prefix'),301);
});