flobbos/laravel-translatable-db
Laravel Translatable DB

Database-backed translated Eloquent attributes, resolved by a language id instead of a locale string.
This package started as a fork of dimsav/laravel-translatable. It only reads translated attributes. It does not manage CRUD forms or translation persistence.
Compatibility
| Laravel | Package |
|---|---|
| 13.x | 2.x |
| 12.x | 2.x |
| 11.x | 2.x |
| 10.x | 2.x |
| 5.3-9.x | 1.x |
Laravel 10 support means the package requires PHP ^8.1. Laravel itself may require a higher PHP version for newer framework releases.
Installation
composer require flobbos/laravel-translatable-db:^2.0
Laravel auto-discovery registers the service provider. Publish the config only when you need to override defaults:
php artisan vendor:publish --provider="Flobbos\TranslatableDB\TranslatableDBServiceProvider"
Tables
Example for a translated Country model:
Schema::create('languages', function (Blueprint $table) {
$table->id();
$table->string('locale')->unique();
$table->string('name');
});
Schema::create('countries', function (Blueprint $table) {
$table->id();
$table->string('code')->unique();
$table->timestamps();
});
Schema::create('country_translations', function (Blueprint $table) {
$table->id();
$table->foreignId('country_id')->constrained()->cascadeOnDelete();
$table->foreignId('language_id')->constrained()->cascadeOnDelete();
$table->string('name');
$table->unique(['country_id', 'language_id']);
});
If your default language lives on the main model table, add the translated column there and enable native fallback on the model:
public $fallbackAttributes = ['name'];
Then set:
'native_mode' => true,
Models
namespace App\Models;
use Flobbos\TranslatableDB\TranslatableDB;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
use TranslatableDB;
public $translatedAttributes = ['name'];
public $fallbackAttributes = ['name'];
protected $fillable = ['code'];
}
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class CountryTranslation extends Model
{
public $timestamps = false;
protected $fillable = ['language_id', 'name'];
}
By convention, Country uses CountryTranslation. Override it when needed:
public $translationModel = CountryText::class;
Usage
$country = Country::where('code', 'gr')->first();
app()->setLocale('en');
echo $country->name; // Greece
app()->setLocale('de');
echo $country->name; // Griechenland
echo $country->getAttribute('name:2'); // Explicit language id
Configuration
Important defaults:
'use_db' => true,
'language_model' => App\Models\Language::class,
'locale_key' => 'language_id',
'locale_column' => 'locale',
'use_fallback' => true,
'fallback_locale_id' => 1,
'middleware_default' => false,
'to_array_always_loads_translations' => false,
With database mode enabled, the middleware resolves app()->getLocale() through your language model and stores the matching id on the request.
With database mode disabled, configure languages directly:
'use_db' => false,
'language_array' => [
'de' => ['name' => 'Deutsch', 'language_id' => 1],
'en' => ['name' => 'English', 'language_id' => 2],
],
Middleware
The middleware is no longer pushed globally by default in 2.x.
Register it where translated route content needs automatic language id resolution:
use Flobbos\TranslatableDB\Middleware\LanguageIdentification;
Route::middleware(LanguageIdentification::class)->group(function () {
// translated routes
});
If you really want the old global behavior:
'middleware_default' => true,
Polymorphic Translations
Implement PolyTrans and set the morph name:
namespace App\Models;
use Flobbos\TranslatableDB\Contracts\PolyTrans;
use Flobbos\TranslatableDB\TranslatableDB;
use Illuminate\Database\Eloquent\Model;
class Country extends Model implements PolyTrans
{
use TranslatableDB;
public $translationModel = Translation::class;
protected $translationForeignKey = 'translatable';
}
Testing
composer test