
Основные моменты.
Приложение может иметь любое количество языков для перевода.Язык приложения будет устанавливаться в зависимости от языковой метки в 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>
Можно выделить текущую, активную, ссылку языка придав ей, например, класс "active":
<a href="<?= route('setlocale', ['lang' => 'en']) ?>"<?= LocaleMiddleware::getLocale() === 'en' ? 'class="active"' : '' ?>>English</a> <a href="<?= route('setlocale', ['lang' => 'ru']) ?>"<?= LocaleMiddleware::getLocale() === null ? 'class="active"' : '' ?>>Русский</a>где === null указывается для основного языка, который не отображается в URL.
Создание ссылок на страницы сайта.
При использовании, в шаблоне, функции 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 тут. Кто не хочет вникать - устанавливайте и пользуйтесь! :)
ответ на комментарий Андрей от 12.10.2017
ответ на комментарий Сергей от 13.10.2017
Использовала Ваше решение и столкнулась с тем, что при основном языке, он у меня перебивается постоянно на русский, не зависимо от того, что указано в настройках app.locale и appl.fallback_locale.
Установила, например, основной язык украинский, через функцию setLocale, захожу в саму функцию \vendor\laravel\framework\src\Illuminate\Foundation\Application.php , смотрю, что ей приходит в качестве $locale, итого получается http://joxi.ru/l2Z6a6auwy9K72, что первый раз правильно устанавливается украинский язык, а потом он перебивается русским. Если переключиться на другой язык, http://joxi.ru/E2pgRgRh9pWnkm то все нормально. Проблема только с основным языком.
В настройках app русского вообще нет http://joxi.ru/LmGDXDXSeNVxj2.
Подскажите, в чем может быть причина? Использую laravel 5.5. Спасибо!
ответ на комментарий Юлия от 26.10.2017
Если по статье, то основной язык устанавливается в посреднике LocaleMiddleware и по-умолчанию, в моем примере, там стоит "ru".
ответ на комментарий Сергей от 26.10.2017
Изменила основной язык на en http://joxi.ru/BA0v3v3hJ8yajm, он сначала устанавливается функцией setLocale, а потом каким-то образом переопределяется на ru http://joxi.ru/EA4v7v7hw5Jzgm.
Скорее всего, ошибка у меня возникает из-за установленной админки.
Спасибо за помощь. Буду смотреть дальше.
c кодом:
Прописал helpers.php в файле composer.json в блок autoload, он должен стать примерно такой:
Моя новая строка была "files": ["app/Http/helpers.php"]
И все после чего вызываю эту функцию уже в самом шаблоне при выборе языка:
ответ на комментарий Илья от 15.02.2018
Язык по умолчанию
Как сделать, что бы при выходе или входе пользователя оставался тот язык, который он выбрал. После авторизации выкидывает на язык указанный в
Спасибо.
ответ на комментарий Игорь от 17.02.2018
Таким образом, например, что бы сохранить выбранный пользователем язык после аутентификации на сайте (после входа) нужно подправить стандартное перенаправление, в данном случае файл app\Http\Middleware\RedirectIfAuthenticated.php:
вместо
указываем
где 'home' – название любого маршрута для перенаправления.
Метод route сформирует ссылку с учетом языка.
ответ на комментарий Сергей от 17.02.2018
ответ на комментарий Игорь от 17.02.2018
Route::get('/{locale}', function ($locale) {
App::setLocale($locale);
return view('welcome');
ответ на комментарий Валерий от 21.02.2018
ответ на комментарий Александр от 17.05.2018
ответ на комментарий Sergey от 17.05.2018
Допустим у вас 3 таблицы ru_posts, en_posts, uk_posts, для выборки данных получаете текущую метку языка, например ru и делаете поиск по таблице текущий_язык . posts. Т.е. к метке добавляете название таблицы, будет ru_posts.
Если формировать ссылку так: href="{{ App\Http\Middleware\LocaleMiddleware::getLocale() .'/device/name/ }}, то ссылка получается " /ru/ru/device/name/ ".
А если с помощью url(): href="{{ url(App\Http\Middleware\LocaleMiddleware::getLocale() .'/device/name/) }}, то получется нормальная ссылка, с одним "/ru". " /ru/device/name ".
Laravel 5.6.
В чём может быть ошибка?
ответ на комментарий Владимир от 27.06.2018