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

Nova Translatable Laravel Package

spatie/nova-translatable

Make any Laravel Nova field translatable with a simple Translatable wrapper. Works with spatie/laravel-translatable to store per-locale values in a JSON column, rendering locale tabs for editing. Requires Nova 4/5 and MySQL 5.7.8+.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/nova-translatable
    

    Publish the config (if needed):

    php artisan vendor:publish --provider="Spatie\NovaTranslatable\NovaTranslatableServiceProvider"
    
  2. First Use Case: Wrap any Nova field(s) in Translatable::make() within your Nova resource's fields() method:

    use Spatie\NovaTranslatable\Translatable;
    
    public function fields(Request $request)
    {
        return [
            ID::make(),
            Translatable::make([
                Text::make('title', 'Title'),
                Text::make('description', 'Description'),
            ]),
        ];
    }
    
  3. Database Migration: Ensure your model uses Spatie\Translatable\HasTranslations trait and has a translations table. Example migration:

    php artisan make:migration create_post_translations_table
    
    Schema::create('post_translations', function (Blueprint $table) {
        $table->string('locale')->index();
        $table->text('title')->nullable();
        $table->text('description')->nullable();
        $table->unsignedBigInteger('post_id');
        $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
    });
    
  4. Model Setup:

    use Spatie\Translatable\HasTranslations;
    
    class Post extends Model
    {
        use HasTranslations;
    
        public $translatable = ['title', 'description'];
    }
    

Implementation Patterns

Common Workflows

  1. Basic Translation Handling: Use Translatable::make() to group fields that need translation. The package automatically handles:

    • Locale selection dropdown in Nova.
    • Dynamic field rendering based on selected locale.
    • Persistence of translations to the translations table.
    Translatable::make([
        Text::make('name'),
        Textarea::make('bio'),
        BelongsTo::make('Author'), // Non-translatable fields can coexist
    ]);
    
  2. Conditional Translations: Use onlyOnDetail() or onlyOnIndex() to control when translations appear:

    Translatable::make([
        Text::make('title'),
    ])->onlyOnDetail();
    
  3. Default Locale: Set a default locale in config/nova-translatable.php or per-resource:

    Translatable::make([...])->defaultLocale('en');
    
  4. Nested Translatable Fields: Combine with other Spatie packages (e.g., nova-relationships) for nested translations:

    Translatable::make([
        BelongsTo::make('Category', 'category'),
        Text::make('translated_name'),
    ]);
    
  5. Customizing the UI: Override the default locale selector or add custom logic via service provider:

    Nova::serving(function () {
        Nova::translatable()->localeField('language_code');
    });
    
  6. Bulk Actions: Use Translatable::make() with NovaTable for bulk editing translations:

    public function fields(Request $request)
    {
        return [
            Translatable::make([
                Text::make('title'),
            ])->bulkActions(['edit']),
        ];
    }
    

Integration Tips

  1. Laravel Scout: If using Scout for search, ensure translations are indexed:

    public function toSearchableArray()
    {
        return array_merge(
            parent::toSearchableArray(),
            $this->getTranslations()->pluck('title')->toArray()
        );
    }
    
  2. Nova Filters: Filter translatable fields by locale:

    public function filters(Request $request)
    {
        return [
            new Filter(
                Nova::translatable()->availableLocales(),
                'locale'
            ),
        ];
    }
    
  3. Nova Actions: Create actions to export translations:

    use Spatie\NovaTranslatable\Actions\ExportTranslations;
    
    public function actions(Request $request)
    {
        return [
            (new ExportTranslations)->withLocales(['en', 'es']),
        ];
    }
    
  4. Nova Tool Integration: Extend existing tools (e.g., NovaTool) to support translations:

    public function tool()
    {
        return new class extends NovaTool
        {
            public function fields()
            {
                return [
                    Translatable::make([
                        Text::make('content'),
                    ]),
                ];
            }
        };
    }
    

Gotchas and Tips

Pitfalls

  1. Missing Translations Table:

    • Error: Column not found: 1054 Unknown column 'locale' in 'where clause'.
    • Fix: Run migrations for the translations table and ensure the locale column exists.
  2. Locale Mismatch:

    • Error: Translations not saving or loading.
    • Fix: Verify config/app.php locales match those in the translations table. Use:
      Nova::translatable()->locales(['en', 'es', 'fr']);
      
  3. Caching Issues:

    • Error: Changes to translations not reflecting in Nova.
    • Fix: Clear Nova cache:
      php artisan nova:cache-clear
      
  4. Non-Translatable Fields in Group:

    • Issue: Fields like BelongsTo or HasMany may not render correctly.
    • Fix: Exclude non-translatable fields or handle them separately:
      Translatable::make([
          Text::make('title'),
      ])->except([
          BelongsTo::make('Author'), // This will be ignored
      ]);
      
  5. Mass Assignment Risks:

    • Issue: Unauthorized mass assignment of translations.
    • Fix: Whitelist translatable fields in your model:
      protected $fillable = ['title', 'description'];
      public $translatable = ['title', 'description'];
      

Debugging Tips

  1. Check Database: Inspect the translations table for missing or malformed data:

    SELECT * FROM translations WHERE post_id = 1;
    
  2. Log Locale Selection: Add debug logs to track locale changes:

    Nova::translatable()->onLocaleSelected(function ($locale) {
        \Log::info("Locale selected: {$locale}");
    });
    
  3. Validate Field Names: Ensure field names in Translatable::make() match the $translatable array in your model.

  4. Test with a Single Locale: Temporarily restrict to one locale to isolate issues:

    Translatable::make([...])->locales(['en']);
    

Extension Points

  1. Custom Locale Provider: Override the default locale provider in your service provider:

    Nova::translatable()->useLocaleProvider(function () {
        return ['en', 'de', 'ja'];
    });
    
  2. Dynamic Field Resolution: Use closures to dynamically resolve translatable fields:

    Translatable::make(function () {
        return [
            Text::make('title'),
            $this->showCustomField() ? Text::make('custom_field') : null,
        ];
    });
    
  3. Custom Storage Engine: Extend the package to use a different storage backend (e.g., Redis) by publishing and modifying the service provider.

  4. Nova Field Extensions: Add custom methods to existing Nova fields for translation-specific logic:

    Text::make('title')->withMeta([
        'translatable' => true,
        'fallback_locale' => 'en',
    ]);
    
  5. Event Listeners: Listen for translation events to trigger side effects:

    Nova::translatable()->onSaving(function ($resource, $locale, $fields) {
        // Log or process fields before save
    });
    
  6. Fallback Logic: Implement custom fallback behavior for missing translations:

    Nova::translatable()->fallbackLocale('en');
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport