Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Translatable Laravel Package

typicms/translatable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require typicms/translatable
    

    No publisher or configuration required—just add the trait to your model.

  2. First Use Case: Apply the HasTranslations trait to any Eloquent model:

    use TypiCMS\Translatable\HasTranslations;
    
    class Product extends Model
    {
        use HasTranslations;
    }
    
  3. Basic Usage:

    $product = new Product();
    $product->setTranslation('title', 'en', 'English Title');
    $product->setTranslation('title', 'es', 'Título en Español');
    $product->save();
    
  4. Where to Look First:

    • README for core features.
    • Source Code for method signatures and edge cases.
    • HasTranslations trait methods (e.g., getTranslation, setTranslation, getTranslations).

Implementation Patterns

Core Workflows

  1. Setting Translations:

    // Single field
    $model->setTranslation('field_name', 'locale', 'value');
    
    // Multiple fields at once
    $model->setTranslations([
        'field1' => ['en' => 'value1', 'es' => 'valor1'],
        'field2' => ['en' => 'value2'],
    ]);
    
  2. 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');
    
  3. 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();
    
  4. 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'
    

Integration Tips

  • 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);
    }
    

Gotchas and Tips

Pitfalls

  1. JSON Storage Overhead:

    • Translations are stored as JSON in a single column (e.g., translations). Large numbers of fields/locales may bloat the database.
    • Mitigation: Limit translatable fields or use a separate table for high-volume use cases.
  2. Locale-Specific Queries:

    • The package doesn’t support querying nested JSON paths directly in all Laravel versions. Use whereJsonContains or raw SQL for complex queries:
      // Raw SQL example (Laravel 8+)
      $query->whereRaw("JSON_EXTRACT(translations, '$.en.title') LIKE ?", ['%phone%']);
      
  3. Default Locale Assumptions:

    • If no default locale is set, setTranslation() without a locale will throw an error. Always configure:
      config(['translatable.default_locale' => 'en']);
      
  4. Mass Assignment Risks:

    • The 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
      
  5. Caching Translations:

    • Frequently accessed translations (e.g., in loops) can be cached manually:
      private $cachedTranslations = [];
      
      public function getTranslation($field, $locale = null)
      {
          $key = $field . ($locale ?? '');
          return $this->cachedTranslations[$key] ?? parent::getTranslation($field, $locale);
      }
      

Debugging Tips

  1. 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
        }
    }
    
  2. Check for Typos:

    • Typos in field names or locales will silently fail. Validate with:
      if (!$model->hasTranslation('field_name', 'locale')) {
          // Handle missing translation
      }
      
  3. Database-Level Debugging:

    • Use DB::enableQueryLog() to inspect generated queries for translation lookups.
  4. Fallback Logic:

    • Ensure fallback locales are handled in getTranslation():
      $model->getTranslation('title', 'fr', 'en'); // Fallback to 'en' if 'fr' missing
      

Extension Points

  1. 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);
    }
    
  2. Locale-Specific Accessors: Dynamically generate accessors for locale-specific fields:

    public function getTitleEnAttribute()
    {
        return $this->getTranslation('title', 'en');
    }
    
  3. 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));
            }
        });
    }
    
  4. 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);
    });
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel