dilbrinazad/laravel-multi-lang

A Laravel package for multilingual support

1.0.0 2025-03-05 21:07 UTC

This package is auto-updated.

Last update: 2025-07-07 21:58:37 UTC


README

Laravel MultiLang is a powerful and flexible package designed to seamlessly integrate multi-language support into your Laravel application. This package enables efficient handling of multiple languages, content translation, and language switching in a structured and scalable manner.

Table of Contents

  1. Installation
  2. Configuration
  3. Usage
  4. Contributing
  5. License

Installation

Step 1: Install via Composer

Run the following command to install the package:

composer require dilbrinazad/laravel-multi-lang

This will add the package to your Laravel project and make it available for use.

Configuration

After installation, publish the package resources using:

php artisan vendor:publish --provider="DilbrinAzad\LaravelMultiLang\Providers\LaravelMultiLangServiceProvider" --tag="multilang-resources"

This will publish the following resources:

  • Configuration File: config/multilang.php
  • Database Migrations for language management
  • Seeders for preloading language data
  • Models for multilingual entities
  • Controllers for language handling
  • Traits for reusable multilingual functionality
  • View Components for easy language selection and input

Usage

Database Migrations

Run the migration command to create the necessary tables:

php artisan migrate

This will generate three tables:

languages Table

Schema::create('languages', function (Blueprint $table) {
    $table->id();
    $table->string('title'); // Display name of the language
    $table->string('name'); // Language name in English
    $table->string('country_code'); // ISO country code
    $table->string('dir'); // Text direction ('ltr' or 'rtl')
    $table->string('locale')->unique(); // Unique language identifier
    $table->boolean('status')->default(1); // Active or inactive status
    $table->timestamps();
});

language_texts Table

Schema::create('language_texts', function (Blueprint $table) {
    $table->id();
    $table->text('default_content'); // Original text before translation
    $table->timestamps();
});

language_translations Table

Schema::create('language_translations', function (Blueprint $table) {
    $table->id();
    $table->foreignId('language_text_id')->constrained('language_texts')->onDelete('cascade');
    $table->foreignId('language_id')->constrained('languages')->onDelete('cascade');
    $table->text('content'); // Translated content
    $table->timestamps();
});

Seeding Languages

You can seed default languages by running:

php artisan db:seed --class=LanguageSeeder

By default, the following languages are included:

$languages = [
    ['title' => 'English', 'name' => 'English', 'country_code' => 'US', 'dir' => 'ltr', 'locale' => 'en', 'status' => 1],
    ['title' => 'Kurdish', 'name' => 'Kurdish', 'country_code' => 'KU', 'dir' => 'rtl', 'locale' => 'ku', 'status' => 1],
];

You can modify this list to include any language you need.

Model Structure

Language Model

class Language extends Model {
    protected $table = 'languages';
    protected $fillable = ['title', 'name', 'country_code', 'dir', 'locale', 'status'];
}

LanguageText Model

class LanguageText extends Model {
    protected $table = 'language_texts';
    protected $fillable = ['default_content'];

    public function translations(): HasMany {
        return $this->hasMany(LanguageTranslation::class, 'language_text_id');
    }
}

LanguageTranslation Model

class LanguageTranslation extends Model {
    protected $table = 'language_translations';
    protected $fillable = ['language_text_id', 'language_id', 'content'];

    public function language(): BelongsTo {
        return $this->belongsTo(Language::class);
    }
}

Traits

LanguageTranslationTrait

This trait simplifies storing and updating translations:

trait LanguageTranslationTrait {
    public function storeLangTranslation($model): void {
        DB::beginTransaction();
        try {
            foreach ($model->getTranslatableColumns() as $column) {
                $data = $model->attributes[$column];
                $defaultContent = $data[config('multilang.default_locale')] ?? null;
                $langText = LanguageText::create(['default_content' => $defaultContent]);
                foreach ($data as $locale => $content) {
                    if ($content) {
                        LanguageTranslation::create([
                            'language_text_id' => $langText->id,
                            'language_id' => $this->getLangIdByLocale($locale),
                            'content' => $content,
                        ]);
                    }
                }
                $model->attributes[$column] = $langText->id;
            }
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
        }
    }
}

This trait ensures that translations are efficiently stored and updated while handling errors gracefully.

Components

MultiLanguageInput Component

This component provides a customizable multilingual input field

class MultiLanguageInput extends Component {
      public function __construct(
        $label = '',
        $type = 'text',
        $name = '',
        $placeholder = '',
        $value = '',
        $textId = 0,
        $currentLanguage = null,
        $otherLanguages = []
    ) {
        $this->label = $label;
        $this->type = $type;
        $this->name = $name;
        $this->placeholder = $placeholder;
        $this->value = $value;
        $this->textId = $textId;
        $this->currentLanguage = $currentLanguage;
        $this->otherLanguages = $otherLanguages;
    }

    /**
     * Get the view / contents that represent the component.
     */
    public function render(): View|Closure|string
    {
        $currentLanguageLocale = config('multilang.default_locale');

        $languages = Language::all()->keyBy('locale');
        $currentLanguage = $languages->firstWhere('locale', $currentLanguageLocale);
        $otherLanguages = $languages->filter(function ($language) use ($currentLanguageLocale) {
            return $language->locale !== $currentLanguageLocale;
        });
        return view('components.multi-language-input', compact('currentLanguage', 'otherLanguages'));
    }
}

How It Works

