parsaaghayi-backend/vendor/spatie/laravel-translatable/resources/boost/skills/translatable-development/SKILL.md
DevOps 3aad54907e
Some checks failed
Deploy Backend / deploy (push) Failing after 3m44s
chore: commit complete vendor (Liara mirror no dist)
2026-06-23 15:37:41 +00:00

5.0 KiB

name description license metadata
translatable-development Use when working with spatie/laravel-translatable. Trigger when the query mentions translatable models, translations, multi-language attributes, the HasTranslations trait, the #[Translatable] attribute, or storing translations as JSON on Eloquent models. Tasks include making a model translatable, writing migrations for translatable columns, setting translations for specific locales, getting translations (with or without fallbacks), querying by locale, handling missing translations, and testing translatable behavior. MIT
author
spatie

spatie/laravel-translatable

Translations are stored as JSON on the model itself. No extra tables, no pivot.

Making a model translatable

Declare translatable attributes via the #[Translatable] PHP attribute (preferred) and add the HasTranslations trait.

use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\Attributes\Translatable;
use Spatie\Translatable\HasTranslations;

#[Translatable('name', 'description')]
class NewsItem extends Model
{
    use HasTranslations;
}

The attribute accepts a variadic list of column names.

The $translatable property is also supported:

class NewsItem extends Model
{
    use HasTranslations;

    public $translatable = ['name', 'description'];
}

When both are present, their values are merged and deduplicated.

Migration

Translatable columns must be json (or text if the database does not support json). One column per translatable attribute — the column holds all locales as a JSON object.

Schema::create('news_items', function (Blueprint $table) {
    $table->id();
    $table->json('name')->nullable();
    $table->json('description')->nullable();
    $table->timestamps();
});

Setting translations

// Current locale (via app()->getLocale())
$newsItem->name = 'Hello';

// Specific locale
$newsItem->setTranslation('name', 'en', 'Hello');
$newsItem->setTranslation('name', 'nl', 'Hallo');

// Multiple locales at once
$newsItem->setTranslations('name', ['en' => 'Hello', 'nl' => 'Hallo']);

// Mass assignment with a per-locale array
NewsItem::create([
    'name' => ['en' => 'Hello', 'nl' => 'Hallo'],
]);

$newsItem->save();

Getting translations

// Current locale
$newsItem->name;

// Specific locale (falls back to the configured fallback locale by default)
$newsItem->getTranslation('name', 'nl');

// Without fallback
$newsItem->getTranslationWithoutFallback('name', 'nl');

// All translations for one attribute
$newsItem->getTranslations('name'); // ['en' => 'Hello', 'nl' => 'Hallo']

// All translations for all translatable attributes
$newsItem->translations;

// All locales this model has translations in
$newsItem->locales();

Querying by locale

// Models that have a translation for the given locale
NewsItem::query()->whereLocale('name', 'en')->get();

// Models that have a translation in any of the given locales
NewsItem::query()->whereLocales('name', ['en', 'nl'])->get();

// Filter by translated value
NewsItem::query()->whereJsonContainsLocale('name', 'en', 'Hello')->get();

Forgetting translations

$newsItem->forgetTranslation('name', 'nl');      // one locale of one attribute
$newsItem->forgetTranslations('name');           // all locales of one attribute
$newsItem->forgetAllTranslations('nl');          // one locale across all translatable attributes

Fallback behavior

By default, getTranslation() falls back to config('app.fallback_locale') when a translation is missing. Override per model:

class NewsItem extends Model
{
    use HasTranslations;

    public $useFallbackLocale = false; // disable fallback for this model

    public function getFallbackLocale(): ?string
    {
        return 'en'; // custom fallback
    }
}

Testing

use Spatie\Translatable\Facades\Translatable;

it('stores a translation', function () {
    $newsItem = NewsItem::create(['name' => ['en' => 'Hello']]);

    expect($newsItem->getTranslation('name', 'en'))->toBe('Hello');
});

it('falls back when a translation is missing', function () {
    config()->set('app.fallback_locale', 'en');

    $newsItem = NewsItem::create(['name' => ['en' => 'Hello']]);

    expect($newsItem->getTranslation('name', 'nl'))->toBe('Hello');
});

Common pitfalls

  • Translatable columns must be json (or text) in the migration. A string column will throw on JSON casting.
  • Setting a plain string on a translatable attribute only writes the current locale. Pass an associative array to set multiple locales at once.
  • Do not register an array or json cast on translatable columns in $casts. HasTranslations handles the JSON encoding.
  • The $translatable property and #[Translatable] attribute are class-level. Runtime mutations of $this->translatable work but are uncommon.
  • getTranslation() returns the fallback locale's value when the requested locale is missing. Use getTranslationWithoutFallback() for a strict lookup.