На днях совершенствовал код, позволяющий пользователю переключаться между языками приложения, чтобы, при этом, текущий язык отображался в URL страницы (полезно для SEO-оптимизации). Данный процесс не настолько сложен, чтобы использовать сторонние библиотеки, поэтому предлагаю осуществить это самостоятельно. Реализацией делюсь ниже.

Основные моменты.
Приложение может иметь любое количество языков для перевода.
Язык приложения будет устанавливаться в зависимости от языковой метки в URL. Для основного языка метка не отображается. Допустим основной – русский:
http://laravel.loc
http://laravel.loc/en
http://laravel.loc/uk


Смена языка осуществляется при нажатии на соответствующие ссылки, которые будут вести на специальный маршрут (в анонимной функции будет устанавливать метку языка и перенаправлять назад). Так же, язык можно менять прямо в адресной строке.
Не используются сессии и куки, код простой, рассчитанный на максимальное быстродействие.


Создаем класс посредник LocaleMiddleware.

Для создания файла с классом - в консоли:
php artisan help make:middleware LocaleMiddleware
создастся соответствующий файл в папке app\Http\Middleware.

Код класса-посредника:
<?php
namespace App\Http\Middleware;

use Closure;
use App;
use Request;

class LocaleMiddleware
{
    public static $mainLanguage = 'ru'; //основной язык, который не должен отображаться в URl
    
    public static $languages = ['en', 'ru', 'uk']; // Указываем, какие языки будем использовать в приложении. 

    
/*
 * Проверяет наличие корректной метки языка в текущем URL
 * Возвращает метку или значеие null, если нет метки
 */
public static function getLocale()
{
    $uri = Request::path(); //получаем URI 


    $segmentsURI = explode('/',$uri); //делим на части по разделителю "/"


    //Проверяем метку языка  - есть ли она среди доступных языков
    if (!empty($segmentsURI[0]) && in_array($segmentsURI[0], self::$languages)) {

        if ($segmentsURI[0] != self::$mainLanguage) return $segmentsURI[0]; 

    }
    return null; 
}

    /*
    * Устанавливает язык приложения в зависимости от метки языка из URL
    */
    public function handle($request, Closure $next)
    {
        $locale = self::getLocale();

        if($locale) App::setLocale($locale); 
        //если метки нет - устанавливаем основной язык $mainLanguage
        else App::setLocale(self::$mainLanguage); 

        return $next($request); //пропускаем дальше - передаем в следующий посредник
    }
    
}

Метод getLocale() будет вызываться из файла маршрутизации (файл routes\web.php) для формирования префикса URL с меткой языка.
Метод handle() будет выполняться перед выполнением кода любого из маршрутов и устанавливать язык приложения – значение свойства 'locale' из файла config\app.php в зависимости от текущей метки языка из URL. Данное значение как раз и используется для осуществления перевода.


Подключаем класс посредника.


В файле app\Http\Kernel.php в конец свойства $middleware прописываем:
\App\Http\Middleware\LocaleMiddleware::class,
теперь класс-посредник будет вызываться при каждом запросе на сервер.




Маршрутизация.


В Laravel 5.4, маршруты прописываются в файле routes\web.php.
Т.к. нужно, чтобы метка языка отображалась в адресной строке, все маршруты (которые подлежат переводу) должны иметь префикс, который будет формироваться из текущего URL страницы. Для этого маршруты должны быть включены в группу с префиксом:
Route::group(['prefix' => App\Http\Middleware\LocaleMiddleware::getLocale()], function(){
..
});

В данном случае, до того как приложение определит подходящий маршрут, вызывается статический метод getLocale() нашего посредника, который подставляет нужное значение префикса. Для основного языка (метку которого не нужно отображать), посредник вернет значение null и префикс не будет задействован.

Пример файла маршрутов:
Route::group(['prefix' => App\Http\Middleware\LocaleMiddleware::getLocale()], function(){
    
    Route::get('/', function () {
            return view('welcome');
    })->name('welcome');
    

    Route::get('/posts', ['uses' => 'PostController@getPosts'])->name('posts');
    Route::get('/home', 'HomeController@index')->name('home');


    Auth::routes(); //стандартные маршруты аутентификации

    
    Route::group(['prefix' => 'admin'], function(){

            Route::get('/', ['uses' => 'admin\AdminController@show'])->name('admin_index');
            Route::get('/add/post',['uses' =>'admin\AdminPostController@show'])->name('admin_add_post');
            Route::post('/add/post',['uses' =>'admin\AdminPostController@create'])->name('admin_add_post_p');
    });

});

Так же, в файл маршрутов нужно добавить специальный маршрут (отдельно от группы остальных маршрутов), который, в анонимной функции, будет обрабатывать переключение языков пользователем (нажатие на соответствующую ссылку):

