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

Models Laravel Package

laravel-lang/models

Adds localized names and attributes for Laravel Eloquent models via Laravel Lang. Plug-and-play translations for model labels across multiple languages to improve UI, validation messages, and admin panels. Install with composer and follow the docs for setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require laravel-lang/models
    

    Publish the package assets (if needed):

    php artisan vendor:publish --provider="LaravelLang\Models\ServiceProvider"
    
  2. Generate a Translatable Model:

    php artisan make:model-translation Post -t en,es,fr
    

    This creates:

    • Post (parent model) with HasTranslations trait.
    • PostTranslation (translation model) with locale, title, body, etc.
  3. First Use Case:

    // Create a post with translations
    $post = Post::create([
        'title' => 'Hello World', // Default locale (e.g., 'en')
        'translations' => [
            'es' => ['title' => 'Hola Mundo'],
            'fr' => ['title' => 'Bonjour le Monde']
        ]
    ]);
    
    // Retrieve translations
    $post->getTranslation('es')->title; // "Hola Mundo"
    

Key Files to Review

  • Traits: HasTranslations (core functionality).
  • Scopes: orderByTranslation, whereTranslation (query filtering).
  • Console Commands: make:model-translation (model generation).

Implementation Patterns

Core Workflow: Model Localization

  1. Define Translatable Fields: Use the translatable array in your model to specify fields for translation:

    protected $translatable = ['title', 'description'];
    
  2. Create/Update Translations:

    // Mass assignment with translations
    $post = Post::create([
        'title' => 'Default Title',
        'translations' => [
            'es' => ['title' => 'Título en Español'],
            'fr' => ['title' => 'Titre en Français']
        ]
    ]);
    
    // Update a single translation
    $post->setTranslation('es', ['title' => 'Nuevo Título']);
    
  3. Querying Translations:

    // Get a specific locale's translation
    $post->getTranslation('es')->title;
    
    // Query with translation scopes
    Post::whereTranslation('title', 'like', '%Hola%')->get();
    Post::orderByTranslation('title', 'asc')->get();
    
  4. Eager Loading:

    // Load translations for multiple posts
    $posts = Post::withTranslations(['en', 'es'])->get();
    

Integration Tips

  • Fallback Logic: Use getTranslation() with a fallback locale:

    $post->getTranslation('es')->title ?? $post->getTranslation('en')->title;
    
  • Dynamic Locales: Set the current locale dynamically (e.g., via middleware):

    app()->setLocale(request()->header('Accept-Language') ?? 'en');
    
  • API Responses: Serialize translations in API responses:

    return PostResource::collection($posts)->additional([
        'translations' => $posts->map->getTranslations()
    ]);
    
  • Admin Panels: Integrate with tools like Nova or Filament for translation management:

    // Nova Tool Example
    Nova::serving(function () {
        Nova::resources([
            new PostResource(),
        ]);
    });
    
  • Testing: Use the LocaleData type for type-safe tests:

    use LaravelLang\Models\LocaleData;
    
    public function test_translations()
    {
        $post = Post::create([
            'translations' => [
                'es' => ['title' => 'Test']
            ] as LocaleData
        ]);
    }
    

Gotchas and Tips

Pitfalls

  1. Locale Column Mismatch:

    • Ensure your PostTranslation model’s locale column matches the expected format (e.g., en, es). The package defaults to locale, but custom names require configuration:
      protected $localeColumn = 'language_code';
      
  2. Eager Loading Quirks:

    • If translations aren’t loading, verify the withTranslations() method is called before get():
      // Wrong: No translations loaded
      $posts = Post::all()->withTranslations(['en']);
      
      // Correct
      $posts = Post::withTranslations(['en'])->get();
      
  3. Mass Assignment Risks:

    • The fill() method may not handle nested translations correctly. Use create() or update() with explicit arrays:
      // Avoid:
      $post->fill(['translations' => ['es' => ['title' => 'Test']]]);
      
      // Prefer:
      $post->setTranslation('es', ['title' => 'Test']);
      
  4. Migration Conflicts:

    • If manually creating PostTranslation, ensure the locale column is defined as a string (not enum or integer).
  5. Caching Issues:

    • Clear cached configurations if translations appear stale:
      php artisan config:clear
      

Debugging Tips

  • Check Translation Existence:

    if ($post->hasTranslation('es')) {
        // Translation exists
    }
    
  • Log Translation Queries: Enable Eloquent logging to debug queries:

    DB::enableQueryLog();
    $post->getTranslation('es');
    dd(DB::getQueryLog());
    
  • Validate Model Structure: Use the lang:models-helper command to generate IDE helpers:

    php artisan lang:models-helper
    

Extension Points

  1. Custom Translation Storage: Override the translationModelName() method to use a custom translation model:

    public function translationModelName()
    {
        return 'CustomPostTranslation';
    }
    
  2. Add Translation Scopes: Extend the HasTranslations trait to add custom scopes:

    use LaravelLang\Models\Scopes\TranslationScope;
    
    public function scopeActiveTranslations($query)
    {
        return $query->whereHas('translations', function ($q) {
            $q->where('active', true);
        });
    }
    
  3. Modify Fallback Logic: Override getTranslation() to implement custom fallback behavior:

    public function getTranslation($locale, $fallback = null)
    {
        $translation = parent::getTranslation($locale);
        return $translation ?: $this->getTranslation($fallback ?? 'en');
    }
    
  4. Locale-Specific Validation: Use the LocaleData type in validation rules:

    use LaravelLang\Models\LocaleData;
    
    public function rules()
    {
        return [
            'translations.es.title' => 'required|string',
            'translations.fr.title' => 'nullable|string',
        ];
    }
    

Performance Optimizations

  • Batch Translation Updates: Use updateTranslations() for bulk updates:

    $posts->each(function ($post) {
        $post->updateTranslations([
            'es' => ['title' => 'Updated Title']
        ]);
    });
    
  • Query Optimization: Limit loaded translations with withTranslations() to avoid N+1 queries:

    $posts = Post::withTranslations(['en'])->limit(100)->get();
    
  • Caching Translations: Cache frequently accessed translations (e.g., product names):

    $title = Cache::remember("post:{$post->id}:title:en", now()->addHours(1), function () {
        return $post->getTranslation('en')->title;
    });
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity