web-dev-qa-db-ja.com

Laravelで多言語翻訳ルートを作成する方法

選択した言語に応じて多くの翻訳されたルートを持つアプリケーションを作成したいと思います。 多言語WebサイトでURLを作成する3つの方法 で説明しました。

この場合、前述のトピックの最初のメソッドでなければなりません

  1. デフォルトの言語が1つあります
  2. 私は他の多くの言語を持つことができます
  3. 検索エンジンにとっても使いやすいように、現在の言語はURL(Cookie /セッションなし)のみで計算する必要があります
  4. デフォルト言語の場合、URLにはプレフィックスを付けないでください。他の言語の場合は、ドメインの後に言語プレフィックスを付けます。
  5. Urlの各部分は、現在の言語に従って翻訳する必要があります。

デフォルトの言語plと2つの他の言語enfrを設定したと仮定しましょう。メインページ、連絡先ページ、アバウトページの3ページしかありません。

サイトのURLは次のようになります。

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

一方、[about]および[contact]は、選択した言語に従って翻訳する必要があります。たとえば、英語ではcontactのままにしますが、ポーランド語ではkontaktなどにします。

どうすればできるだけ簡単にできますか?

43

最初のステップ:

app/langディレクトリに移動し、ここで各言語のルートの翻訳を作成します。 3つのroutes.phpファイルを作成する必要があります-それぞれ3つの言語を使用するため、それぞれ別個の言語ディレクトリ(pl/en/fr)にあります

ポーランド語の場合:

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

2番目のステップ:

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 configでは、代替言語(あなたの場合は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の最初のセグメントが言語の名前と一致するかどうかを確認します。一致する場合は、ロケールと現在の言語プレフィックスを変更します。

次に、小さなループで、すべてのルート名の要件を設定します(URLでaboutcontactを翻訳する必要があると述べました)。現在の言語用。

最後に、言語と同じプレフィックスを持つルートグループを作成し(既定の言語では空になります)、グループ内で単にパスを作成しますが、これらのパラメーターはaboutおよびcontactを扱いますvariablesとして、routes.phpおよび{about}構文を使用します。

その場合、現在の言語の最初のステップで定義したものと同じであれば、すべてのルートの{contact}がチェックされることを覚えておく必要があります。この効果が望ましくなく、whereを使用して各ルートに手動でルートを設定する場合、ループごとにcontactaboutを個別に設定するループなしの代替{contact}ファイルがあります。

app\routes.php

4番目のステップ:

あなたはそれについて言及していませんが、あなたが考慮することができる1つの余分なものがあります。誰かがsomethingが正しいルートでないURL <?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')); }); を使用する場合、リダイレクトを行うための最良の解決策だと思います。ただし、/en/somethingはデフォルト言語であるため、/ではなく、/enにリダイレクトする必要があります。

したがって、app/start/global.phpファイルを開いて、不明なURLの301リダイレクトをここで作成できます。

// app/start/global.php

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

MarcinNabiałek が最初の回答で提供してくれたのは、ルートのローカリゼーションの問題に対する確実な解決策です。

マイナーバグベア:

彼のソリューションの唯一の本当の欠点は、キャッシュされたルートを使用できないことです。これは、_Laravel's_ docs

アプリケーションがコントローラーベースのルートのみを使用している場合は、Laravelのルートキャッシュを利用する必要があります。ルートキャッシュを使用すると、アプリケーションのすべてのルートを登録するのにかかる時間が大幅に短縮されます。場合によっては、ルート登録の速度が最大100倍になることもあります。ルートキャッシュを生成するには、_route:cache_ Artisanコマンドを実行するだけです。


ルートをキャッシュできないのはなぜですか?

MarcinNabiałek's メソッドは_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_ファイルの外に移動する必要があります。ルートをキャッシュすると、その1つは使用されなくなります。とりあえず、_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プレフィックスをすべてキャッシュできるようにするには、それらをすべて登録する必要があります。どうすればそれができますか?

_public static function get($key, $replace = array(), $locale = null, $fallback = true){ return \Illuminate\Translation\Translator::get($key, $replace, $locale, $fallback); } _

Lang::get(..)の定義を見てみましょう:

_$locale_

その関数の3番目のパラメーターは_$all_langs_変数です!素晴らしい-確かにそれを有利に使うことができます!この関数では、実際に翻訳を取得するロケールを選択できます!

次に行うことは、_404_配列を反復処理し、各言語プレフィックスに対して新しいRouteグループを作成することです。それだけでなく、以前必要だったwhereチェーンとpatternsも削除し、適切な翻訳でルートを登録するだけです(他の人は__(SOMECODE)をスローします) __もう確認する必要はありません):

_/**
* 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/_が_locale_perfix_配列の最後の項目であったため、_$all_langs_のみがhomeルートにバインドされます。

ルート名の前に言語_$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関数を登録します!

つまり、routeテンプレートでこれまでに行われたBlade呼び出しをすべて書き換える必要なく、問題を非常に簡単に回避できます。

_app/helpers.php_ファイルを実際にすばやく作成しましょう。

_helpers.php_に次の行を入力して、_bootstrap/autoload.php_をロードする前にLaravelがファイルをロードすることを確認しましょう

_//Put this line here
require __DIR__ . '/../app/helpers.php';
//Right before this original line
require __DIR__.'/../vendor/autoload.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);
}
_

それだけです!

したがって、基本的に行ったことは、使用可能なすべてのプレフィックスグループを登録することです。翻訳された各ルートを作成し、その名前にも接頭辞を付けました。そして、sort ofLaravel route関数をオーバーライドして、すべてのルート名(一部を除く)に現在の_locale_prefix_これにより、毎回config('app.locale_prefix')と入力することなく、ブレードテンプレートに適切なURLが作成されます。

そうそう:

_php artisan route:cache
_

ルートをキャッシュするのは、開発中にプロジェクトを混乱させる可能性が高いため、プロジェクトを展開した後にのみ行う必要があります。ただし、キャッシュはいつでもクリアできます。

_php artisan route:clear
_

MarcinNabiałek の元の回答に感謝します。本当に助かりました。

22
PeterTheLobster

同じ結果をよりシンプルなアプローチで適用できます。完璧ではありませんが、迅速で簡単なソリューションを提供します。ただし、そのシナリオでは、各ルートを作成する必要があるため、大規模なWebサイトに対してはそうしない可能性があります。

Route::get('/contact-us', function () {
    return view('contactus');
})->name('rte_contact'); // DEFAULT

Route::get('/contactez-nous', function () {
    return view('contactus');
})->name('rte_contact_fr');

ローカライズファイルでルート名を次のように定義するだけです。

# app/resources/lang/en.json
{ "rte_contact": "rte_contact" } //DEFAULT

// app/resources/lang/fr.json
{ "rte_contact": "rte_contact_fr" }

その後、次のような生成されたロケール変数を使用して、ブレードテンプレートでそれらを使用できます。

<a class="nav-link" href="{{ route(__('rte_contact')) }}"> {{ __('nav_contact') }}</a>
1
JeanMGirard