如何在Laravel中创build多语种翻译路线

我想根据select的语言创build具有很多翻译路线的应用程序。 我曾经用三种方法在多语种网站上创buildurl来描述它。

在这种情况下,它应该是提到的话题中的第一种方法

  1. 我有一个默认的语言
  2. 我可以有许多其他的语言
  3. 目前的语言应该只计算URL(没有cookies/会话),以使它也非常友好的search引擎
  4. 对于默认语言,URL中不应该有前缀,其他语言应该是域之后的语言前缀
  5. url的每个部分都应该根据当前的语言进行翻译。

假设我已经设置了默认语言pl和其他两种语言enfr 。 我只有3页 – 主页,联系页面和关于页面。

网站的url应该这样看:

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

[about][contact]则应根据所选的语言进行翻译,例如英文应该保持contact但对于波兰语则应该是kontakt等等。

怎样才能做到尽可能简单?

第一步:

进入app/lang目录,在这里为每种语言的路线创build翻译。 您需要创build3个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文件。

你应该find一行:

 '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_langsconfiguration中,您可以设置替代语言(在您的情况下是enfr ) – 它们应该与第一步创build的文件名称相同。

locale_prefix是您的语言环境的前缀。 你不需要默认语言环境的前缀,所以它被设置为空string。 如果select了默认以外的其他语言,该configuration将在运行时修改。

第三步

去你的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的第一部分是否匹配你的语言的名字 – 如果是,你改变语言环境和当前的语言前缀。

然后在一个小小的循环中,为所有路由名称设置要求(你提到你需要在URL中有aboutcontact翻译),所以在这里设置它们与当前语言的routes.php文件中定义的相同。

最后你创build的路由组的前缀与你的语言相同(对于默认语言,它将是空的),在组内你只需创buildpath,但是那些aboutcontact你的参数作为variables处理,所以你使用{about}{contact}语法。

您需要记住,在这种情况下,将检查所有路线中的{contact}是否与当前语言的第一步中定义的相同。 如果你不想要这个效果,并且想要为每个路由使用where来手动设置路由,还有另一个app\routes.php文件,没有循环,你可以为每个路由分别设置contactabout

 <?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是不正确的路线,我认为是最好的解决scheme,使redirect。 但是你应该redirect不是/因为它是默认语言,而是/en

所以现在你可以打开app/start/global.php文件,并在这里创build301redirect为未知的URL:

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

MarcinNabiałek在他最初的答案中提供给我们的是路线本地化问题的可靠解决scheme。

小恶霸:

他的解决scheme唯一的缺点是我们不能使用caching的路由,根据Laravel's 文档 ,这有时会带来很大的好处:

如果您的应用程序仅使用基于控制器的路由,则应该利用Laravel的路由caching。 使用路由caching将大大减less注册所有应用程序路由所需的时间。 在某些情况下,您的路线注册甚至可能高达100倍。 要生成路由caching,只需执行route:cache Artisan命令。


为什么我们不能caching我们的路线?

由于MarcinNabiałek的方法dynamic生成基于locale_prefix的新路由,caching时会在caching时访问未存储在locale_prefixvariables中的任何前缀时导致404错误。


我们保留什么?

基础似乎非常稳固,我们可以保留大部分!

我们当然可以保留各种本地化的路线文件:

 <?php // app/lang/pl/routes.php return array( 'contact' => 'kontakt', 'about' => 'o-nas' ); 

我们也可以保留所有的app/config/app.phpvariables:

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

我们还需要一些检查路段的代码。 但是由于这一点是利用caching,我们需要将其移动到routes.php文件之外。 一旦我们caching路线,这个将不会被使用。 我们暂时可以把它移到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文件中会发生一些变化。

首先,我们必须使一个新的数组包含所有的alt_langs以及默认的locale_prefix ,这很可能是''

 $all_langs = config('app.all_langs'); 

为了能够caching所有具有翻译的路由参数的各种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); } 

该函数的第三个参数是$localevariables! 太好了 – 我们当然可以利用这个优势! 这个函数实际上让我们select我们想从哪个语言环境获得翻译!

接下来我们要做的是遍历$all_langs数组,并为每个语言前缀创build一个新的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')}} 

但到目前为止,我的解决scheme存在一个问题:路由名称相互覆盖。 上面的foreach循环只会注册最后的前缀路由和他们的名字。

换句话说,只有example.com/将被绑定到home路由,因为locale_perfix$all_langs数组中的最后一项。

我们可以通过在语言$prefix加上路由名称来解决这个问题。 例如:

 Route::get('/', 'MainController@getHome')->name($prefix.'_home'); 

我们将不得不为循环中的每条路线执行此操作。 这又造成了另一个小障碍。


但是我的大规模项目快完成了!

那么你可能已经猜到了,现在你必须回到所有的文件中,并且使用从appconfigurationlocale_prefix加载的当前locale_prefix前缀每个route帮助函数。

除了你没有!

*** 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将首先检查routefunction是否已经存在。 只有当另一个不存在的时候才会注册routefunction!

这意味着我们可以非常轻松地解决我们的问题,而无需重新编写Blade模板中到目前为止所做的每一个route调用。

让我们快速创build一个app/helpers.php文件。

让我们确保Laravel在加载helpers.php之前加载文件,方法是helpers.php放在bootstrap/autoload.php

 //Put this line here require __DIR__ . '/../app/helpers.php'; //Right before this original line require __DIR__.'/../vendor/autoload.php'; 

我们现在所要做的就是在我们的app/helpers.php文件中创build自己的routefunction。 我们将以原始实施为基础:

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

而已!

所以我们所做的基本上是注册所有可用的前缀组。 创build了每个翻译过的路线,并将其名称作为前缀。 然后重写Laravel route函数,将所有路由名称(除了某些之外)加上当前的locale_prefix以便在我们的刀片模板中创build适当的URL,而不必每次都键入config('app.locale_prefix')

哦耶:

 php artisan route:cache 

caching路线应该只有在你部署你的项目时才能真正完成,因为你可能会在错误期间混淆它们。 但是您可以随时清除caching:

 php artisan route:clear 

再次感谢MarcinNabiałek的原始答复。 这对我真的很有帮助。