dimsav/laravel-translatable

该包已被弃用且不再维护。作者建议使用 astrotomic/laravel-translatable 包。

Laravel 多语言模型包

v10.0.0 2019-06-03 06:56 UTC

README

此包已被弃用。但不用担心。您可以使用 Astrotomic/laravel-translatable.

Laravel-Translatable

Total Downloads Build Status Code Coverage Latest Stable Version License SensioLabsInsight StyleCI

Laravel Translatable

如果您想要将模型翻译存储到数据库中,这个包就是为您准备的。

这是一个用于翻译模型的 Laravel 包。它的目标是减少获取和存储多语言模型实例的复杂性。使用此包,您将编写更少的代码,因为翻译将在获取/保存实例时获取/保存。

文档

演示

获取翻译属性

  $greece = Country::where('code', 'gr')->first();
  echo $greece->translate('en')->name; // Greece
  
  App::setLocale('en');
  echo $greece->name;     // Greece

  App::setLocale('de');
  echo $greece->name;     // Griechenland

保存翻译属性

  $greece = Country::where('code', 'gr')->first();
  echo $greece->translate('en')->name; // Greece
  
  $greece->translate('en')->name = 'abc';
  $greece->save();
  
  $greece = Country::where('code', 'gr')->first();
  echo $greece->translate('en')->name; // abc

填充多个翻译

  $data = [
    'code' => 'gr',
    'en'  => ['name' => 'Greece'],
    'fr'  => ['name' => 'Grèce'],
  ];

  $greece = Country::create($data);
  
  echo $greece->translate('fr')->name; // Grèce

Laravel 兼容性

Laravel 可翻译
5.8 9.*
5.7 9.*
5.6 9.*
5.5 8.*
5.4 7.*
5.3 6.*
5.2 5.5 - 6.*
5.1 5.0 - 6.*
5.0 5.0 - 5.4
4.2.x 4.4.x
4.1.x 4.4.x
4.0.x 4.3.x

教程

4 步安装

步骤 1:安装包

通过执行命令将包添加到您的 composer.json 中。

composer require dimsav/laravel-translatable

接下来,将服务提供者添加到 app/config/app.php

Dimsav\Translatable\TranslatableServiceProvider::class,

步骤 2:迁移

在此示例中,我们想要翻译 Country 模型。我们需要一个额外的表 country_translations

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

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

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

步骤 3:模型

  1. 可翻译模型 Country 应该使用 特质 Dimsav\Translatable\Translatable
  2. 翻译模型的约定是 CountryTranslation
// models/Country.php
class Country extends Eloquent {
    
    use \Dimsav\Translatable\Translatable;
    
    public $translatedAttributes = ['name'];
    protected $fillable = ['code'];
    
    /**
     * The relations to eager load on every query.
     *
     * @var array
     */
    // (optionaly)
    // protected $with = ['translations'];

}

// models/CountryTranslation.php
class CountryTranslation extends Eloquent {

    public $timestamps = false;
    protected $fillable = ['name'];

}

数组 $translatedAttributes 包含了在 "Translation" 模型中翻译的字段名称。

步骤 4:配置

我们将配置文件复制到我们的项目中。

Laravel 5.*

php artisan vendor:publish --tag=translatable 

Laravel 4.*

php artisan config:publish dimsav/laravel-translatable

注意:对本地化格式的格式没有限制。请随意使用您认为更好的格式,例如使用“eng”而不是“en”,或使用“el”而不是“gr”。重要的是要定义您的本地化并坚持使用。

配置

配置文件

您可以在配置文件中查看进一步定制的选项。

翻译模型

定义翻译模型类时使用的约定是附加关键字Translation

因此,如果您的模型是\MyApp\Models\Country,则默认翻译为\MyApp\Models\CountryTranslation

要使用自定义类作为翻译模型,请将包含命名空间的自定义类(作为参数)定义为翻译类。例如

<?php 

namespace MyApp\Models;

use Dimsav\Translatable\Translatable;
use Illuminate\Database\Eloquent\Model as Eloquent;

class Country extends Eloquent
{
    use Translatable;

    public $translationModel = 'MyApp\Models\CountryAwesomeTranslation';
}

功能列表

请先阅读安装步骤,了解需要创建哪些类。

可用方法

// Before we get started, this is how we determine the default locale.
// It is set by laravel or other packages.
App::getLocale(); // 'fr' 

// To use this package, first we need an instance of our model
$germany = Country::where('code', 'de')->first();

// This returns an instance of CountryTranslation of using the default locale.
// So in this case, french. If no french translation is found, it returns null.
$translation = $germany->translate();

// It is possible to define a default locale per model by overriding the model constructor.
public function __construct(array $attributes = [])
{
    parent::__construct($attributes);
    
    $this->defaultLocale = 'de';
}

// It is also possible to define a default locale for our model on the fly:
$germany->setDefaultLocale('de');

// If an german translation exists, it returns an instance of 
// CountryTranslation. Otherwise it returns null.
$translation = $germany->translate('de');

// If a german translation doesn't exist, it attempts to get a translation  
// of the fallback language (see fallback locale section below).
$translation = $germany->translate('de', true);

