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

novius/laravel-translatable

Make Laravel Eloquent models translatable using locale and locale_parent_id. Includes migration macro, Translatable trait, relations for translations (with/without soft-deleted), translate() and getTranslation(), plus withLocale() query scope. Supports Laravel 10–13, PHP 8.2–8.5.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require novius/laravel-translatable
    

    Publish language files (optional):

    php artisan vendor:publish --provider="Novius\LaravelTranslatable\LaravelTranslatableServiceProvider" --tag=lang
    
  2. Migration Setup: Add the translatable() macro to your table schema:

    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->translatable(); // Adds `locale` and `locale_parent_id` columns
        $table->string('title');
        $table->text('content');
        $table->timestamps();
    });
    
  3. Model Integration: Use the Translatable trait in your Eloquent model:

    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    use Novius\LaravelTranslatable\Traits\Translatable;
    
    class Post extends Model
    {
        use Translatable;
    }
    
  4. First Translation: Create a model instance and translate it to a new locale:

    $post = new Post(['title' => 'Titre en Français']);
    $post->save();
    
    $post->translate('en', ['title' => 'English Title']);
    

Where to Look First

  • README.md: Focus on the Usage section for quick implementation.
  • Migrations: Check the translatable() macro for column requirements.
  • Trait Methods: Review translate(), getTranslation(), and translations relation for core functionality.

First Use Case

Localizing a Blog Post:

// Create a post in French
$post = Post::create(['title' => 'Mon Article']);

// Add English translation
$post->translate('en', ['title' => 'My Article']);

// Retrieve English translation
$englishPost = $post->getTranslation('en');

// Query posts with English translations
$englishPosts = Post::withLocale('en')->get();

Implementation Patterns

Usage Patterns

  1. Basic Translation Workflow:

    • Create: Save parent record, then translate to new locales.
    • Read: Use getTranslation($locale) or translations relation.
    • Update: Pass attributes to translate() method.
    • Delete: Soft deletes (if using SoftDeletes) require handling translationsWithDeleted.
  2. Querying Translations:

    • Scope: Use withLocale($locale) for query builder:
      $posts = Post::withLocale('es')->get();
      
    • Eager Loading: Load translations with with('translations'):
      $posts = Post::with('translations')->get();
      
  3. Customizing Behavior:

    • Restrict Locales: Override translatableConfig():
      public function translatableConfig(): TranslatableModelConfig
      {
          return new TranslatableModelConfig(
              ['en', 'fr', 'es'], // Allowed locales
              'language_code',    // Custom locale column name
              'parent_id'         // Custom parent ID column name
          );
      }
      
    • Pre-Translate Attributes: Override translateAttributes():
      protected function translateAttributes($parent): void
      {
          $this->slug = Str::slug($parent->title . ' ' . $this->locale);
      }
      
  4. Handling Fallbacks:

    • Manually implement fallback logic in getTranslation() or use a service layer:
      public function getFallbackTranslation(Post $post, string $locale)
      {
          return $post->getTranslation(config('app.fallback_locale')) ?? $post;
      }
      

Workflows

  1. Content Management System (CMS):

    • Use translatable for localized pages, menus, or media.
    • Example: Page::with('translations')->find($id) to fetch all language versions.
  2. E-Commerce:

    • Translate product titles, descriptions, and metadata:
      $product = Product::find($id);
      $product->translate('de', ['name' => 'Deutscher Name']);
      
  3. User-Generated Content:

    • Allow users to submit content in their preferred language:
      $comment = new Comment(['body' => 'Comment in User\'s Locale']);
      $comment->translate($user->locale, ['body' => $translatedBody]);
      

Integration Tips

  1. API Resources:

    • Use Laravel’s API resources to structure translated responses:
      public function toArray($request)
      {
          return [
              'id' => $this->id,
              'translations' => $this->translations->map(fn($t) => [
                  'locale' => $t->locale,
                  'title' => $t->title,
              ]),
          ];
      }
      
  2. Form Requests:

    • Validate translations in form requests:
      public function rules()
      {
          return [
              'title_en' => 'required|string',
              'title_fr' => 'required|string',
          ];
      }
      
  3. Observers:

    • Trigger actions on translation events:
      Post::observe(TranslatableObserver::class);
      
      class TranslatableObserver
      {
          public function saved(Post $post)
          {
              if ($post->wasRecentlyCreated) {
                  $post->translate('en', $post->attributes);
              }
          }
      }
      
  4. Testing:

    • Test translation logic with PHPUnit:
      public function testTranslationCreation()
      {
          $post = Post::create(['title' => 'Original']);
          $post->translate('es', ['title' => 'Traducción']);
      
          $this->assertDatabaseHas('posts', [
              'title' => 'Traducción',
              'locale' => 'es',
              'locale_parent_id' => $post->id,
          ]);
      }
      

Gotchas and Tips

Pitfalls

  1. Table Bloat:

    • Issue: Storing all translations in a single table can lead to performance issues with many languages.
    • Fix: Monitor table size; consider archiving old translations or using a separate table for large datasets.
  2. Orphaned Translations:

    • Issue: Deleting a parent record leaves orphaned translations unless using SoftDeletes.
    • Fix: Override delete() to handle translations:
      public function delete()
      {
          $this->translations->each->delete();
          parent::delete();
      }
      
  3. Locale Conflicts:

    • Issue: Duplicate locale_parent_id values can cause ambiguity.
    • Fix: Ensure locale_parent_id is unique per locale or use composite keys.
  4. Query Performance:

    • Issue: Queries with with('translations') can be slow for large datasets.
    • Fix: Use with(['translations' => function($query) { $query->where('locale', $locale); }]) to limit translations.
  5. Soft Deletes:

    • Issue: translationsWithDeleted may return soft-deleted records unexpectedly.
    • Fix: Explicitly filter soft-deleted translations if needed:
      $translations = $post->translationsWithDeleted()->whereNull('deleted_at');
      

Debugging

  1. Missing Translations:

    • Debug: Check if the translation exists in the database:
      $translation = Post::where('locale_parent_id', $post->id)
          ->where('locale', 'es')
          ->first();
      
    • Fix: Ensure translate() is called with correct attributes.
  2. Locale Parent ID Mismatch:

    • Debug: Verify locale_parent_id points to the correct parent:
      $parent = Post::find($post->locale_parent_id);
      
    • Fix: Reset locale_parent_id if corrupted:
      $post->translations->each(function($t) {
          $t->locale_parent_id = $post->id;
          $t->save();
      });
      
  3. Query Builder Issues:

    • Debug: Use toSql() to inspect generated queries:
      $query = Post::withLocale('fr');
      dd($query->toSql(), $query->getBindings());
      
    • Fix: Ensure the withLocale scope is correctly implemented in the trait.

Config Quirks

  1. Default Locale:

    • Issue: No default locale is set globally.
    • Fix: Set it in AppServiceProvider:
      public function boot()
      {
          Translatable::setDefaultLocale('en');
      }
      
  2. Column Naming:

    • Issue: Hardcoded column names (locale, locale_parent_id) may conflict with existing columns.
    • Fix: Override translatableConfig() to use custom names.
  3. Attribute Whitelisting:

    • **Issue
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.
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
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