Я хотел бы создать приложение со многими переведенными маршрутами в зависимости от выбранного языка. Однажды я описал это тремя способами создания URL-адресов на многоязычных веб-сайтах .
В этом случае это должен быть первый метод из указанной темы, поэтому:
Предположим, что я установил язык по умолчанию pl
и 2 других языка en
и fr
. У меня есть только 3 страницы – главная страница, страница контактов и страница.
Urls для сайта должны выглядеть следующим образом:
/ /[about] /[contact] /en /en/[about] /en/[contact] /fr /fr/[about] /fr/[contact]
тогда как [about]
и [contact]
должны быть переведены в соответствии с выбранным языком, например, на английском языке его следует оставлять в contact
но по-польски он должен быть kontakt
и так далее.
Как это можно сделать как можно проще?
Первый шаг:
Перейдите в каталог app/lang
и создайте здесь переводы для ваших маршрутов для каждого языка. Вам необходимо создать 3 файла routes.php
– каждый в отдельном языковом каталоге (pl / en / fr), потому что вы хотите использовать 3 языка
Для польских:
<?php // app/lang/pl/routes.php return array( 'contact' => 'kontakt', 'about' => 'o-nas' );
Для английского:
<?php // app/lang/en/routes.php return array( 'contact' => 'contact', 'about' => 'about-us' );
Для французского:
<?php // app/lang/fr/routes.php return array( 'contact' => 'contact-fr', 'about' => 'about-fr' );
Второй шаг:
Перейдите в файл app/config/app.php
.
Вы должны найти строку:
'locale' => 'en',
и изменить его на язык, который должен быть вашим основным языком сайта (в вашем случае польский):
'locale' => 'pl',
Вам также необходимо поместить в этот файл следующие строки:
/** * 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' => '',
В конфигурации alt_langs
вы устанавливаете альтернативные языки (в вашем случае en
и fr
) – они должны совпадать с именами файлов с первого шага, на котором вы создали файлы с переводами.
И locale_prefix
– это префикс для вашей локали. Вам не нужен префикс для вашего локали по умолчанию, поэтому он установлен в пустую строку. Эта конфигурация будет изменена во время выполнения, если будет выбран другой язык по умолчанию.
Третий этап
Перейдите в файл app/routes.php
и поместите их содержимое (это весь контент файла app/routes.php
):
<?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(); } ); });
Как вы видите, сначала вы проверяете, совпадает ли первый сегмент URL с именами ваших языков – если да, вы меняете языковой стандарт и префикс текущего языка.
Затем в крошечной петле вы устанавливаете требования для всех ваших имен маршрутов (вы упомянули, что хотите, чтобы и about
них routes.php
и которые были переведены по URL-адресу), поэтому здесь вы устанавливаете их так же, как определено в файле routes.php
для текущего языка.
Наконец, вы создаете группу маршрутов, у которой будет префикс как тот же, что и ваш язык (для языка по умолчанию он будет пустым), а внутри группы вы просто создаете пути, а те параметры, about
вы говорите, и contact
, рассматриваются как variables
поэтому вы используете {about}
и {contact}
для них.
Вы должны помнить, что в этом случае {contact}
во всех маршрутах будет проверяться, если он будет таким же, как вы определили его на первом этапе для текущего языка. Если вы не хотите этого эффекта и хотите настроить маршруты вручную для каждого маршрута, используя там, есть альтернативный файл app\routes.php
без цикла, в котором вы устанавливаете contact
и отдельно для каждого маршрута:
<?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')); });
Четвертый шаг:
Вы не упомянули об этом, но есть одна дополнительная вещь, которую вы могли бы рассмотреть. Если кто-то будет использовать url /en/something
где something
не правильно, Route, я думаю, лучшее решение для перенаправления. Но вы должны сделать перенаправление не до /
потому что это язык по умолчанию, но для /en
.
Итак, теперь вы можете открыть файл app/start/global.php
и создать здесь 301 перенаправление для неизвестных URL-адресов:
// app/start/global.php App::missing(function() { return Redirect::to(Config::get('app.locale_prefix'),301); });
То, что Марцин Набиялек предоставил нам в своем первоначальном ответе, является прочным решением проблемы локализации маршрута.
Малый жучок:
Единственный реальный недостаток в его решении заключается в том, что мы не можем использовать кешированные маршруты, которые иногда могут принести большую пользу в соответствии Laravel's
документами Laravel's
:
Если ваше приложение использует исключительно маршруты на основе контроллера, вы должны использовать кеш маршрутов Laravel. Использование кеша маршрута резко сократит время, необходимое для регистрации всех маршрутов вашего приложения. В некоторых случаях регистрация вашего маршрута может быть даже до 100 раз быстрее. Чтобы создать кеш маршрута, просто выполните команду
route:cache
Artisan.
Почему мы не можем кэшировать наши маршруты?
Поскольку метод Marcin Nabiałek генерирует новые маршруты на основе locale_prefix
динамически, кэширование их приведет к ошибке 404
при посещении любого префикса, не сохраненного в переменной locale_prefix
во время кеширования.
Что мы храним?
Фонд кажется действительно солидным, и мы можем сохранить большую часть этого!
Мы можем, конечно, сохранить различные файлы маршрутов, специфичные для локализации:
<?php // app/lang/pl/routes.php return array( 'contact' => 'kontakt', 'about' => 'o-nas' );
Мы также можем хранить все переменные app/config/app.php
:
/** * Default locale */ 'locale' => 'pl' /** * 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' => '', /** * Let's also add a all_langs array */ 'all_langs' => array ('en', 'fr', 'pl'),
Нам также понадобится бит кода, который проверяет сегменты маршрута. Но поскольку в этом заключается использование кэша, нам нужно переместить его за routes.php
файла routes.php
. Этого больше не будет использовать, как только мы будем кэшировать маршруты. В настоящее время мы можем переместить его в app/Providers/AppServiceProver.php
например:
public function boot(){ /* * Set up locale and locale_prefix if other language is selected */ if (in_array(Request::segment(1), config('app.alt_langs'))) { App::setLocale(Request::segment(1)); config([ 'app.locale_prefix' => Request::segment(1) ]); } }
Не забывайте:
use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\App;
Настройка наших маршрутов:
В app/Http/routes.php
файле app/Http/routes.php
произойдут несколько изменений.
Во-первых, мы должны создать новый массив, содержащий все alt_langs
а также locale_prefix
по умолчанию, который, скорее всего, будет выглядеть следующим образом:
$all_langs = config('app.all_langs');
Чтобы иметь возможность кэшировать все различные префиксы lang с переведенными параметрами маршрута, нам необходимо зарегистрировать их все. Как мы можем сделать это?
*** Laravel aside 1: ***
Давайте рассмотрим определение Lang::get(..)
:
public static function get($key, $replace = array(), $locale = null, $fallback = true){ return \Illuminate\Translation\Translator::get($key, $replace, $locale, $fallback); }
Третий параметр этой функции – переменная $locale
! Отлично – мы можем, конечно, использовать это в наших интересах! Эта функция фактически позволяет нам выбрать, с какой локали мы хотим получить перевод!
Следующее, что мы собираемся сделать, это $all_langs
массив $all_langs
и создать новую группу Route
для каждого языкового префикса. Не только это, но мы также избавимся от тех цепей и patterns
которые нам раньше нужны, и только регистрируем маршруты с их надлежащими переводами (другие будут бросать 404
без необходимости их проверять):
/** * Iterate over each language prefix */ foreach( $all_langs as $prefix ){ if ($prefix == 'pl') $prefix = ''; /** * Register new route group with current prefix */ Route::group(['prefix' => $prefix], function() use ($prefix) { // Now we need to make sure the default prefix points to default lang folder. if ($prefix == '') $prefix = 'pl'; /** * The following line will register: * * example.com/ * example.com/en/ */ Route::get('/', 'MainController@getHome')->name('home'); /** * The following line will register: * * example.com/kontakt * example.com/en/contact */ Route::get(Lang::get('routes.contact',[], $prefix) , 'MainController@getContact')->name('contact'); /** * “In another moment down went Alice after it, never once * considering how in the world she was to get out again.” */ Route::group(['prefix' => 'admin', 'middleware' => 'admin'], function () use ($prefix){ /** * The following line will register: * * example.com/admin/uzivatelia * example.com/en/admin/users */ Route::get(Lang::get('routes.admin.users',[], $prefix), 'AdminController@getUsers') ->name('admin-users'); }); }); } /** * There might be routes that we want to exclude from our language setup. * For example these pesky ajax routes! Well let's just move them out of the `foreach` loop. * I will get back to this later. */ Route::group(['middleware' => 'ajax', 'prefix' => 'api'], function () { /** * This will only register example.com/api/login */ Route::post('login', 'AjaxController@login')->name('ajax-login'); });
Хьюстон у нас проблема!
Как вы можете видеть, я предпочитаю использовать именованные маршруты (вероятно, большинство людей):
Route::get('/', 'MainController@getHome')->name('home');
Их можно легко использовать внутри ваших шаблонов клинков:
{{route('home')}}
Но до сих пор есть проблема с моим решением: имена маршрутов переопределяют друг друга. Цикл foreach
выше регистрирует только последние префиксные маршруты с их именами.
Другими словами, только example.com/
будет привязан к home
маршруту, поскольку locale_perfix
был последним элементом массива $all_langs
.
Мы можем обойти это, предварительно указав названия маршрутов на $prefix
языка $prefix
. Например:
Route::get('/', 'MainController@getHome')->name($prefix.'_home');
Мы должны будем сделать это для каждого из маршрутов в нашем цикле. Это создает еще одно небольшое препятствие.
Но мой масштабный проект почти закончен!
Ну, как вы, наверное, догадались, теперь вам нужно вернуться ко всем вашим файлам и префикс каждого вызова функции вспомогательного вызова route
с текущим locale_prefix
загруженным из конфигурации app
.
Кроме тебя нет!
*** Laravel aside 2: ***
Давайте посмотрим, как Laravel реализует метод route
помощника.
if (! function_exists('route')) { /** * Generate a URL to a named route. * * @param string $name * @param array $parameters * @param bool $absolute * @return string */ function route($name, $parameters = [], $absolute = true) { return app('url')->route($name, $parameters, $absolute); } }
Как вы можете видеть, Laravel сначала проверит, существует ли функция route
. Он зарегистрирует свою функцию route
только в том случае, если другой еще не существует!
Это означает, что мы легко справляемся с нашей проблемой, не переписывая каждый route
сделанный до сих пор в наших шаблонах Blade
.
Давайте быстро app/helpers.php
файл app/helpers.php
.
Давайте helpers.php
что Laravel загружает файл, прежде чем он загрузит его helpers.php
, поставив следующую строку в bootstrap/autoload.php
//Put this line here require __DIR__ . '/../app/helpers.php'; //Right before this original line require __DIR__.'/../vendor/autoload.php';
Все, что нам нужно сделать, это сделать нашу собственную функцию route
в нашем файле app/helpers.php
. Мы будем использовать исходную реализацию в качестве основы:
<?php //Same parameters and a new $lang parameter function route($name, $parameters = [], $absolute = true, $lang = null) { /* * Remember the ajax routes we wanted to exclude from our lang system? * Check if the name provided to the function is the one you want to * exclude. If it is we will just use the original implementation. **/ if (str_contains($name, ['ajax', 'autocomplete'])){ return app('url')->route($name, $parameters, $absolute); } //Check if $lang is valid and make a route to chosen lang if ( $lang && in_array($lang, config('app.alt_langs')) ){ return app('url')->route($lang . '_' . $name, $parameters, $absolute); } /** * For all other routes get the current locale_prefix and prefix the name. */ $locale_prefix = config('app.locale_prefix'); if ($locale_prefix == '') $locale_prefix = 'pl'; return app('url')->route($locale_prefix . '_' . $name, $parameters, $absolute); }
Это оно!
Итак, что мы сделали, по существу, зарегистрированы все доступные префиксные группы. Создан каждый переводимый маршрут и с его именем также префикс. И затем вроде бы переопределить функцию route
Laravel, чтобы префикс всех имен маршрутов (кроме некоторых) с текущим locale_prefix
чтобы соответствующие URL-адреса были созданы в наших шаблонах blade config('app.locale_prefix')
без необходимости вводить config('app.locale_prefix')
каждый раз.
О, да:
php artisan route:cache
Маршруты кэширования должны выполняться только после развертывания вашего проекта, так как вы, вероятно, столкнетесь с ними во время devlopement. Но вы всегда можете очистить кеш:
php artisan route:clear
Еще раз спасибо Marcin Nabiałek за его оригинальный ответ. Это было очень полезно для меня.