1. Component Class (MultiLanguageInput.php)

  • The component is located in App\View\Components\MultiLanguageInput.
  • It extends Laravel’s Component class, making it reusable in Blade templates.
  • The constructor accepts multiple parameters for configuration:
   class MultiLanguageInput extends Component {
       public function __construct(
           $label = '',
           $type = 'text',
           $name = '',
           $placeholder = '',
           $value = '',
           $textId = 0,
           $currentLanguage = null,
           $otherLanguages = []
       ) {
           $this->label = $label;
           $this->type = $type;
           $this->name = $name;
           $this->placeholder = $placeholder;
           $this->value = $value;
           $this->textId = $textId;
           $this->currentLanguage = $currentLanguage;
           $this->otherLanguages = $otherLanguages;
       }
   }
  • $label: The label text for the input field.

  • $type: Input field type (default: text).

  • $name: The name attribute of the input.

  • $placeholder: Placeholder text.

  • $value: Pre-filled value (if any).

  • $textId: ID for retrieving translations via AJAX.

  • $currentLanguage: The primary language for input.

  • $otherLanguages: Additional languages for translation.

  • Fetching Languages:

       public function render(): View|Closure|string
          {
              $currentLanguageLocale = config('multilang.default_locale');
      
              $languages = Language::all()->keyBy('locale');
              $currentLanguage = $languages->firstWhere('locale', $currentLanguageLocale);
              $otherLanguages = $languages->filter(function ($language) use ($currentLanguageLocale) {
                  return $language->locale !== $currentLanguageLocale;
              });
              return view('components.multi-language-input', compact('currentLanguage', 'otherLanguages'));
          }
    • It retrieves all available languages from the languages table.
    • It identifies the current language from the config file (multilang.default_locale).
    • It filters out other available languages for translation.
    • Passes this data to the Blade component view.

2. Blade Component (multi-language-input.blade.php)

  • Primary Input Field

    • Displays an input field for the current language.
    • Includes a label and placeholder.
  • Dropdown for Other Languages

    • If multiple languages exist, a dropdown button appears next to the input field.
    • Clicking the dropdown button reveals additional input fields for other languages.
    • Each language gets a separate text input.
  • JavaScript (jQuery) for Interactivity

    • Clicking the dropdown button toggles the additional language fields.
    • Clicking outside the dropdown closes it automatically.
    • If $textId is provided, an AJAX request fetches saved translations and fills the input fields dynamically.

Example Usage

For Creating a New Entry

<x-multi-language-input 
    label="Name"
    type="text"
    name="name_lang_id"
    placeholder="Enter name..."
/>

For Updating an Existing Entry

<x-multi-language-input 
    label="Name"
    type="text"
    name="name_lang_id"
    placeholder="Enter name..."
    :text_id="$lookup->name_lang_id ?? 0"
/>

The component retrieves existing translations using $lookup->name_lang_id. An AJAX request fetches saved translations and fills the input fields automatically.

Example

Introduction

This guide explains how to integrate the MultiLanguageInput component for handling multilingual fields in Laravel using the Hotel model as an example.

1. How It Works

The MultiLanguageInput component allows users to:

  • Enter text in different languages.
  • Retrieve existing translations dynamically via AJAX.
  • Use a dropdown to toggle between language inputs.
  • Store & update translations automatically with a trait.

2. Setting Up the Hotel Model

Step 1: Create the Model and Use the Trait

In your Hotel model, you need to:

  1. Use the LanguageTranslationTrait.
  2. Define translatable fields in the $translatable array.
  3. Use Eloquent model events (creating & updating) to handle translations.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Traits\LanguageTranslationTrait;

class Hotel extends Model
{
    use LanguageTranslationTrait;

    // Define translatable fields
    public array $translatable = ['name_lang_id'];

    protected $table = 'hotels';

    protected $fillable = ['name_lang_id'];

    // Automatically handle translations on create & update
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            return $model->storeLangTranslation($model);
        });

        static::updating(function ($model) {
            return $model->updateLangTranslation($model);
        });
    }
}

Explanation

  • use LanguageTranslationTrait; → Includes the trait responsible for handling translations.
  • public array $translatable = ['name_lang_id']; → Defines which fields support multiple languages.
  • protected static function boot();
    • Calls storeLangTranslation($model) before creating a new record.
    • Calls updateLangTranslation($model) before updating an existing record.

3. Creating a Hotel with Multi-Language Support

Step 2: Create the Form

To create a new hotel, use the <x-multi-language-input> component in your Blade file.

<form action="{{ route('hotels.store') }}" method="POST">
    @csrf
    <div class="form-group">
        <x-multi-language-input 
            label="Name"
            type="text"
            name="name_lang_id"
            placeholder="Enter hotel name..."
        />
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</form>

What Happens Here?

  • Users enter a name for the hotel in the default language.
  • If multiple languages exist, a dropdown appears to add translations.
  • Upon submission, the translations are stored properly.

4. Updating an Existing Hotel

Step 3: Edit Form with Translations

To edit a hotel’s name and retrieve existing translations, pass the text_id attribute.

<form action="{{ route('hotels.update', $hotel->id) }}" method="POST">
    @csrf
    @method('PUT')

    <div class="form-group">
        <x-multi-language-input 
            label="Name"
            type="text"
            name="name_lang_id"
            placeholder="Enter hotel name..."
            :text_id="$hotel->name_lang_id ?? 0"
        />
    </div>

    <button type="submit" class="btn btn-primary">Update</button>
</form>

What Happens Here?

  • text_id is passed to fetch existing translations via AJAX.
  • Users can modify existing translations directly.

Contributing

Contributions are welcome! Feel free to submit issues and pull requests to improve the package.

License

This package is open-source and available under the MIT License.

By following this guide, you will have a fully functional multilingual system integrated into your Laravel application with robust support for translations and dynamic language switching.