flobbos / laravel-translatable-db
A Laravel package for multilingual models
Installs: 2 949
Dependents: 2
Suggesters: 0
Security: 0
Stars: 5
Watchers: 3
Forks: 0
Open Issues: 0
Requires
- php: >=7.1.0
- illuminate/support: >=5.5.0
README
Adding multilingual support to your models with ease
The package is based on Based on https://github.com/dimsav/laravel-translatable
but with the added twist of loading the translation based on a language_id
rather
than a string based locale
from the translations tables.
The package simply deals with retrieving translated content. If you want an easy way of storing your content you should check out https://github.com/Flobbos/laravel-crudable which deals with saving content in your database in a compatible way.
Docs
Demo
Getting translated attributes
$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
Laravel compatibility
Installation
Install package
Add the package in your composer.json by executing the command.
composer require flobbos/laravel-translatable-db
Next, add the service provider to config/app.php
. This step is not necessary
if auto discover didn't work.
Flobbos\TranslatableDB\TranslatableDBServiceProvider::class,
Migrations method 1
In this example, we want to translate the model Country
. We will need an extra table country_translations
and
an extra table languages
.
Schema::create('languages', function(Blueprint $table) { $table->increments('id'); $table->string('locale'); $table->string('name'); } 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('language_id')->index(); $table->unique(['country_id','language_id']); $table->foreign('country_id')->references('id')->on('countries')->onDelete('cascade'); $table->foreign('language_id')->references('id')->on('languages')->onDelete('cascade'); });
Migrations method 2
Schema::create('languages', function(Blueprint $table) { $table->increments('id'); $table->string('locale'); $table->string('name'); } Schema::create('countries', function(Blueprint $table) { $table->increments('id'); $table->string('code'); $table->string('name'); //the original name lives in this table $table->timestamps(); }); Schema::create('country_translations', function(Blueprint $table) { $table->increments('id'); $table->integer('country_id')->unsigned(); $table->string('name'); $table->string('language_id')->index(); $table->unique(['country_id','language_id']); $table->foreign('country_id')->references('id')->on('countries')->onDelete('cascade'); $table->foreign('language_id')->references('id')->on('languages')->onDelete('cascade'); });
Method 2 assumes that your default translation lives in the same table as the model that is to be translated. In this case you have to set:
public $fallbackAttributes = ['name'];
The default translation will then be pulled from the original model's table.
Step 3: Models
- The translatable model
Country
should use the traitFlobbos\TranslatableDB\TranslatableDB
. - The convention for the translation model is
CountryTranslation
.
// models/Country.php class Country extends Eloquent { use \Flobbos\TranslatableDB\TranslatableDB; public $translatedAttributes = ['name']; public $fallbackAttributes = ['name']; protected $fillable = ['code']; //protected $fillable = ['code','name']; //if method 2 was used /** * 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']; }
The array $translatedAttributes
contains the names of the fields being translated in the "Translation" model.
Step 4: Publish config
Laravel ^5.3.*
php artisan vendor:publish
With this command, initialize the configuration and modify the created file, located under app/config/translatable.php
.
Configuration
DB Usage
If you want to load languages from a table in the datbase you need to set this to true in the config.
'use_db' => true,
Language model
The language model is set to App\Language
by default but it can be anything
you want.
'language_model' => 'App\Whatever'
Language array
If you prefer to use the config file for storing your language based information you can setup the language array to your needs. We will still be relying on the language_id as the identifier for each language.
'language_array' => [ 'de' => ['name' => 'Deutsch', 'language_id' => '1'], 'en' => ['name' => 'English', 'language_id' => '2'], 'fr' => ['name' => 'Français', 'language_id' => '3'] ]
Fallback
Sometimes translations can be missing. In this case we can use a fallback translation to prevent missing content on a page.
'use_fallback' => true,
If you don't want to use a fallback, simply set this to false and you will get
null
if there's no suitable translation available.
Default fallback
The default fallback varies depending on DB or non-DB usage in the package. You can set these options in the config with either:
'fallback_locale' => 'de'
or
'fallback_locale_id' => 1,
Native mode
Sometimes content has already been added to a table that later needs to be translated. To prevent the mess of migrating existing content into our translation tables we have the option to use native mode. This will assume the default content lives in the table of the translated model.
'native_mode' => true
Locale key
The key used for finding the corresponding translation in our translations table. We assume language_id as this is used throughout the package but it can be whatever you set it to be here.
'locale_key' => 'language_id',
This will also set the value pushed into the request by the middleware.
Locale column
We need to identify the current locale by calling app()->getLocale()
and find
the corresponding language in the database. In case your language identifier is
called something other than 'locale' in the DB, you can set it here:
'locale_column' => 'locale',
toArray translations
It gets tricky when your translated models are output in an array or JSON format. If the translation isn't loaded into the model it will get omitted when either function is called. With this setting you can force the translation into the model. Beware of unnecessary n-queries!
'to_array_always_loads_translations' => true,
middleware_default
If you don't want to use the middleware on all routes, then set this to false and add the middleware where needed.
'middleware_default' => false
Translation Model
The convention used to define the class of the translation model is to append the keyword Translation
.
So if your model is \MyApp\Models\Country
, the default translation would be \MyApp\Models\CountryTranslation
.
To use a custom class as translation model, define the translation class (including the namespace) as parameter. For example:
<?php namespace MyApp\Models; use Flobbos\TranslatableDB\TranslatableDB; use Illuminate\Database\Eloquent\Model as Eloquent; class Country extends Eloquent { use TranslatableDB\TranslatableDB; public $translationModel = MyApp\Models\CountryAwesomeTranslation::class; }
Polymorphic translations
Sometimes translations are in a polymorphic table. In order to make this work you need to make the following changes to your model.
namespace MyApp\Models; use Flobbos\TranslatableDB\TranslatableDB; use Flobbos\TranslatableDB\Contracts\PolyTrans; use Illuminate\Database\Eloquent\Model as Eloquent; class Country extends Eloquent implements PolyTrans { use TranslatableDB; public $translationModel = MyApp\Models\CountryAwesomeTranslation::class; //Set your polymorphic key here protected $translationForeignKey = 'translatable'; }
By implementing the PolyTrans contract TranslatableDB knows that it needs to handle translations differently. The translation key needs to be set as well because otherwise TranslatableDB will assume the key automatically which doesn't work with a polymorphic relationship.
Middleware
Default
By default the middleware is pushed into the kernel by the service provider. If DB use is set to true in the config, the middleware resolves the language model set in the config and loads the 'language_id' parameter into every request. With this language_id property the corresponding translation is automatically loaded into the model.
Custom
If you only want specific routes to load translated content from the DB you can set this in the config:
'middleware_default' => false
Then you need to manually register the middleware for the routes where needed.