We've covered translations for static text and routes, but what about the Models? What if we have a Blog Post that we want to store in a multi-language format?
There are quite a few ways to do this, but first, let's look at the simplest way without any packages:
Let's Get Started
For this demo, we will create Posts
in multiple languages. The idea here will be:
- Make sure that we have a list of supported locales somewhere (for example in
config/app.php
assupportedLocales
) - Create a
Post
model that will have all the details about the post, except for the title and content - Create a
PostTranslation
model that will have the title and content of the post in a specific language - Automatically load the current locale translation of the post when we retrieve the post (with
hasOne
relation and attribute mutators in ourPost
model) - Validate each translated field for each supported locale
- Create a
Post
and its translations in one go
Here's the database schema for our Post
and its translations in PostTranslation
:
Migration
Schema::create('posts', function (Blueprint $table) { $table->id(); $table->dateTime('publish_date')->nullable(); $table->foreignId('user_id')->constrained(); $table->softDeletes(); $table->timestamps();}); Schema::create('post_translations', function (Blueprint $table) { $table->id(); $table->foreignId('post_id')->constrained()->cascadeOnDelete(); $table->string('locale'); $table->string('title'); $table->longText('post'); $table->softDeletes(); $table->timestamps();});
Our Post model, which will contain a title
and post
attributes to always have the current locale translation of the text:
app/Models/Post.php
use Illuminate\Database\Eloquent\Casts\Attribute;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo;use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Database\Eloquent\Relations\HasOne;use Illuminate\Database\Eloquent\SoftDeletes; class Post extends Model{ use SoftDeletes; protected $fillable = [ 'publish_date', 'user_id', ]; protected $casts = [ 'publish_date' => 'datetime', ]; // Preloading current locale translation at all times protected $with = [ 'defaultTranslation' ]; public function title(): Attribute { return new Attribute( // Always making sure that we have current locale title get: fn() => $this->defaultTranslation->title, ); } public function post(): Attribute { return new Attribute( // Always making sure that we have current locale post model get: fn() => $this->defaultTranslation->post, ); } public function author(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); } public function translations(): HasMany { return $this->hasMany(PostTranslation::class); } public function defaultTranslation(): HasOne { // Making sure that we always retrieve current locale information return $this->translations()->one()->where('locale', app()->getLocale()); }}
And our PostTranslation
model:
app/Models/PostTranslation.php
use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo;use Illuminate\Database\Eloquent\SoftDeletes; class PostTranslation extends Model{ use SoftDeletes; protected $fillable = [ 'post_id', 'locale', 'title', 'post', ]; public function post(): BelongsTo { return $this->belongsTo(Post::class); }}
Once we have our Models, we can look into our PostController
and see how we can create a new post with translations...