astrotomic/laravel-translatable

repository·main·Indexed 23 days ago

https://github.com/astrotomic/laravel-translatable

A Laravel package for managing multilingual Eloquent models. It automates the retrieval and storage of translations in the database, supporting explicit and implicit locale retrieval, translation wrappers for bulk updates, and hierarchical fallback logic for country-based locales. Features include built-in scopes for sorting by translated attributes and tools for migrating existing tables to a translatable structure.

Tokens
12.1K
Snippets
46
Records
73
Agent score
79%

What's inside laravel-translatable

  1. Use the Locales helper to manage available locales

    main

    The \Astrotomic\Translatable\Locales class is a singleton service that manages all locales available for translation. It can be accessed via the translatable.locales service container binding or by instantiating the class directly. It implements ArrayAccess, allowing it to be used like a standard array.

    Use this helper to check for, add, or remove locales at runtime without needing to modify the underlying configuration file.

  2. How country-based fallback logic works

    main

    When a translation for a country-specific locale (like es-MX) is missing, the package follows a hierarchical fallback pattern:

    1. It first attempts to find a translation for the base language (e.g., es).
    2. If no base language translation is found, it then falls back to the global fallback_locale (e.g., en).

    Example: If searching for es-MX fails, it checks es, then en.

  3. Understand the TranslatableProtected interface

    main

    The package relies on a set of protected methods that are considered part of its stable contract. Changes to these methods will trigger a major release. If you are extending the package's core functionality, be aware of these internal lifecycle methods:

    • isEmptyTranslatableAttribute(string $key, $value): bool: Detects if a specific translation attribute value is considered empty.
    • saveTranslations(): bool: Handles the persistence of all currently attached translations.
    interface TranslatableProtected
    {
        // detect if a given translation attribute value is empty or not
        protected function isEmptyTranslatableAttribute(string $key, $value): bool;
    
        // save all attached translations
        protected function saveTranslations(): bool;
    }
  4. Validate translated attributes with RuleFactory

    main

    When validating translated attributes, manually listing rules for every locale is complex. The RuleFactory class simplifies this by allowing you to use placeholders (e.g., %title%) in your rule keys. The factory automatically expands these placeholders into full dot-notation or colon-separated keys for all configured locales, including updating any rule strings (like required_with) to match the new locale-specific keys.

    Example usage:

    $rules = RuleFactory::make([
        'translations.%title%' => 'sometimes|string',
        'translations.%content%' => ['required_with:translations.%title%', 'string'],
    ]);
    
    $validatedData = $request->validate($rules);
  5. Configure locales for translations

    main

    First, publish the configuration file:

    php artisan vendor:publish --tag=translatable

    Then, define the locales your application should use in the published config file. You can use simple strings or nested arrays for specific regional variations (e.g., Mexican Spanish).

    There is no strict format for locale names; you can use any identifier (like en, eng, or es) as long as you are consistent.

    'locales' => [
        'en',
        'fr',
        'es' => [
            'MX', // mexican spanish
            'CO', // colombian spanish
        ],
    ],
  6. Implement translatable models

    main

    To make a model translatable, follow these steps:

    1. Implement the Astrotomic\Translatable\Contracts\Translatable interface.
    2. Use the Astrotomic\Translatable\Translatable trait.
    3. Define the $translatedAttributes array containing the names of the fields to be translated.
    4. Create a corresponding Translation model (following the convention {ModelName}Translation) that includes the translatable fields in its $fillable array and typically sets $timestamps = false.
    // Post.php
    use Astrotomic\Translatable\Contracts\Translatable as TranslatableContract;
    use Astrotomic\Translatable\Translatable;
    
    class Post extends Model implements TranslatableContract
    {
        use Translatable;
    
        public $translatedAttributes = ['title', 'content'];
        protected $fillable = ['author'];
    }
    
    // PostTranslation.php
    class PostTranslation extends Model
    {
        public $timestamps = false;
        protected $fillable = ['title', 'content'];
    }
  7. Set up database migrations for translations

    main

    To implement translations, you need two tables: the main model table and a dedicated translations table. The translations table must have a unique constraint on the combination of the foreign key and the locale column.

    Example for a Post model:

    1. Main table (posts): Contains non-translatable attributes like author.
    2. Translations table (post_translations): Contains the locale index, the foreign key (e.g., post_id), and the translatable fields (e.g., title, content).
    // create_posts_table.php
    Schema::create('posts', function(Blueprint $table) {
        $table->increments('id');
        $table->string('author');
        $table->timestamps();
    });
    
    // create_post_translations_table.php
    Schema::create('post_translations', function(Blueprint $table) {
        $table->increments('id');
        $table->integer('post_id')->unsigned();
        $table->string('locale')->index();
        $table->string('title');
        $table->text('content');
    
        $table->unique(['post_id', 'locale']);
        $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
    });
  8. Structure form inputs using colon notation for translations

    main

    Alternatively, you can use colon notation for your input names. In this syntax, the attribute name comes first, followed by the locale. This is another way to structure requests that can be handled by the package.

    <input type="text" name="title:en" />
    <input type="text" name="title:de" />
  9. Translate multiple fields with a single form using array syntax

    main

    To translate multiple fields across different locales in a single operation, use the overridden fill() method. This method accepts an associative array where the first level of keys represents the locales, and the second level contains the translated attributes for that specific locale.

    This approach is ideal for multi-language forms where you want to save all translations at once.

    $post->fill([
      'en' => [
        'title' => 'My first edited post',
      ],
      'de' => [
        'title' => 'Mein erster bearbeiteter Beitrag',
      ],
    ]);
  10. Use the Translatable trait on Pivot models

    main

    You can use the Translatable trait on Laravel pivot models to allow intermediate table data to be translatable.

    Requirements:

    • The pivot model must extend Illuminate\Database\Eloquent\Relations\Pivot.
    • The pivot model must implement Astrotomic\Translatable\Contracts\Translatable.
    • Because the trait introduces a new relationship, your pivot model must have a primary key. By default, it is recommended to use an auto-incrementing id column. If you use a UUID or a different key type, you must manually configure the model and the trait to recognize that key.

    To use an auto-incrementing ID, ensure $incrementing = true is set on the model.

    use Illuminate\Database\Eloquent\Relations\Pivot;
    use Astrotomic\Translatable\Contracts\Translatable as TranslatableContract;
    use Astrotomic\Translatable\Translatable;
    
    class RoleUser extends Pivot implements TranslatableContract
    {
        use Translatable;
    
        public $incrementing = true;
    }