Search by

onurkacmaz / laravel-model-translate

onurkacmaz

Translate Eloquent model attributes into multiple locales, stored in a database table, Redis or MongoDB. Inspired by Gedmo Translatable.

Package info

github.com/onurkacmaz/laravel-model-translate

pkg:composer/onurkacmaz/laravel-model-translate

Statistics

Installs: 17

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

v2.0.0 2026-10-25 18:16 UTC

This package is auto-updated.

Last update: 2026-10-25 18:20:23 UTC


README

Latest Version on Packagist Tests Total Downloads License

Translate the attributes of your Eloquent models into multiple locales, inspired by Gedmo Translatable for Symfony.

Add a trait to a model, list the columns to translate, and the package takes care of the rest:

  • The values written in the locale that is active when a model is created stay in the model's own table.
  • A copy of the translatable columns is stored for every other supported locale, in a database table, Redis or MongoDB.
  • When a model is retrieved, its translatable columns are replaced with the translation of the current application locale.
  • When a model is updated in a translated locale, the changed translatable columns are written to the translation storage and the model's own table is left untouched. Other (non-translatable) columns are saved as usual.
  • When a model is deleted, its translations are deleted too (soft deleted models keep them until force deleted).

Requirements

  • PHP 8.2+
  • Laravel 12 or 13
  • For the redis driver: phpredis or predis
  • For the mongodb driver: the mongodb extension and mongodb/mongodb

Installation

Install the package via composer:

composer require onurkacmaz/laravel-model-translate

Publish the config file and the migration, then run the migration:

php artisan vendor:publish --tag=laravel-model-translate-config
php artisan vendor:publish --tag=laravel-model-translate-migrations
php artisan migrate

The migration is only needed for the database driver.

Configuration

config/laravel-model-translate.php:

return [
    // Locales the models can be translated into.
    'supported_locales' => ['tr', 'en', 'de', 'fr'],

    // database, redis or mongodb
    'driver' => env('TRANSLATION_DRIVER', 'database'),

    'drivers' => [
        'database' => [
            'driver' => \Onurkacmaz\LaravelModelTranslate\Drivers\Database::class,
            'connection' => env('TRANSLATION_DB_CONNECTION'), // null: default connection
            'table' => env('TRANSLATION_DB_TABLE', 'translations'),
        ],
        'redis' => [
            'driver' => \Onurkacmaz\LaravelModelTranslate\Drivers\Redis::class,
            'connection' => env('TRANSLATION_REDIS_CONNECTION', 'default'), // from config/database.php
            'prefix' => env('TRANSLATION_REDIS_PREFIX', 'translations'),
        ],
        'mongodb' => [
            'driver' => \Onurkacmaz\LaravelModelTranslate\Drivers\MongoDb::class,
            'dsn' => env('TRANSLATION_MONGO_DSN', 'mongodb://localhost:27017'),
            'database' => env('TRANSLATION_MONGO_DB', 'translations'),
            'collection' => env('TRANSLATION_MONGO_COLLECTION', 'translations'),
        ],
    ],
];

To use the MongoDB driver, install the client library first:

composer require mongodb/mongodb

Usage

Making a model translatable

Use the Translatable trait and list the translatable columns in a $translatable property:

use Illuminate\Database\Eloquent\Model;
use Onurkacmaz\LaravelModelTranslate\Traits\Translatable;

class Blog extends Model
{
    use Translatable;

    protected array $translatable = ['title', 'content'];
}

or, if you need to compute them, override getTranslatable():

public function getTranslatable(): array
{
    return ['title', 'content'];
}

Reading and writing translations

app()->setLocale('tr');

$blog = Blog::create(['title' => 'Merhaba Dünya', 'content' => '...']);
// "tr" is stored in the blogs table, "en", "de" and "fr" start as copies of it.

app()->setLocale('en');

$blog = Blog::find($blog->id);
$blog->title;                     // "Merhaba Dünya" (the "en" copy)

$blog->update(['title' => 'Hello World']);
// Only the "en" translation is updated, the blogs table still says "Merhaba Dünya".

Blog::find($blog->id)->title;     // "Hello World"

app()->setLocale('tr');
Blog::find($blog->id)->title;     // "Merhaba Dünya"

The trait also adds a few helpers:

// Store a translation without changing the locale or the model's attributes.
// Returns false when the locale is the one kept in the model's own table.
$blog->saveTranslation('de', ['title' => 'Hallo Welt']);

// Get the stored translation of a locale, keyed by column.
$blog->getTranslations('de');     // ['title' => 'Hallo Welt', 'content' => '...']

// Show the model in another locale.
$blog->translateTo('de')->title;  // "Hallo Welt"

Without the trait

The LaravelModelTranslate class can be used directly, for example in a controller:

use Onurkacmaz\LaravelModelTranslate\LaravelModelTranslate;

$translate = new LaravelModelTranslate();
$translate->setColumns(['title', 'content']);
$translate->setModel($blog);
$translate->setLocale('en');
$translate->translate();

// or
(new LaravelModelTranslate($blog, ['title', 'content'], 'en'))->translate();

// or
LaravelModelTranslate::make()
    ->setModel($blog)
    ->setLocale('en')
    ->setColumns(['title', 'content'])
    ->translate();

// or, through the facade
\LaravelModelTranslate::setModel($blog)->setColumns(['title'])->setLocale('en')->translate();

When no locale is set, the current application locale is used. Besides translate(), the class has getTranslations(), create(), update(?array $values = null) and delete().

Custom drivers

Any class implementing Onurkacmaz\LaravelModelTranslate\Drivers\DriverInterface can be used as a driver. Register it in the config file; its constructor receives the driver's config array as $config:

'driver' => 'dynamodb',

'drivers' => [
    'dynamodb' => [
        'driver' => App\Translation\DynamoDbDriver::class,
        'table' => 'translations',
    ],
],

Things to know

  • The locale that creates a model owns its table. A locale is treated as "translated" for a model when a translation exists for it. Translations are only seeded once, when the model is first saved, so a locale added to supported_locales later has no translation for existing models; updating a model in that locale writes to the model's own table. Use saveTranslation() to add the translation instead.
  • One query per model. Translations are loaded in the retrieved event, so loading a collection of N models runs N translation lookups.
  • Morph map. Models are identified by getMorphClass(), so an enforced morph map is respected. Adding a morph map after translations were stored changes the stored identifier.

Testing

composer test

The Redis tests use the server at REDIS_HOST:REDIS_PORT (default 127.0.0.1:6379, database 15) and are skipped when it is not reachable. The MongoDB tests run when mongodb/mongodb is installed and a server is reachable at TRANSLATION_MONGO_DSN.

Changelog

Please see CHANGELOG for what has changed recently, and UPGRADE when upgrading from 1.x.

Contributing

Please see CONTRIBUTING for details.

Security

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.