// Alias of the above.
$translation = $germany->translateOrDefault('de');

// Returns instance of CountryTranslation of using the default locale.
// If no translation is found, it returns a fallback translation
// if enabled in the configuration.
$translation = $germany->getTranslation();

// If an german translation exists, it returns an instance of 
// CountryTranslation. Otherwise it returns null.
// Same as $germany->translate('de');
$translation = $germany->getTranslation('de', true);

// To set the translation for a field you can either update the translation model.
// Saving the model will also save all the related translations.
$germany->translate('en')->name = 'Germany';
$germany->save();

// Alternatively we can use the shortcut
$germany->{'name:en'} = 'Germany';
$germany->save();

// There are two ways of inserting mutliple translations into the database
// First, using the locale as array key.
$greece = $country->fill([
    'en'  => ['name' => 'Greece'],
    'fr'  => ['name' => 'Grèce'],
]);

// The second way is to use the following syntax.  
$greece = $country->fill([
    'name:en' => 'Greece',
    'name:fr' => 'Grèce',
]);

// Returns true/false if the model has translation about the current locale. 
$germany->hasTranslation();

// Returns true/false if the model has translation in french. 
$germany->hasTranslation('fr');

// If a german translation doesn't exist, it returns
// a new instance of CountryTranslation.
$translation = $germany->translateOrNew('de');

// Returns a new CountryTranslation instance for the selected
// language, and binds it to $germany
$translation = $germany->getNewTranslation('it');

// The eloquent model relationship. Do what you want with it ;) 
$germany->translations();

// Remove all translations linked to an object
$germany->deleteTranslations();

// Delete one or multiple translations
$germany->deleteTranslations('de');
$germany->deleteTranslations(['de', 'en']);

// Gel all the translations as array
$germany->getTranslationsArray();
// Returns
[
 'en' => ['name' => 'Germany'],
 'de' => ['name' => 'Deutschland'],
 'fr' => ['name' => 'Allemagne'],
];

// Creates a clone and clones the translations
$replicate = $germany->replicateWithTranslations(); 

可用作用域

// Returns all countries having translations in english
Country::translatedIn('en')->get();

// Returns all countries not being translated in english
Country::notTranslatedIn('en')->get();

// Returns all countries having translations
Country::translated()->get();

// Eager loads translation relationship only for the default
// and fallback (if enabled) locale
Country::withTranslation()->get();

// Returns an array containing pairs of country ids and the translated
// name attribute. For example: 
// [
//     ['id' => 1, 'name' => 'Greece'], 
//     ['id' => 2, 'name' => 'Belgium']
// ]
Country::listsTranslations('name')->get()->toArray();

// Filters countries by checking the translation against the given value 
Country::whereTranslation('name', 'Greece')->first();

// Filters countries by checking the translation against the given value, only in the specified locale
Country::whereTranslation('name', 'Greece', 'en')->first();

// Or where translation
Country::whereTranslation('name', 'Greece')->orWhereTranslation('name', 'France')->get();

// Filters countries by checking the translation against the given string with wildcards
Country::whereTranslationLike('name', '%Gree%')->first();

// Or where translation like
Country::whereTranslationLike('name', '%eece%')->orWhereTranslationLike('name', '%ance%')->get();

魔法属性

要使用魔法属性,您必须在主模型中定义属性$translatedAttributes

class Country extends Eloquent {

    use \Dimsav\Translatable\Translatable;

    public $translatedAttributes = ['name'];
}
// Again we start by having a country instance
$germany = Country::where('code', 'de')->first();

// We can reference properties of the translation object directly from our main model.
// This uses the default locale and is the equivalent of $germany->translate()->name
$germany->name; // 'Germany'

// We can also quick access a translation with a custom locale
$germany->{'name:de'} // 'Deutschland'

回退

回退本地化

如果您希望在找不到翻译时回退到默认翻译,请使用use_fallback键在配置中启用此功能。要选择默认本地化,请使用fallback_locale键。

配置示例

return [
    'use_fallback' => true,

    'fallback_locale' => 'en',    
];

您还可以通过设置$useTranslationFallback属性来为“是否使用回退”定义每个模型的自定义默认值。

class Country {

    public $useTranslationFallback = true;

}

按属性回退

尽管我们尽量使所有模型都得到很好的翻译,但某些字段可能会被留空。结果是什么?您会发现这些字段缺少翻译!

属性回退功能就是为了帮助这种情况。启用此功能后,可翻译将返回回退语言的空属性值。

此功能在新的安装中默认启用。如果您的配置文件在v7.1之前设置,请确保添加以下行以启用此功能

'use_property_fallback' => true,

当然,必须启用回退本地化才能使用此功能。

如果配置中启用了属性回退,则可翻译将返回回退本地化的翻译,用于翻译为空的字段。

自定义空翻译属性检测

此包旨在翻译字符串,但通常它也能翻译数字、布尔值或您想翻译的任何内容。默认情况下,使用简单的empty()调用检测翻译值是否为空。如果您想自定义此操作或为每个属性使用不同的逻辑,您可以在主模型中覆盖isEmptyTranslatableAttribute()

