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 fields. Provides migration macro, Translatable trait with translations relations (incl. soft-deleted), translate/getTranslation helpers, and withLocale query scope. Supports Laravel 10–13, PHP 8.2–8.5.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require novius/laravel-translatable
    
  2. Add the translatable() macro to your migration:
    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. Apply the trait to your model:
    use Novius\LaravelTranslatable\Traits\Translatable;
    
    class Post extends Model {
        use Translatable;
    }
    
  4. Create a translation:
    $post = new Post(['title' => 'Titre Français']);
    $post->save();
    
    $post->translate('en', ['title' => 'English Title']);
    
  5. Retrieve a translation:
    $englishPost = $post->getTranslation('en');
    

First Use Case: Localized Blog Posts

  • Use the withLocale() scope to fetch posts in a specific language:
    $posts = Post::withLocale('es')->get();
    
  • Dynamically switch languages in your frontend by passing the locale to getTranslation().

Implementation Patterns

Core Workflows

1. Translation Creation

  • Basic Translation:
    $post->translate('de', ['title' => 'Deutscher Titel']);
    
  • Bulk Translation:
    $post->translate('ja', [
        'title' => '日本語のタイトル',
        'content' => '日本語のコンテンツ...'
    ]);
    
  • Custom Attributes: Override translateAttributes() to transform data before saving:
    protected function translateAttributes($parent): void {
        $this->slug = Str::slug($parent->title . ' ' . $this->locale);
    }
    

2. Translation Retrieval

  • Get a Specific Translation:
    $translation = $post->getTranslation('fr');
    
  • Query with Locale Scope:
    $posts = Post::withLocale('pt')->get();
    
  • Include Soft-Deleted Translations:
    $translation = $post->getTranslation('en', true); // Includes soft-deleted
    

3. Handling Fallbacks

  • Implement fallback logic manually (not built-in):
    $translation = $post->getTranslation($locale) ?: $post->getTranslation('en');
    

4. Restricting Locales

  • Limit allowed locales via translatableConfig():
    public function translatableConfig(): TranslatableModelConfig {
        return new TranslatableModelConfig(
            ['en', 'fr', 'es'], // Only these locales
            'locale',
            'locale_parent_id'
        );
    }
    

Integration Tips

Admin Panel Integration

  • Use the translations relation to list all translations in a dropdown:
    $translations = $post->translations->pluck('locale', 'id');
    
  • Dynamically render translation fields based on the current locale.

API Endpoints

  • Create endpoints to fetch translations:
    Route::get('/posts/{post}/translations/{locale}', function (Post $post, $locale) {
        return $post->getTranslation($locale) ?: abort(404);
    });
    
  • Use withLocale() in API queries to filter results by language.

Frontend Dynamic Loading

  • Load translations via JavaScript based on user preference:
    fetch(`/posts/${postId}/translations/${userLocale}`)
        .then(response => response.json())
        .then(data => renderPost(data));
    

Caching Translations

  • Cache translations to reduce database load:
    $translation = Cache::remember(
        "post.{$post->id}.locale.{$locale}",
        now()->addHours(1),
        fn() => $post->getTranslation($locale)
    );
    

Gotchas and Tips

Pitfalls

  1. Orphaned Translations on Soft Delete

    • If the parent model uses SoftDeletes, translations may become orphaned. Mitigate by:
      • Using translationsWithDeleted to fetch them.
      • Adding a deleted_at column to translations or cascading deletes.
  2. Performance with Many Locales

    • Querying translations for models with 50+ locales can be slow. Optimize with:
      $post->translations()->where('locale', $desiredLocale)->first();
      
    • Add indexes to locale and locale_parent_id:
      Schema::table('posts_translations', function (Blueprint $table) {
          $table->index(['locale', 'locale_parent_id']);
      });
      
  3. Missing Fallback Logic

    • The package doesn’t auto-fallback to a default locale. Implement this in your application:
      public function getTranslationOrFallback(string $locale): ?Model {
          return $this->getTranslation($locale) ?: $this->getTranslation('en');
      }
      
  4. No Translation History

    • Changes to translations aren’t tracked. Consider adding:
      • A versions table for history.
      • Laravel’s audit packages (e.g., owen-it/auditing) for tracking.
  5. AGPL License Restrictions

    • The AGPL-3.0 license may require open-sourcing your project. Evaluate alternatives like spatie/laravel-translatable (MIT) if proprietary code is involved.

Debugging Tips

  1. Check for Orphaned Records

    • Run a query to find translations without parents:
      SELECT * FROM posts_translations WHERE locale_parent_id NOT IN (SELECT id FROM posts);
      
  2. Verify Locale Configuration

    • Ensure translatableConfig() is correctly set if you override it. Test with:
      $post->translate('invalid_locale', [...]); // Should fail if restricted
      
  3. Debug Soft Deletes

    • If translations disappear after parent soft delete, check:
      • Whether translationsWithDeleted is used.
      • If the deleted_at column exists on the translations table.
  4. Query Performance

    • Use Laravel Debugbar or DB::enableQueryLog() to analyze slow queries:
      $post->translations; // Check the generated SQL
      

Extension Points

  1. Add Translation Events

    • Listen for translation creation/updates:
      $post->translations()->created(function ($translation) {
          // Log or notify
      });
      
  2. Custom Validation

    • Validate translations before saving:
      protected function translateAttributes($parent): void {
          $this->validate([
              'title' => ['required', 'max:255'],
              'content' => ['required', 'max:10000'],
          ]);
      }
      
  3. Translation Scopes

    • Extend the withLocale scope for complex queries:
      public function scopeWithLocaleActive($query, $locale) {
          return $query->withLocale($locale)->where('published', true);
      }
      
  4. Bulk Translation Tools

    • Create a command to bulk-translate existing records:
      $posts = Post::where('locale', 'fr')->get();
      foreach ($posts as $post) {
          $post->translate('en', ['title' => __($post->title)]);
      }
      
  5. Integration with Localization Packages

    • Combine with laravel-localization for route/locale switching:
      use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
      
      $locale = LaravelLocalization::getCurrentLocale();
      $post = Post::withLocale($locale)->find($id);
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle