如何為 Eloquent 新增多語言支援

JokerLinly發表於2017-03-20

file
Eloquent ORM 是 Laravel 非常強大的一個部分。麻煩地是,它不提供開箱即用的支援多語言的模型。不過,自己來新增這個功能也不是有多困難。

我們先隨便來找個例子看看一個沒有多語言支援的遷移:

Schema::create('articles', function (Blueprint $table) {
   $table->increments('id');
   $table->string('name');
   $table->text('text');
   $table->timestamps();
});

通過為特定語言新增列,我們可以輕鬆地建一張包含多種語言的表,如下:

Schema::create('articles', function (Blueprint $table) {
   $table->increments('id');
   $table->string('name_en');
   $table->text('text_en');
   $table->string('name_fr');
   $table->text('text_fr');
   $table->boolean('online');
   $table->timestamps();
});

現在,此表支援以英語和法語儲存文字。顯然,這種方法有個缺點:每當你想新增一個新的語言支援,你就不得不新增新的欄位到支援多語言的每個表裡面。當想要支援的語言開始變多,或者使用多語言支援的表多到某種數量,這種做法明顯行不通。

而人們暫時能想到的最好的方法是為必須翻譯的欄位建立一個新的表。

Schema::create('articles', function (Blueprint $table) {
    $table->increments('id');
    $table->boolean('online');
    $table->timestamps();
});

Schema::create('article_translations', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('article_id')->unsigned();
    $table->string('locale')->index();

    $table->string('name');
    $table->text('text');

    $table->unique(['article_id','locale']);
    $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
});

當應用程式需要支援額外的語言時,巧妙地使用上面的遷移方法,就不再需要建立額外的欄位。

上面的欄位 locale 用於確定儲存記錄的語言。在本文的下一個示例中,我們將在此欄位中儲存一個 iso-code

新增關聯刪除的外來鍵確保當 articles 表中的記錄被刪除時,它相應的翻譯也將被刪除。

到這裡我們就已經為資料庫準備好可翻譯的內容了,接下來和我一起逐步思考該如何使用它。

$frenchText = $article->getTranslation('fr')->text;

以上無需解釋一目瞭然的使用方法是非常讚的。還有更好的一個點,就是如果先設定當前應用程式的語言環境為法語,article 模型將返回法語的翻譯內容:

app()->setLocale('fr');
$frenchText = $article->text;

這樣的做法讓翻譯資料的工作變得更加容易。當然檢視也可以有這樣方便的做法:

The article with title {{ $article->title }} is {{ $this->online ? 'online' : 'offline' }}.

你看,使用翻譯欄位和非翻譯欄位沒有任何區別。真的是超讚!

推薦一個我經常使用的 laravel-translatableDimitris Savvopoulos 寫的一個輪子,可以執行所有這些操作。

根據 指引安裝後,更新你的模型以使用 Translatable-trait。該模型還應該有一個 $translatableAttributes 屬性的陣列,其中欄位 name 可以被翻譯:

// app/Article.php
class Article extends Model
{
    use \Dimsav\Translatable\Translatable;

    public $translatedAttributes = ['name', 'text'];

    ...
}

// app/ArticleTranslation.php
class ArticleTranslation extends Model
{
    public $timestamps = false;

    ...
}

現在所有的設定完成了,我們可以儲存一些新的可翻譯文章。寫個簡單的路由例子:

Route::get('create', function($locale) {
    $article = new Article();
    $article->online = true;
    $article->save();

    foreach (['en', 'nl', 'fr', 'de'] as $locale) {
        $article->translateOrNew($locale)->name = "Title {$locale}";
        $article->translateOrNew($locale)->text = "Text {$locale}";
    }

    $article->save();

    echo 'Created an article with some translations!';
});

在瀏覽到 /create 檢視資料庫時,會看到 articles 表包含的記錄。 article_translations 表包含三個記錄:一個用於上面示例中的每個語言環境。

現在我們來檢索翻譯。新增這個路由:

Route::get('{locale}', function($locale) {
   app()->setLocale($locale);

   $article = Article::first();

   return view('article')->with(compact('article'));
});

再新增一個 article.blade.php 的檢視和以下的內容:

<h1>{{ $article->name }}</h1>
{{ $article->text }}

當你瀏覽到 /en,你會看到文章的英文翻譯。瀏覽 /nl/fr/de 以檢視這些區域設定的本地化文章。

恭喜!看到這裡我想你已經學會了如何為 Eloquent 模型新增多語言支援。GitHub 上面有本文的 示例程式碼。但我更建議你去閱讀 Laravel-Translatable 的 readme。如果你想了解得多一些,就去他的 GitHub 上檢視原始碼。這不會很難,你會從中學到很多。

以上內容翻譯改編自 Freek Van der Herten 的 How To Add Multilingual Support to Eloquent

Stay Hungry, Stay Foolish.

相關文章