如何为 Eloquent 添加多语言支持

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

本作品采用《CC 协议》,转载必须注明作者和本文链接
Stay Hungry, Stay Foolish.
本帖由系统于 6年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 13

正好要用到语言 ,谢谢

7年前 评论

@JokerLinly

准备写一个CMS做企业站用,会用到自定义模型字段。

那么比如一些较长的text字段(内容),是否有必要分主附表呢?

6年前 评论

@IceBay 区分应用场景来判断嘛

6年前 评论

@IceBay 跟长短没关系,关键是要不要做成多语言的

6年前 评论

@JokerLinly 要的。全站多语言(大佬说至少有4个语言:broken_heart:)。

6年前 评论

感谢分享~

6年前 评论

请问如何设置才能路由不带iso-code的时候显示中文,切换en显示英文,现在我的只能是用url切换,不能默认显示一个语言。

4年前 评论

这个在6.0上好像不能用吧, laravel-translatable这个包不支持

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!