Marcin Nabiałek 在他最初的回答中为我们提供的是路线定位问题的可靠解决方案。
小熊虫:
他的解决方案唯一真正的缺点是我们不能使用缓存路由,根据Laravel'sdocs,这有时会带来很大的好处:
如果您的应用程序专门使用基于控制器的路由,您
应该利用 Laravel 的路由缓存。使用路由缓存
将大大减少注册所有人所需的时间
您的应用程序的路线。在某些情况下,您的路线注册
甚至可能快 100 倍。要生成路由缓存,只需执行
route:cache Artisan 命令。
为什么我们不能缓存我们的路线?
由于Marcin Nabiałek's 方法会根据locale_prefix 动态生成新路由,因此在访问缓存时未存储在locale_prefix 变量中的任何前缀时,缓存它们会导致404 错误。
我们保留什么?
基础似乎很稳固,我们可以保留大部分!
我们当然可以保留各种本地化特定的路由文件:
<?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 文件。一旦我们缓存了路由,就不会再使用那个了。我们可以暂时将其移至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');
为了能够缓存所有带有翻译路由参数的各种语言前缀,我们需要将它们全部注册。我们该怎么做?
*** 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 数组并为每个语言前缀创建一个新的Route 组。不仅如此,我们还将摆脱我们之前需要的 where 链和 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 来解决这个问题。例如:
Route::get('/', 'MainController@getHome')->name($prefix.'_home');
我们必须为循环中的每条路线执行此操作。这又造成了一个小障碍。
但是我的大型项目快完成了!
您可能已经猜到了,您现在必须返回所有文件,并在每个 route 辅助函数调用前加上从 app 配置加载的当前 locale_prefix。
除非你不这样做!
*** 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 函数!
这意味着我们可以非常轻松地解决我们的问题,而无需重写迄今为止在我们的 Blade 模板中进行的每一个 route 调用。
让我们快速创建一个app/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';
LARAVEL 7+ 更新
bootstrap/autoload.php 文件已不存在,您必须将上面的代码添加到public/index.php 文件中。
我们现在要做的就是在我们的app/helpers.php 文件中创建我们自己的route 函数。我们将以原始实现为基础:
<?php
//Same parameters and a new $lang parameter
use Illuminate\Support\Str;
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);
}
就是这样!
所以我们所做的基本上是注册所有可用的前缀组。创建翻译的每条路线,并为其名称加上前缀。然后某种覆盖了 Laravel 的 route 函数,以当前的 locale_prefix 为所有路由名称(除了一些)添加前缀,以便在我们的刀片模板中创建适当的 url,而无需输入 @987654382 @每一次。
哦,是的:
php artisan route:cache
只有在部署项目后才能真正完成缓存路由,因为在开发过程中很可能会弄乱它们。但是你可以随时清除缓存:
php artisan route:clear
再次感谢 Marcin Nabiałek 的原始回答。这对我真的很有帮助。