protected function isEmptyTranslatableAttribute(string $key, $value): bool
{
    switch($key) {
        case 'name':
            return empty($value);
        case 'price':
            return !is_number($value);
        default:
            return is_null($value);
    }
}

基于国家的回退

从版本v5.3开始,可以使用基于国家的本地化。例如,您可以使用以下本地化

  • 英语:en
  • 西班牙语:es
  • 墨西哥西班牙语:es-MX
  • 哥伦比亚西班牙语:es-CO

配置这些区域设置看起来像这样

    'locales' => [ 
        'en',
        'es' => [
            'MX',
            'CO',
        ],
    ];

我们还可以配置语言和国家之间的“粘合剂”。例如,如果我们更喜欢格式es_MX而不是es-MX,配置应如下所示

   'locale_separator' => '_',

对于使用en-MX格式的区域设置的回退是什么?

假设我们的回退区域设置为en。现在,当我们尝试从数据库中获取es-MX区域设置的翻译,但不存在时,我们不会得到en的翻译作为回退。可翻译将使用eses-MX的第一部分)作为回退,并且只有在没有找到任何内容时,才会返回en的翻译。

翻译自动加载

如果调用toArray()方法,则可以自动加载所有翻译。为了控制此功能,该包提供了一个配置值to_array_always_loads_translations和特例中的三个静态方法

  • enableAutoloadTranslations() - 强制加载所有翻译
  • disableAutoloadTranslations() - 禁用自动加载并返回父属性
  • defaultAutoloadTranslations() - 不更改默认行为逻辑(默认

附加组件

多亏了社区,一些包被编写出来,以便在处理表单时更容易使用可翻译

常见问题解答

我需要一些示例代码!

所有包功能的示例可以在用于测试代码中找到。

我需要帮助!

有任何问题或建议?请随意打开一个问题

我想帮助!

你很棒!关注仓库并回复问题。你将帮助为包的用户提供出色的体验。#communityWorks

此外,通过捐赠给我一杯啤酒。❤️

我遇到了与其他特例方法冲突的情况!

可翻译与所有类型的Eloquent扩展完全兼容,包括Ardent。如果您需要帮助将这些扩展与可翻译一起实现,请参阅此示例

我如何将现有的表迁移到使用laravel-translatable?

请参阅安装步骤以了解您的数据库应该如何构建。

如果您的属性是用英文编写的,我们建议在迁移中使用以下命令

// We insert the translation attributes into the fresh translated table: 
\DB::statement("insert into country_translations (country_id, name, locale) select id, name, 'en' from countries");

// We drop the translation attributes in our main table: 
Schema::table('countries', function ($table) {
    $table->dropColumn('name');
}); 

我如何根据翻译进行排序?

这里的一个提示是首先执行MySQL查询,然后执行Eloquent查询。

要获取按翻译字段排序的记录列表,您可以这样做

SELECT * from countries
JOIN country_translations as t on t.country_id = countries.id 
WHERE locale = 'en'
GROUP BY countries.id
ORDER BY t.name desc

相应的Eloquent查询将是

Country::join('country_translations as t', function ($join) {
        $join->on('countries.id', '=', 't.country_id')
            ->where('t.locale', '=', 'en');
    }) 
    ->groupBy('countries.id')
    ->orderBy('t.name', 'desc')
    ->with('translations')
    ->get();

如何通过翻译字段选择国家?

例如,假设我们想找到国家名称翻译为 '葡萄牙' 的国家。

Country::whereHas('translations', function ($query) {
    $query->where('locale', 'en')
    ->where('name', 'Portugal');
})->first();

更多信息请参考 Laravel 的查询关系文档

为什么在运行迁移时得到 mysql 错误?

如果你看到了以下 mysql 错误

[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1005 Can't create table 'my_database.#sql-455_63'
  (errno: 150) (SQL: alter table `country_translations` 
  add constraint country_translations_country_id_foreign foreign key (`country_id`) 
  references `countries` (`id`) on delete cascade)

那么你的表使用了 MyISAM 引擎,该引擎不允许外键约束。MyISAM 是 5.5 版本之前的 mysql 默认引擎。从 版本 5.5 开始,默认使用 InnoDB 存储引擎创建表。

如何修复

对于已经创建在生产环境中的表,在添加外键约束之前,更新迁移以更改表的引擎。

public function up()
{
    DB::statement('ALTER TABLE countries ENGINE=InnoDB');
}

public function down()
{
    DB::statement('ALTER TABLE countries ENGINE=MyISAM');
}

对于新表,一个快速的解决方案是在迁移中设置存储引擎

Schema::create('language_translations', function(Blueprint $table){
  $table->engine = 'InnoDB';
  $table->increments('id');
    // ...
});

不过,最好的解决方案是更新你的 mysql 版本。并且务必确保开发和生产环境中使用相同的版本!

捐赠

这款软件是经过精心制作的。

通过向以下地址发送比特币来表达你的爱和支持:167QC4XQ3acgbwVYWAdmS81jARCcVTWBXU

或者通过以下 PayPal 地址发送:ds@dimsav.com

❤️ 感谢!