如何在Laravel中创build多语种翻译路线
我想根据select的语言创build具有很多翻译路线的应用程序。 我曾经用三种方法在多语种网站上创buildurl来描述它。
在这种情况下,它应该是提到的话题中的第一种方法 :
- 我有一个默认的语言
- 我可以有许多其他的语言
- 目前的语言应该只计算URL(没有cookies/会话),以使它也非常友好的search引擎
- 对于默认语言,URL中不应该有前缀,其他语言应该是域之后的语言前缀
- url的每个部分都应该根据当前的语言进行翻译。
假设我已经设置了默认语言pl
和其他两种语言en
和fr
。 我只有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_langs
configuration中,您可以设置替代语言(在您的情况下是en
和fr
) – 它们应该与第一步创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中有about
和contact
翻译),所以在这里设置它们与当前语言的routes.php
文件中定义的相同。
最后你创build的路由组的前缀与你的语言相同(对于默认语言,它将是空的),在组内你只需创buildpath,但是那些about
和contact
你的参数作为variables
处理,所以你使用{about}
和{contact}
语法。
您需要记住,在这种情况下,将检查所有路线中的{contact}
是否与当前语言的第一步中定义的相同。 如果你不想要这个效果,并且想要为每个路由使用where来手动设置路由,还有另一个app\routes.php
文件,没有循环,你可以为每个路由分别设置contact
和about
:
<?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_prefix
variables中的任何前缀时导致404
错误。
我们保留什么?
基础似乎非常稳固,我们可以保留大部分!
我们当然可以保留各种本地化的路线文件:
<?php // app/lang/pl/routes.php return array( 'contact' => 'kontakt', 'about' => 'o-nas' );
我们也可以保留所有的app/config/app.php
variables:
/** * 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); }
该函数的第三个参数是$locale
variables! 太好了 – 我们当然可以利用这个优势! 这个函数实际上让我们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');
我们将不得不为循环中的每条路线执行此操作。 这又造成了另一个小障碍。
但是我的大规模项目快完成了!
那么你可能已经猜到了,现在你必须回到所有的文件中,并且使用从app
configurationlocale_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将首先检查route
function是否已经存在。 只有当另一个不存在的时候才会注册route
function!
这意味着我们可以非常轻松地解决我们的问题,而无需重新编写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自己的route
function。 我们将以原始实施为基础:
<?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的原始答复。 这对我真的很有帮助。