//Переключение языков
Route::get('setlocale/{lang}', function ($lang) {

    $referer = Redirect::back()->getTargetUrl(); //URL предыдущей страницы
    $parse_url = parse_url($referer, PHP_URL_PATH); //URI предыдущей страницы

    //разбиваем на массив по разделителю
    $segments = explode('/', $parse_url);

    //Если URL (где нажали на переключение языка) содержал корректную метку языка
    if (in_array($segments[1], App\Http\Middleware\LocaleMiddleware::$languages)) {

        unset($segments[1]); //удаляем метку
    } 
    
    //Добавляем метку языка в URL (если выбран не язык по-умолчанию)
    if ($lang != App\Http\Middleware\LocaleMiddleware::$mainLanguage){ 
        array_splice($segments, 1, 0, $lang); 
    }

    //формируем полный URL
    $url = Request::root().implode("/", $segments);
    
    //если были еще GET-параметры - добавляем их
    if(parse_url($referer, PHP_URL_QUERY)){    
        $url = $url.'?'. parse_url($referer, PHP_URL_QUERY);
    }
    return redirect($url); //Перенаправляем назад на ту же страницу                            

})->name('setlocale');

При обработке данного маршрута выполнится анонимная функция, которая установит языковую метку переданную пользователем в GET-параметрах и вернет на предыдущую страницу с новой меткой.


Переключение языков.

Ссылки, ведущие на маршрут меняющий метку языка, будут такого вида:
<a href="<?= route('setlocale', ['lang' => 'en']) ?>">English</a>
<a href="<?= route('setlocale', ['lang' => 'ru']) ?>">Русский</a>
<a href="<?= route('setlocale', ['lang' => 'uk']) ?>">Українська</a>


Создание ссылок на страницы сайта.

При использовании, в шаблоне, функции route() которой передаем, в качестве аргумента, название нужного маршрута, ничего не меняется:
<a href="{{ route('home') }}">Home</a>

Если атрибут href указывается с помощью функции-помошника url() (или любым другим образом), то в начале передаем текущий язык приложения:
<a href="{{ url(App\Http\Middleware\LocaleMiddleware::getLocale() .'/home') }}">Home</a>
или
<a href="{{ App\Http\Middleware\LocaleMiddleware::getLocale() .'/home' }}">Home</a>




Отображать в URL все языковые метки, в т.ч. для основного языка.


Если, нужно отображать в URL все языки (в т.ч. основной), необходимо изменить несколько строк в коде.

В посреднике LocaleMiddleware:
public static function getLocale()
{
    $uri = Request::path(); //получаем URI 

    $segmentsURI = explode('/',$uri); //делим на части по разделителю "/"

    //Проверяем метку языка  - есть ли она среди доступных языков
    if (!empty($segmentsURI[0]) && in_array($segmentsURI[0], self::$languages)) {

        return $segmentsURI[0]; 

    } else {
        return  self::$mainLanguage; 
    }
}

public function handle($request, Closure $next)
{
    $locale = self::getLocale();

    if($locale) App::setLocale($locale); 

    return $next($request); //пропускаем дальше - передаем в следующий посредник
}

В маршрутизаторе (файл routes\web.php):
//Переключение языков
Route::get('setlocale/{lang}', function ($lang) {

    $referer = Redirect::back()->getTargetUrl();; //URL предыдущей страницы
    $parse_url = parse_url($referer, PHP_URL_PATH); //URI предыдущей страницы

    //разбиваем на массив по разделителю
    $segments = explode('/', $parse_url);

    //Если URL (где нажали на переключение языка) содержал корректную метку языка
    if (in_array($segments[1], App\Http\Middleware\LocaleMiddleware::$languages)) {

        unset($segments[1]); //удаляем метку
    } 
    
    //Добавляем метку языка в URL (если выбран не язык по-умолчанию)
    array_splice($segments, 1, 0, $lang); 

    //формируем полный URL
    $url = Request::root().implode("/", $segments);
    
    //если были еще GET-параметры - добавляем их
    if(parse_url($referer, PHP_URL_QUERY)){
        $url = $url.'?'. parse_url($referer, PHP_URL_QUERY);
    }
    return redirect($url); //Перенаправляем назад на ту же страницу                            

})->name('setlocale');

Так же в начало данного файла добавляем маршрут для главной страницы:
Route::get('/', function () {
    return redirect('/'. App\Http\Middleware\LocaleMiddleware::$mainLanguage);
});
теперь при переходе на главную страницу - домен (без метки языка), автоматически будет перенаправление на эту же страницу с меткой основного языка.

Все готово. Далее можно создавать и использовать языковые файлы стандартным способом. Основы использования локализации можно почитать, например, тут.


Оформил, все же, данный код в виде пакета для Composer. Страничка на GitHub тут. Кто не хочет вникать - устанавливайте и пользуйтесь! :)