onurkacmaz / laravel-model-translate
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
Requires
- php: ^8.2
- illuminate/contracts: ^12.0|^13.0
- illuminate/database: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.13
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.5|^12.5
- predis/predis: ^2.2|^3.0
Suggests
- ext-redis: Required to use the redis driver with the phpredis client.
- mongodb/mongodb: Required to use the mongodb driver (^1.15|^2.0).
- predis/predis: Required to use the redis driver with the predis client.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-25 18:20:23 UTC
README
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
redisdriver: phpredis or predis - For the
mongodbdriver: themongodbextension andmongodb/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_localeslater has no translation for existing models; updating a model in that locale writes to the model's own table. UsesaveTranslation()to add the translation instead. - One query per model. Translations are loaded in the
retrievedevent, 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.