Weave Code
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

Laravel Translatable Laravel Package

astrotomic/laravel-translatable

Laravel package for translatable Eloquent models. Store model translations in the database and automatically fetch/save multilingual attributes based on locale, reducing boilerplate when working with multi-language content.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to Begin

  1. Installation

    composer require astrotomic/laravel-translatable
    php artisan vendor:publish --tag=translatable
    

    Configure locales in config/translatable.php (e.g., ['en', 'fr', 'es']).

  2. Create Migration

    php artisan make:migration create_posts_table
    php artisan make:migration create_post_translations_table
    

    Define tables for the main model (e.g., posts) and translations (e.g., post_translations).

  3. Define Models

    • Main Model (Post.php):
      use Astrotomic\Translatable\Translatable;
      
      class Post extends Model
      {
          use Translatable;
          public $translatedAttributes = ['title', 'content'];
          protected $fillable = ['author'];
      }
      
    • Translation Model (PostTranslation.php):
      class PostTranslation extends Model
      {
          public $timestamps = false;
          protected $fillable = ['title', 'content'];
      }
      
  4. First Use Case

    // Create a post with translations
    $post = Post::create([
        'author' => 'John Doe',
        'en' => ['title' => 'Hello World'],
        'fr' => ['title' => 'Bonjour le monde'],
    ]);
    
    // Access translations
    echo $post->translate('en')->title; // "Hello World"
    echo $post->translate('fr')->title; // "Bonjour le monde"
    

Implementation Patterns

Core Workflows

  1. Dynamic Locale Switching Use App::setLocale() or middleware to switch locales globally:

    App::setLocale('fr');
    echo $post->title; // Automatically fetches French translation
    
  2. Mass Assignment with Translations

    $data = [
        'author' => 'Jane Doe',
        'translations' => [
            'en' => ['title' => 'Updated Title'],
            'es' => ['title' => 'Título Actualizado'],
        ],
    ];
    Post::create($data);
    
  3. Fallback Handling

    // Force fallback to 'en' if translation is missing
    $post->translate('it', true)->title; // Falls back to English
    
  4. Querying with Translations

    // Get posts with English translations
    $posts = Post::withTranslations('en')->get();
    
  5. Replicating with Translations

    $clone = $post->replicateWithTranslations();
    

Integration Tips

  • API Responses: Use getTranslationsArray() to serialize translations:
    return response()->json($post->getTranslationsArray());
    
  • Forms: Use translateOrNew() to preload translations:
    $translation = $post->translateOrNew('de');
    
  • Admin Panels: Leverage translatedAttributes for dynamic form fields (e.g., QuickAdminPanel, Nova).
  • Scopes: Create custom scopes for locale-specific queries:
    public function scopeInLocale($query, $locale)
    {
        return $query->whereHas('translations', function($q) use ($locale) {
            $q->where('locale', $locale);
        });
    }
    

Gotchas and Tips

Pitfalls

  1. Locale Mismatches

    • Ensure all locales in config/translatable.php match those used in code.
    • Fix: Validate locales during creation:
      if (!in_array($locale, config('translatable.locales'))) {
          throw new \InvalidArgumentException("Locale {$locale} not configured.");
      }
      
  2. Missing Translations

    • translate() returns null for missing locales. Use translateOrDefault() or translateOrNew() to avoid NullPointerException.
    • Tip: Set a fallback_locale in config to auto-fallback:
      'fallback_locale' => 'en',
      
  3. Circular References in Serialization

    • Avoid serializing models with translations in loops (e.g., toArray() in collections).
    • Fix: Use getTranslationsArray() or exclude translations:
      protected $hidden = ['translations'];
      
  4. STI (Single Table Inheritance) Issues

    • Customize translationForeignKey if using STI:
      protected $translationForeignKey = 'parent_id';
      
  5. Performance with Large Datasets

    • Eager-load translations to avoid N+1 queries:
      $posts = Post::with(['translations' => function($query) {
          $query->where('locale', app()->getLocale());
      }])->get();
      

Debugging Tips

  • Check Translation Existence:
    if (!$post->hasTranslation('fr')) {
        // Handle missing translation
    }
    
  • Inspect Translation Data:
    dd($post->getTranslationsArray());
    
  • Enable Query Logging:
    \DB::enableQueryLog();
    $post->translate('en');
    dd(\DB::getQueryLog());
    

Extension Points

  1. Custom Translation Models Override the default Translation model by setting translationModel() in the trait:

    use Astrotomic\Translatable\Translatable;
    
    class Post extends Model
    {
        use Translatable;
    
        protected function translationModel()
        {
            return CustomPostTranslation::class;
        }
    }
    
  2. Dynamic Translated Attributes Use accessors/mutators for dynamic fields:

    public function getDescriptionAttribute()
    {
        return $this->translate()->description ?? 'No description';
    }
    
  3. Validation Rules Extend validation to ensure translations exist:

    use Astrotomic\Translatable\Validation\TranslatableRule;
    
    $validator = Validator::make($data, [
        'translations.en.title' => ['required', new TranslatableRule('Post', 'en', 'title')],
    ]);
    
  4. Events Listen for translation-related events:

    \Event::listen('translatable.saving', function ($model, $locale, $attributes) {
        // Log translation changes
    });
    
  5. Caching Cache translations for read-heavy apps:

    $translation = Cache::remember("post_{$post->id}_{$locale}", now()->addHours(1), function() use ($post, $locale) {
        return $post->translate($locale);
    });
    

Configuration Quirks

  • translations_wrapper: Useful for nested payloads (e.g., API requests):

    'translations_wrapper' => 'i18n',
    
    $post = Post::create([
        'i18n' => [
            'en' => ['title' => 'Hello'],
            'fr' => ['title' => 'Bonjour'],
        ],
    ]);
    
  • fallback_locale: Set to null to disable fallback entirely. Defaults to the first locale in locales.

  • default_locale: Override per-model:

    class Post extends Model
    {
        use Translatable;
        protected $defaultLocale = 'fr';
    }
    
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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony