如何在拉拉维尔创建多语言翻译路线

2022-08-30 10:10:37

我想根据所选语言创建具有许多翻译路线的应用程序。我曾经在多语言网站中创建URL的3种方法中描述了它。

在这种情况下,它应该是上述主题中的第一种方法,因此:

  1. 我有一种默认语言
  2. 我可以有许多其他语言
  3. 当前语言应仅按URL计算(没有cookie/会话),以使其对搜索引擎也非常友好
  4. 对于默认语言,URL中不应有前缀,对于其他语言,应在域后为语言前缀
  5. URL的每个部分都应根据当前语言进行翻译。

假设我已经设置了默认语言和2种其他语言和.我只有3个页面 - 主页,联系页面和关于页面。plenfr

网站的网址应该这样看:

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

而 应该根据选定的语言进行翻译,例如在英语中,它应该被保留,但对于波兰语,它应该是等等。[about][contact]contactkontakt

如何做到尽可能简单?


答案 1

第一步:

转到目录并在此处为每种语言的路线创建翻译。您需要创建 3 个文件 - 每个文件位于单独的语言目录中 (pl/en/fr),因为您希望使用 3 种语言app/langroutes.php

对于波兰语:

<?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_langsenfr

并且是区域设置的前缀。你不希望默认区域设置前缀,因此将其设置为空字符串。如果选择了默认语言以外的其他语言,则将在运行时修改此配置。locale_prefix

第三步

转到您的文件并输入其内容(这是文件的全部内容):app/routes.phpapp/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中进行翻译),因此在这里您将它们设置为与当前语言的文件中定义的相同。aboutcontactroutes.php

最后,您创建与您的语言具有相同前缀的路由组(对于默认语言,它将为空),并且在组内,您只需创建路径,但这些参数,您将其视为使用它们和语法。aboutcontactvariables{about}{contact}

您需要记住,在这种情况下,如果所有路由与您在第一步中为当前语言定义的路线相同,都将进行检查。如果您不希望出现这种效果,并希望使用 where 为每个路由手动设置路由,则可以使用不带循环的替代文件,您可以在其中设置并为每个路由单独设置:{contact}app\routes.phpcontactabout

<?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/somethingsomething//en

所以现在你可以打开文件,并在这里为未知的网址创建301重定向:app/start/global.php

// app/start/global.php

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

答案 2

Marcin Nabiałek在他最初的回答中为我们提供了一个坚实的解决方案,以解决路线本地化问题。

小虫熊:

他的解决方案唯一真正的缺点是我们不能使用缓存的路由,这有时可能会非常有益于文档Laravel's

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


为什么我们不能缓存我们的路由?

由于 Marcin Nabiałek 的方法基于动态生成新路由,因此缓存它们将导致在缓存时访问变量中未存储的任何前缀时出错。locale_prefix404locale_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.phpapp/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_langslocale_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_langsRoutewherepatterns404

/**
* 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/homelocale_perfix$all_langs

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

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

我们必须为循环中的每个路由执行此操作。这又造成了另一个小障碍。


但是我的大型项目几乎完成了!

正如您可能猜到的那样,您现在必须返回所有文件,并在每个帮助程序函数调用之前使用从配置加载的当前值作为前缀。routelocale_prefixapp

除非你没有!

*** 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将首先检查函数是否已存在。仅当另一个函数尚不存在时,它才会注册其函数!routeroute

这意味着我们可以非常轻松地解决问题,而不必重写到目前为止在模板中所做的每个调用。routeBlade

让我们快速制作一个文件。app/helpers.php

让我们确保Laravel在加载文件之前加载文件,方法是将以下行放入helpers.phpbootstrap/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.phppublic/index.php

我们现在要做的就是在文件中创建自己的函数。我们将使用原始实现作为基础:routeapp/helpers.php

<?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函数,以在所有路由名称(某些路由名称除外)前面加上当前值,以便在我们的边栏选项卡模板中创建适当的URL,而无需每次都键入。routelocale_prefixconfig('app.locale_prefix')

哦,是的:

php artisan route:cache

缓存路由应该只在部署项目后真正完成,因为在开发过程中可能会弄乱它们。但您始终可以清除缓存:

php artisan route:clear

再次感谢Marcin Nabiałek的原始答案。这对我真的很有帮助。


推荐