Installation:
composer require typicms/translatable
No publisher or configuration required—just add the trait to your model.
First Use Case:
Apply the HasTranslations trait to any Eloquent model:
use TypiCMS\Translatable\HasTranslations;
class Product extends Model
{
use HasTranslations;
}
Basic Usage:
$product = new Product();
$product->setTranslation('title', 'en', 'English Title');
$product->setTranslation('title', 'es', 'Título en Español');
$product->save();
Where to Look First:
HasTranslations trait methods (e.g., getTranslation, setTranslation, getTranslations).Setting Translations:
// Single field
$model->setTranslation('field_name', 'locale', 'value');
// Multiple fields at once
$model->setTranslations([
'field1' => ['en' => 'value1', 'es' => 'valor1'],
'field2' => ['en' => 'value2'],
]);
Retrieving Translations:
// Get a single translation
$title = $model->getTranslation('title', 'en');
// Get all translations for a field
$translations = $model->getTranslations('title');
// Fallback to default locale if missing
$model->getTranslation('title', 'fr', 'default_fallback');
Querying Models with Translations:
// Filter by translation value
$results = Product::whereTranslation('title', 'en', 'like', '%phone%')->get();
// Filter by existence of a translation
$results = Product::whereHasTranslation('title', 'es')->get();
Default Locale Handling:
// Set default locale (e.g., in AppServiceProvider)
config(['translatable.default_locale' => 'en']);
// Translations without a locale use the default
$model->setTranslation('title', 'English Title'); // Auto-assigned to 'en'
Form Requests:
Use setTranslations() in form handlers to batch-set translations from request data:
$model->setTranslations($request->input('translations', []));
API Responses:
Serialize translations with getTranslations() in API resources:
public function toArray($request)
{
return [
'title' => $this->getTranslations('title'),
// ...
];
}
Validation: Validate translations using Laravel’s validation rules:
$validator = Validator::make($request->all(), [
'translations.title.en' => 'required|string|max:255',
]);
Scopes: Create custom scopes for complex translation queries:
public function scopeInLanguage($query, $locale)
{
return $query->whereJsonContains('translations->' . $locale, '{}')
->where('translations->' . $locale, '!=', null);
}
JSON Storage Overhead:
translations). Large numbers of fields/locales may bloat the database.Locale-Specific Queries:
whereJsonContains or raw SQL for complex queries:
// Raw SQL example (Laravel 8+)
$query->whereRaw("JSON_EXTRACT(translations, '$.en.title') LIKE ?", ['%phone%']);
Default Locale Assumptions:
setTranslation() without a locale will throw an error. Always configure:
config(['translatable.default_locale' => 'en']);
Mass Assignment Risks:
translations attribute is not guarded by default. Explicitly fillable attributes in $fillable may not include translation fields:
protected $fillable = ['title', 'translations']; // Add if using mass assignment
Caching Translations:
private $cachedTranslations = [];
public function getTranslation($field, $locale = null)
{
$key = $field . ($locale ?? '');
return $this->cachedTranslations[$key] ?? parent::getTranslation($field, $locale);
}
Inspect JSON Structure:
Use dd($model->translations) to debug the stored JSON structure. Example output:
{
"title": {
"en": "English Title",
"es": "Título"
},
"description": {
"en": null
}
}
Check for Typos:
if (!$model->hasTranslation('field_name', 'locale')) {
// Handle missing translation
}
Database-Level Debugging:
DB::enableQueryLog() to inspect generated queries for translation lookups.Fallback Logic:
getTranslation():
$model->getTranslation('title', 'fr', 'en'); // Fallback to 'en' if 'fr' missing
Custom Storage:
Override the getTranslationsAttribute and setTranslationsAttribute methods to change storage logic (e.g., separate tables):
public function getTranslationsAttribute($value)
{
return $this->translations ?? [];
}
public function setTranslationsAttribute($value)
{
$this->attributes['translations'] = json_encode($value);
}
Locale-Specific Accessors: Dynamically generate accessors for locale-specific fields:
public function getTitleEnAttribute()
{
return $this->getTranslation('title', 'en');
}
Event Hooks:
Trigger events for translation changes (e.g., translationsUpdated):
protected static function booted()
{
static::saved(function ($model) {
if ($model->wasChanged('translations')) {
event(new TranslationsUpdated($model));
}
});
}
Validation Rules: Extend validation with custom rules for translations:
Validator::extend('has_translation', function ($attribute, $value, $parameters, $validator) {
[$field, $locale] = $parameters;
return $validator->getData()->hasTranslation($field, $locale);
});
How can I help you explore Laravel packages today?