HasTranslations Trait
The HasTranslations trait allows WNCMS models to store multilingual content for specific attributes. It uses a custom translation system integrated with WNCMS's localization infrastructure.
Overview
Models using HasTranslations can store translated versions of fields like title, content, description, etc., supporting multiple languages simultaneously.
php
use Wncms\Translatable\Traits\HasTranslations;
class Post extends BaseModel
{
use HasTranslations;
protected $translatable = ['title', 'content', 'excerpt'];
}Configuration
Define Translatable Attributes
Specify which attributes should support translations:
php
protected $translatable = [
'title',
'content',
'description',
'excerpt',
];Usage Examples
Setting Translations
php
$post = Post::create([
'title' => 'Hello World', // Default locale
'content' => 'Post content',
]);
// Set translation for specific locale
$post->setTranslation('title', 'zh_TW', '你好世界');
$post->setTranslation('content', 'zh_TW', '文章內容');
$post->save();
// Or use array syntax
$post->setTranslations('title', [
'en' => 'Hello World',
'zh_TW' => '你好世界',
'ja' => 'こんにちは世界',
]);
$post->save();Retrieving Translations
php
// Get translation for current locale
$title = $post->getTranslation('title');
// Get translation for specific locale
$titleZh = $post->getTranslation('title', 'zh_TW');
// Get translation with fallback
$title = $post->getTranslation('title', 'ja', false);
// If Japanese not available, returns default locale
// Access directly (uses current locale)
echo $post->title; // Automatically uses app()->getLocale()Check Translation Existence
php
// Check if translation exists for field
if ($post->hasTranslation('title', 'zh_TW')) {
echo $post->getTranslation('title', 'zh_TW');
}
// Get all translations for a field
$allTitles = $post->getTranslations('title');
// Returns: ['en' => 'Hello World', 'zh_TW' => '你好世界']Advanced Usage
Translate Multiple Fields at Once
php
$post->translate('zh_TW', [
'title' => '你好世界',
'content' => '文章內容',
'excerpt' => '摘要',
]);
$post->save();Get All Translations
php
// Get all translations for all fields
$translations = $post->translations;
foreach ($translations as $field => $locales) {
foreach ($locales as $locale => $value) {
echo "$field [$locale]: $value\n";
}
}Forget Translation
php
// Remove translation for specific locale and field
$post->forgetTranslation('title', 'zh_TW');
$post->save();
// Remove all translations for a field
$post->forgetAllTranslations('title');
$post->save();Automatic Locale Detection
The trait automatically uses app()->getLocale():
php
// In controller or route
app()->setLocale('zh_TW');
// This will automatically return Chinese title
echo $post->title;
// Switch locale
app()->setLocale('en');
echo $post->title; // Now returns English titleManager Integration
Managers can load translations automatically:
php
class PostManager extends ModelManager
{
public function get(array $options = []): ?Model
{
$q = $this->query();
// Translations are automatically included when accessing attributes
return $q->find($options['id']);
}
}Database Storage
Translations can be stored in:
- JSON column (recommended for smaller datasets)
- Separate
translationstable (for complex scenarios)
JSON Column Approach
php
// Migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->json('title')->nullable();
$table->json('content')->nullable();
$table->timestamps();
});Separate Table Approach
php
// Migration
Schema::create('translations', function (Blueprint $table) {
$table->id();
$table->string('model_type');
$table->unsignedBigInteger('model_id');
$table->string('locale', 10);
$table->string('key'); // field name
$table->text('value');
$table->unique(['model_type', 'model_id', 'locale', 'key']);
});Form Handling
Backend Form Example
blade
<form method="POST" action="{{ route('backend.posts.update', $post) }}">
@foreach(['en', 'zh_TW', 'ja'] as $locale)
<div class="locale-group">
<h4>{{ $locale }}</h4>
<label>Title ({{ $locale }})</label>
<input type="text"
name="translations[{{ $locale }}][title]"
value="{{ $post->getTranslation('title', $locale, false) }}">
<label>Content ({{ $locale }})</label>
<textarea name="translations[{{ $locale }}][content]">
{{ $post->getTranslation('content', $locale, false) }}
</textarea>
</div>
@endforeach
<button type="submit">Save</button>
</form>Controller Handling
php
public function update(Request $request, Post $post)
{
$validated = $request->validate([
'translations.*.title' => 'required|string|max:255',
'translations.*.content' => 'required|string',
]);
foreach ($validated['translations'] as $locale => $fields) {
foreach ($fields as $field => $value) {
$post->setTranslation($field, $locale, $value);
}
}
$post->save();
return redirect()->back()->with('success', 'Translations updated');
}API Response Example
php
// In API Resource
class PostResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->getTranslation('title'),
'content' => $this->getTranslation('content'),
'translations' => [
'title' => $this->getTranslations('title'),
'content' => $this->getTranslations('content'),
],
];
}
}Best Practices
- Define Early - Set
$translatableproperty before using the model - Save After Setting - Always call
save()aftersetTranslation() - Fallback Strategy - Use fallback locales for missing translations
- Validate Input - Validate translation data in forms
- Index Properly - Index locale and model_type columns if using separate table
- Cache Wisely - Translation data can be cached per locale
Troubleshooting
Translation Not Saving
php
// Make sure to save after setting
$post->setTranslation('title', 'zh_TW', '標題');
$post->save(); // Don't forget this!Wrong Locale Returned
php
// Check current locale
dd(app()->getLocale());
// Force specific locale
$title = $post->getTranslation('title', 'zh_TW');Missing Fallback
php
// Enable fallback to default locale
config(['app.fallback_locale' => 'en']);
// Or handle manually
$title = $post->getTranslation('title', $locale, false)
?? $post->getTranslation('title', 'en');