weboccult/laravel-translatable

A Laravel package for easy model translations using morph relationships. Supports multiple languages, caching, validation, and automatic translation loading.

v1.0.0 2025-06-01 09:12 UTC

This package is not auto-updated.

Last update: 2025-06-07 09:51:07 UTC


README

A Laravel package for easy model translations using morph relationships. This package allows you to add multilingual support to your Laravel models using a simple trait.

Installation

You can install the package via composer:

composer require weboccult/laravel-translatable

After installing the package, publish the configuration and migration files:

php artisan vendor:publish --provider="WebOccult\LaravelTranslatable\TranslatableServiceProvider"

Then run the migrations:

php artisan migrate

Basic Setup

  1. Add the HasTranslations trait to your model and specify translatable attributes:
use WebOccult\LaravelTranslatable\Traits\HasTranslations;

class Department extends Model
{
    use HasTranslations;

    protected $fillable = [
        'code'  // non-translatable field
    ];

    // Define which attributes can be translated
    protected $translatable = [
        'name',
        'description',
        'address'
    ];
}

Usage Examples

1. Creating and Setting Translations

// Create a new department
$department = Department::create([
    'code' => 'IT-001'
]);

// Method 1: Set translations one by one
$department->setTranslation('name', 'Information Technology', 'en');
$department->setTranslation('name', 'Technologies de l\'information', 'fr');
$department->setTranslation('name', 'Tecnología de la Información', 'es');

// Method 2: Set multiple translations at once
$department->updateTranslations([
    'name' => [
        'en' => 'Information Technology',
        'fr' => 'Technologies de l\'information',
        'es' => 'Tecnología de la Información'
    ],
    'description' => [
        'en' => 'Main IT Department',
        'fr' => 'Département principal des TI',
        'es' => 'Departamento Principal de TI'
    ]
]);

// Method 3: Set multiple fields for same locale
$department->setTranslation(
    ['name', 'description'],
    ['IT Department', 'Handles all IT operations'],
    'en'
);

2. Retrieving Translations

// Method 1: Get single translation
$name = $department->getTranslation('name', 'en');
// Output: "Information Technology"

// Method 2: Get all translations for an attribute
$allNames = $department->getAllTranslations('name');
// Output:
// [
//     'en' => 'Information Technology',
//     'fr' => 'Technologies de l\'information',
//     'es' => 'Tecnología de la Información'
// ]

// Method 3: Load with translations for current locale
$departments = Department::withTranslations()->get();
foreach ($departments as $dept) {
    echo $dept->getTranslation('name'); // Uses current locale
}

// Method 4: Load with all translations
$department = Department::with('translations')->find(1);
$translations = $department->translations->groupBy('locale');
// Output:
// [
//     'en' => [
//         ['attribute' => 'name', 'value' => 'Information Technology'],
//         ['attribute' => 'description', 'value' => 'Main IT Department']
//     ],
//     'fr' => [
//         ['attribute' => 'name', 'value' => 'Technologies de l\'information'],
//         ['attribute' => 'description', 'value' => 'Département principal des TI']
//     ]
// ]

3. Querying with Translations

// Load models with translations for specific locale
$departments = Department::query()
    ->with(['translations' => function($query) {
        $query->where('locale', 'en')
              ->whereIn('attribute', ['name', 'description']);
    }])
    ->get();

// Format for API response
$formatted = $departments->map(function ($department) {
    return [
        'id' => $department->id,
        'code' => $department->code,
        'name' => $department->getTranslation('name', 'en'),
        'description' => $department->getTranslation('description', 'en')
    ];
});
// Output:
// [
//     {
//         "id": 1,
//         "code": "IT-001",
//         "name": "Information Technology",
//         "description": "Main IT Department"
//     }
// ]

4. Practical Examples

Example 1: Controller with Translation Handling

class DepartmentController extends Controller
{
    public function store(Request $request)
    {
        $department = Department::create([
            'code' => $request->code
        ]);

        // Handle translations from form
        // Input format:
        // translations = [
        //     'name' => [
        //         'en' => 'English Name',
        //         'fr' => 'French Name'
        //     ],
        //     'description' => [
        //         'en' => 'English Description',
        //         'fr' => 'French Description'
        //     ]
        // ]
        $department->updateTranslations($request->translations);

        return response()->json([
            'department' => $department,
            'translations' => $department->translations
        ]);
    }

    public function index()
    {
        // Get departments with translations for current locale
        $departments = Department::withTranslations()
            ->get()
            ->map(function ($department) {
                return [
                    'id' => $department->id,
                    'code' => $department->code,
                    'name' => $department->getTranslation('name'),
                    'description' => $department->getTranslation('description')
                ];
            });

        return view('departments.index', compact('departments'));
    }
}

Example 2: Blade View Usage

<!-- departments/index.blade.php -->
@foreach($departments as $department)
    <div class="department-card">
        <h3>{{ $department->getTranslation('name') }}</h3>
        <p>{{ $department->getTranslation('description') }}</p>
        
        <!-- Show all available translations -->
        <div class="translations">
            @foreach($department->getAllTranslations('name') as $locale => $name)
                <span class="translation">
                    {{ $locale }}: {{ $name }}
                </span>
            @endforeach
        </div>
    </div>
@endforeach

Example 3: API Resource

class DepartmentResource extends JsonResource
{
    public function toArray($request)
    {
        $locale = $request->get('locale', app()->getLocale());
        
        return [
            'id' => $this->id,
            'code' => $this->code,
            'translations' => [
                'name' => $this->getAllTranslations('name'),
                'description' => $this->getAllTranslations('description')
            ],
            'current_locale' => [
                'name' => $this->getTranslation('name', $locale),
                'description' => $this->getTranslation('description', $locale)
            ]
        ];
    }
}

// Output:
// {
//     "id": 1,
//     "code": "IT-001",
//     "translations": {
//         "name": {
//             "en": "Information Technology",
//             "fr": "Technologies de l'information",
//             "es": "Tecnología de la Información"
//         },
//         "description": {
//             "en": "Main IT Department",
//             "fr": "Département principal des TI",
//             "es": "Departamento Principal de TI"
//         }
//     },
//     "current_locale": {
//         "name": "Information Technology",
//         "description": "Main IT Department"
//     }
// }

Features

  • Easy integration with Laravel models
  • Support for multiple translatable attributes
  • Efficient relationship-based translation loading
  • Flexible query building for translations
  • Support for multiple languages
  • Bulk translation operations
  • Automatic fallback to model attributes

Configuration

You can configure the package by editing the config/translatable.php file:

return [
    'default_locale' => 'en',
    'fallback_locale' => 'en',
    'available_locales' => [
        'en' => 'English',
        'fr' => 'French',
        'es' => 'Spanish',
    ],
];

License

The MIT License (MIT). Please see License File for more information.