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 Underscore Translatable Laravel Package

esign/laravel-underscore-translatable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require esign/laravel-underscore-translatable
    

    Publish the config (optional):

    php artisan vendor:publish --provider="Esign\UnderscoreTranslatable\UnderscoreTranslatableServiceProvider"
    
  2. First Use Case:

    • Add the trait to your model:
      use Esign\UnderscoreTranslatable\UnderscoreTranslatable;
      
      class Post extends Model
      {
          use UnderscoreTranslatable;
      
          public $translatable = ['title', 'content'];
      }
      
    • Run migrations to add language-specific columns (e.g., title_en, title_nl).
    • Use the 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"
      

Where to Look First

  • Config: config/underscore-translatable.php (default locale, fallback behavior).
  • Model Trait: UnderscoreTranslatable for core methods.
  • Migrations: Check database/migrations/ for auto-generated language columns.

Implementation Patterns

Core Workflows

  1. Defining Translatable Fields:

    public $translatable = ['title', 'description', 'meta_keywords'];
    
    • Only fields listed here will generate language-specific columns.
  2. 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']
    ]);
    
  3. 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')
    
  4. Dynamic Locale Handling:

    • Use middleware to set the current locale dynamically:
      // In a middleware
      app()->setLocale(request()->header('accept-language') ?? config('app.locale'));
      
    • Access current locale in models:
      $post->getTranslation('title'); // Uses app()->getLocale()
      
  5. Mass Assignment:

    • Enable mass assignment for translatable fields:
      protected $fillable = ['title', 'content'];
      
    • Automatically populates translations:
      $post = Post::create([
          'title_en' => 'Hello',
          'title_nl' => 'Hallo',
          'content_en' => 'Content...'
      ]);
      

Integration Tips

  • APIs:

    • Use setTranslations() to batch-save translations from API requests.
    • Example:
      $data = request()->all();
      $post->setTranslations($data);
      $post->save();
      
  • Forms:

    • Dynamically generate form fields for each locale:
      @foreach (config('underscore-translatable.locales') as $locale)
          <input name="title_{{ $locale }}" value="{{ old("title_{$locale}", $post->getTranslation('title', $locale)) }}">
      @endforeach
      
  • Relationships:

    • Translate related models:
      class Category extends Model
      {
          use UnderscoreTranslatable;
          public $translatable = ['name'];
      
          public function posts()
          {
              return $this->hasMany(Post::class);
          }
      }
      
  • Scopes:

    • Filter by translations:
      public function scopeInLanguage($query, $locale)
      {
          return $query->where("title_{$locale}", 'like', '%' . $search . '%');
      }
      

Gotchas and Tips

Pitfalls

  1. Migration Conflicts:

    • If manually adding columns, ensure they follow the naming convention (field_locale).
    • Example: title_en, content_nl (not title-en or title[en]).
  2. Default Locale Fallback:

    • If a translation is missing, the package falls back to the default locale (configured in config/underscore-translatable.php).
    • Tip: Override fallback behavior in the model:
      public function getTranslation($attribute, $locale = null, $default = null)
      {
          return parent::getTranslation($attribute, $locale, $default ?? 'fallback_value');
      }
      
  3. Case Sensitivity:

    • Locale codes are case-sensitive (e.g., ENen). Stick to lowercase (e.g., en, nl).
  4. Mass Assignment Risks:

    • Be cautious with $guarded or $fillable. Translatable fields like title_en may not be explicitly listed.
    • Fix: Whitelist all possible translatable columns:
      protected $fillable = [
          'title_en', 'title_nl', 'content_en', 'content_nl',
          // ... other fields
      ];
      
  5. Performance:

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

Debugging

  1. Missing Translations:

    • Check if the column exists in the database (e.g., title_en).
    • Verify the locale is correctly set (app()->getLocale()).
  2. SQL Errors:

    • Ensure the table has columns for all locales in $translatable.
    • Run a fresh migration if columns are missing.
  3. Locale Not Updating:

    • Clear the config cache:
      php artisan config:clear
      
    • Verify the locale is set before accessing translations.

Extension Points

  1. Custom Column Naming:

    • Override the default field_locale naming convention:
      use Esign\UnderscoreTranslatable\Concerns\GeneratesTranslationColumnNames;
      
      class CustomPost extends Post
      {
          use GeneratesTranslationColumnNames;
      
          protected $translationColumnPattern = '{{field}}_{{locale}}_custom';
      }
      
  2. Custom Fallback Logic:

    • Extend the trait to add logic for missing translations:
      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)
      }
      
  3. Dynamic Locale Detection:

    • Override the locale resolution:
      public function getCurrentLocale()
      {
          return request()->input('locale') ?: app()->getLocale();
      }
      
  4. Validation:

    • Use Laravel validation rules to ensure translations exist:
      $validator = Validator::make($data, [
          'title_en' => 'required|string',
          'title_nl' => 'required|string',
      ]);
      
  5. Events:

    • Listen for translation changes:
      public static function bootUnderscoreTranslatable()
      {
          static::saved(function ($model) {
              if ($model->wasRecentlyCreated) {
                  event(new TranslationsCreated($model));
              }
          });
      }
      
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.
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium