Database-driven translation management for Laravel applications with automatic locale support and slug generation
ebrahimhanna/translation-manager is a Laravel package for database-driven translation management for laravel applications with automatic locale support and slug generation.
It currently has 0 GitHub stars and 1 downloads on Packagist (latest version v2.0.0).
Install it with composer require ebrahimhanna/translation-manager.
Discover more Laravel packages by ebrahimhanna
or browse all Laravel packages to compare alternatives.
Last updated
Laravel Translation Management is a powerful, database-driven translation package that simplifies multi-language content management in Laravel applications. Store translations in dedicated database tables with automatic management through Eloquent events.
Install the package via Composer:
composer require ebrahimhanna/translation-manager
Publish the configuration file:
php artisan vendor:publish --provider="EbrahimHanna\TranslationManager\PackageServiceProvider"
This will create config/laravel-translations.php with default settings.
You need three types of tables:
Languages table:
Schema::create('languages', function (Blueprint $table) {
$table->id();
$table->string('title'); // e.g., "English"
$table->string('code'); // e.g., "en"
$table->timestamps();
});
Main model table (e.g., products):
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('sku')->unique();
$table->decimal('price', 10, 2);
// ... other non-translatable fields
$table->timestamps();
});
Translation table (e.g., products_translations):
Schema::create('products_translations', function (Blueprint $table) {
$table->id();
$table->foreignId('language_id')->constrained('languages')->cascadeOnDelete();
$table->foreignId('product_id')->constrained('products')->cascadeOnDelete();
$table->string('locale')->nullable(); // Auto-filled
$table->string('name'); // Translatable field
$table->string('slug')->nullable(); // Auto-generated
$table->text('description')->nullable();
$table->timestamps();
$table->unique(['language_id', 'product_id']);
});
Main Model (Product.php):
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use EbrahimHanna\TranslationManager\Traits\HasTranslations;
class Product extends Model
{
use HasTranslations;
protected $fillable = ['sku', 'price', 'stock'];
protected $translation_model = [
'model' => ProductTranslation::class,
'owner_key' => 'product_id',
'slug' => 'name', // Auto-generate slug from 'name' field
];
}
Translation Model (ProductTranslation.php):
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ProductTranslation extends Model
{
protected $fillable = [
'product_id',
'language_id',
'locale',
'name',
'slug',
'description',
];
}
Store with translations:
// The trait automatically saves translations from the request
Product::create([
'sku' => 'PROD-001',
'price' => 99.99,
]);
// Request should contain:
// translations[1][name] = "English Product Name"
// translations[1][description] = "English description"
// translations[2][name] = "اسم المنتج بالعربية"
// translations[2][description] = "وصف باللغة العربية"
Retrieve translations:
$product = Product::find(1);
// Get specific field translation
$englishName = $product->getTranslation(1, 'name');
// or by language code
$arabicName = $product->getTranslation('ar', 'name', 'code');
// Get all translations for a language
$translation = $product->getTranslationsByLanguage(1);
echo $translation->name;
echo $translation->description;
echo $translation->slug; // auto-generated
// Get all translations
$allTranslations = $product->getAllTranslations;
The $translation_model property accepts the following options:
protected $translation_model = [
// Required
'model' => ProductTranslation::class,
// Optional - Override global config
'foreign_key' => 'language_id', // FK to languages table
'owner_key' => 'product_id', // FK to parent model
'translations_data_key' => 'translations', // Request array key
'locale_column' => 'locale', // Column for language code
'language_code_column' => 'code', // Languages table code column
// Optional - Advanced features
'slug' => 'name', // Auto-generate slug from field
'skip_routes' => ['products.update'], // Skip auto-save on routes
];
Edit config/laravel-translations.php:
return [
'language_model' => 'App\Models\Language',
'foreign_key' => 'language_id',
'owner_key' => 'model_id',
'translations_data_key' => 'translations',
'language_code_column' => 'code',
'locale_column' => 'locale', // Set to null to disable
];
Create forms with nested arrays using language IDs as keys:
<form action="{{ route('products.store') }}" method="POST">
@csrf
<!-- Non-translatable fields -->
<input type="text" name="sku" required>
<input type="number" name="price" step="0.01" required>
<!-- Translatable fields for each language -->
@foreach($languages as $language)
<div class="language-section">
<h4>{{ $language->title }} ({{ $language->code }})</h4>
<input type="text"
name="translations[{{ $language->id }}][name]"
placeholder="Product Name">
<textarea name="translations[{{ $language->id }}][description]"
placeholder="Description"></textarea>
</div>
@endforeach
<button type="submit">Create Product</button>
</form>
Use withTranslations() for batch operations or when routes are skipped:
// Create product and manually add translations
$product = Product::create([
'sku' => 'PROD-001',
'price' => 99.99,
]);
$product->withTranslations([
1 => ['name' => 'English Name', 'description' => 'English description'],
2 => ['name' => 'اسم عربي', 'description' => 'وصف عربي'],
]);
// Method chaining
Product::create($data)->withTranslations($translations);
// Delete all translations for a specific language
$product->clearTranslations(1); // By language ID
$product->clearTranslations('en', 'code'); // By language code
// Deleting the model cascades to translations
$product->delete(); // All translations are auto-deleted
// Get single field translation
$name = $product->getTranslation(1, 'name');
$name = $product->getTranslation('en', 'name', 'code');
// Get all fields for a language (returns model or null)
$translation = $product->getTranslationsByLanguage(1);
$translation = $product->getTranslationsByLanguage('en', 'code');
// Get all translations (returns HasMany relationship)
$allTranslations = $product->getAllTranslations;
// Access via Eloquent relationship
$product->translationRelation()->where('locale', 'en')->get();
protected $translation_model = [
'model' => ProductTranslation::class,
'owner_key' => 'product_id',
'slug' => 'name',
'locale_column' => 'locale',
];
protected $translation_model = [
'model' => PostTranslation::class,
'owner_key' => 'post_id',
'slug' => 'title',
];
protected $translation_model = [
'model' => CategoryTranslation::class,
'owner_key' => 'category_id',
'skip_routes' => ['categories.quick-update'], // Don't auto-save here
];
saved event triggers translation managementwithTranslations() for batch inserts/updates->with('translationRelation')Check the /example directory for a complete working Laravel application with:
Contributions are welcome! Please follow these steps:
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)Please ensure:
If you discover any security-related issues, please report them by emailing [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.