esign/laravel-underscore-translatable
Installation:
composer require esign/laravel-underscore-translatable
Publish the config (optional):
php artisan vendor:publish --provider="Esign\UnderscoreTranslatable\UnderscoreTranslatableServiceProvider"
First Use Case:
use Esign\UnderscoreTranslatable\UnderscoreTranslatable;
class Post extends Model
{
use UnderscoreTranslatable;
public $translatable = ['title', 'content'];
}
title_en, title_nl).setTranslation() and getTranslation() methods:
$post = new Post();
$post->setTranslation('title', 'nl', 'Nederlandse titel');
$post->setTranslation('title', 'en', 'English title');
echo $post->getTranslation('title', 'nl'); // Outputs: "Nederlandse titel"
config/underscore-translatable.php (default locale, fallback behavior).UnderscoreTranslatable for core methods.database/migrations/ for auto-generated language columns.Defining Translatable Fields:
public $translatable = ['title', 'description', 'meta_keywords'];
Setting Translations:
// Single field
$post->setTranslation('title', 'es', 'Título en español');
// Multiple fields at once
$post->setTranslations([
'title' => ['en' => 'English Title', 'fr' => 'Titre Français'],
'content' => ['en' => 'English content', 'fr' => 'Contenu français']
]);
Retrieving Translations:
// Get translation for a specific locale
$title = $post->getTranslation('title', 'de');
// Get all translations for a field
$allTitles = $post->getTranslations('title');
// Fallback to default locale if translation missing
$title = $post->getTranslation('title', 'it'); // Falls back to config('underscore-translatable.default_locale')
Dynamic Locale Handling:
// In a middleware
app()->setLocale(request()->header('accept-language') ?? config('app.locale'));
$post->getTranslation('title'); // Uses app()->getLocale()
Mass Assignment:
protected $fillable = ['title', 'content'];
$post = Post::create([
'title_en' => 'Hello',
'title_nl' => 'Hallo',
'content_en' => 'Content...'
]);
APIs:
setTranslations() to batch-save translations from API requests.$data = request()->all();
$post->setTranslations($data);
$post->save();
Forms:
@foreach (config('underscore-translatable.locales') as $locale)
<input name="title_{{ $locale }}" value="{{ old("title_{$locale}", $post->getTranslation('title', $locale)) }}">
@endforeach
Relationships:
class Category extends Model
{
use UnderscoreTranslatable;
public $translatable = ['name'];
public function posts()
{
return $this->hasMany(Post::class);
}
}
Scopes:
public function scopeInLanguage($query, $locale)
{
return $query->where("title_{$locale}", 'like', '%' . $search . '%');
}
Migration Conflicts:
field_locale).title_en, content_nl (not title-en or title[en]).Default Locale Fallback:
config/underscore-translatable.php).public function getTranslation($attribute, $locale = null, $default = null)
{
return parent::getTranslation($attribute, $locale, $default ?? 'fallback_value');
}
Case Sensitivity:
EN ≠ en). Stick to lowercase (e.g., en, nl).Mass Assignment Risks:
$guarded or $fillable. Translatable fields like title_en may not be explicitly listed.protected $fillable = [
'title_en', 'title_nl', 'content_en', 'content_nl',
// ... other fields
];
Performance:
getTranslations() in tight loops. Cache results if needed:
private $cachedTranslations = [];
public function getTranslation($attribute, $locale = null)
{
$cacheKey = "{$attribute}_{$locale}";
return $this->cachedTranslations[$cacheKey] ?? parent::getTranslation($attribute, $locale);
}
Missing Translations:
title_en).app()->getLocale()).SQL Errors:
$translatable.Locale Not Updating:
php artisan config:clear
Custom Column Naming:
field_locale naming convention:
use Esign\UnderscoreTranslatable\Concerns\GeneratesTranslationColumnNames;
class CustomPost extends Post
{
use GeneratesTranslationColumnNames;
protected $translationColumnPattern = '{{field}}_{{locale}}_custom';
}
Custom Fallback Logic:
public function getTranslation($attribute, $locale = null, $default = null)
{
$translation = parent::getTranslation($attribute, $locale, $default);
return $translation ?: $this->generateFallback($attribute, $locale);
}
protected function generateFallback($attribute, $locale)
{
// Custom logic (e.g., fetch from another model)
}
Dynamic Locale Detection:
public function getCurrentLocale()
{
return request()->input('locale') ?: app()->getLocale();
}
Validation:
$validator = Validator::make($data, [
'title_en' => 'required|string',
'title_nl' => 'required|string',
]);
Events:
public static function bootUnderscoreTranslatable()
{
static::saved(function ($model) {
if ($model->wasRecentlyCreated) {
event(new TranslationsCreated($model));
}
});
}
How can I help you explore Laravel packages today?