PHPackages                             mahmoud-abdelhamid1/transloquent - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. mahmoud-abdelhamid1/transloquent

ActiveLibrary[Database &amp; ORM](/categories/database)

mahmoud-abdelhamid1/transloquent
================================

Polymorphic, API-friendly Eloquent translation package for Laravel

v1.0.1(1mo ago)00MITPHPPHP ^8.1|^8.2|^8.3

Since Mar 18Pushed 1mo agoCompare

[ Source](https://github.com/Mahmoud-Abdelhamid1/Transloquent)[ Packagist](https://packagist.org/packages/mahmoud-abdelhamid1/transloquent)[ RSS](/packages/mahmoud-abdelhamid1-transloquent/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (1)Versions (3)Used By (0)

Transloquent
============

[](#transloquent)

> Polymorphic, API-friendly Eloquent translation package for Laravel.

[![Latest Version](https://camo.githubusercontent.com/d2e906f3dac225c5a5d67ef2eadffea4b90d388243adcc1b8b02494195faf5ea/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d61686d6f75642d616264656c68616d6964312f7472616e736c6f7175656e742e737667)](https://packagist.org/packages/mahmoud-abdelhamid1/transloquent)[![PHP Version](https://camo.githubusercontent.com/cc9cdea9aa96b40a822425e981b0a030e3371202973c7d57b74e8e99834f81dc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e312d626c7565)](https://packagist.org/packages/mahmoud-abdelhamid1/transloquent)[![Laravel Version](https://camo.githubusercontent.com/3020a5644b41b0a6e42c716ae0486d76e1af9fdfb5ccbce77edf26d8c4e0554e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d25354531302e3025323025374325323025354531312e302d726564)](https://packagist.org/packages/mahmoud-abdelhamid1/transloquent)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

---

What it does
------------

[](#what-it-does)

Transloquent lets any Eloquent model expose translated fields with zero boilerplate. Translations are stored in a single polymorphic `translations` table and resolved automatically on every query based on the request's `Accept-Language` header.

```
// Your model — that's all you need
class Product extends TranslatableModel
{
    protected $fillable    = ['price'];
    protected array $translatable = ['name', 'description'];
}

// Store — pass locale-keyed arrays directly
Product::create([
    'price' => 99,
    'name'  => ['en' => 'Chair', 'ar' => 'كرسي'],
]);

// Read — transparent, no extra calls
// Request: Accept-Language: ar
$product->name; // "كرسي"

// Search across all locales
Product::whereTranslation('name', 'like', '%chair%')->paginate(10);
```

---

Features
--------

[](#features)

- **Polymorphic** — attach translations to any model with a single trait
- **Auto-sync** — `create()` and `update()` handle translations automatically
- **Auto-loading** — translations are eager-loaded on every query via a global scope
- **Locale detection** — parses `Accept-Language` header with full q-value support
- **Fallback chain** — requested locale → default locale → first available → `null`
- **Dual return modes** — `current` (resolved string) or `all` (locale object) per request
- **Clean serialization** — raw model returns work without a Resource class
- **Translation search** — `whereTranslation()` searches across all locales via subquery
- **Validation rule** — `TranslatableRule` validates locale-keyed input in FormRequests
- **API Resources** — base `TranslatableResource` and `TranslatableCollection` included

---

Installation
------------

[](#installation)

```
composer require mahmoud-abdelhamid1/transloquent
php artisan migrate
```

Optionally publish the config:

```
php artisan vendor:publish --tag=transloquent-config
```

---

Setup
-----

[](#setup)

### 1. Register middleware

[](#1-register-middleware)

**Laravel 11+ — `bootstrap/app.php`**

```
->withMiddleware(function (Middleware $middleware) {
    $middleware->api(append: [
        \MahmoudAbdelhamid\Transloquent\Http\Middleware\SetLocaleMiddleware::class,
        \MahmoudAbdelhamid\Transloquent\Http\Middleware\TranslationModeMiddleware::class,
    ]);
})
```

**Laravel 10 — `app/Http/Kernel.php`**

```
protected $middlewareGroups = [
    'api' => [
        // ...
        \MahmoudAbdelhamid\Transloquent\Http\Middleware\SetLocaleMiddleware::class,
        \MahmoudAbdelhamid\Transloquent\Http\Middleware\TranslationModeMiddleware::class,
    ],
];
```

Or apply them selectively using the registered aliases:

```
Route::middleware(['set.locale', 'translation.mode'])->group(function () {
    // your routes
});
```

### 2. Set up your model

[](#2-set-up-your-model)

**Option A — Extend `TranslatableModel` (recommended)**

```
use MahmoudAbdelhamid\Transloquent\Models\TranslatableModel;

class Product extends TranslatableModel
{
    protected $fillable    = ['price', 'category_id'];
    protected array $translatable = ['name', 'description'];
}
```

> ⚠️ Translatable fields must **not** exist as columns on the model's table. They live exclusively in the `translations` table. Do **not** add them to `$fillable`.

**Option B — Use the trait directly**

If you already extend a custom base model:

```
use MahmoudAbdelhamid\Transloquent\Traits\HasTranslations;

class Product extends YourBaseModel
{
    use HasTranslations;

    protected $fillable    = ['price'];
    protected array $translatable = ['name', 'description'];
}
```

---

Writing Translations
--------------------

[](#writing-translations)

### Automatic — via `create()` / `update()`

[](#automatic--via-create--update)

Pass locale-keyed arrays directly. The model extracts them automatically before the SQL runs.

```
// Create
Product::create([
    'price' => 99,
    'name'  => [
        'en' => 'Chair',
        'ar' => 'كرسي',
        'fr' => 'Chaise',
    ],
    'description' => [
        'en' => 'A comfortable chair',
        'ar' => 'كرسي مريح',
    ],
]);

// Update — only provided locales are upserted, others stay untouched
$product->update([
    'price' => 120,
    'name'  => ['en' => 'Armchair'],
]);
```

### Manual — fluent API

[](#manual--fluent-api)

For more granular control:

```
// Single field + locale
$product->setTranslation('name', 'fr', 'Fauteuil')->save();

// Multiple fields for one locale
$product->setTranslationsForLocale('en', [
    'name'        => 'Armchair',
    'description' => 'Very comfortable',
])->save();

// Multiple locales at once
$product->setTranslationsArray([
    'en' => ['name' => 'Chair', 'description' => 'Comfortable'],
    'ar' => ['name' => 'كرسي', 'description' => 'مريح'],
])->save();
```

---

Reading Translations
--------------------

[](#reading-translations)

### Transparent attribute access

[](#transparent-attribute-access)

Translatable fields resolve automatically to the active locale set by the `Accept-Language` header:

```
// Request: Accept-Language: ar
$product->name;        // "كرسي"
$product->description; // "كرسي مريح"
```

### Force a specific locale

[](#force-a-specific-locale)

```
$product->getTranslation('name', 'fr'); // "Chaise"
```

### Get all locales for a key

[](#get-all-locales-for-a-key)

```
$product->getTranslations('name');
// ['en' => 'Chair', 'ar' => 'كرسي', 'fr' => 'Chaise']
```

### Fallback chain

[](#fallback-chain)

When the requested locale has no translation:

```
Requested locale → Default locale (en) → First available locale → null

```

---

Auto-loading
------------

[](#auto-loading)

Translations are **automatically eager-loaded** on every query via a global scope. You never need to manually call `withTranslations()`.

```
// All of these automatically include translations — no extra call needed
Product::all();
Product::find(1);
Product::where('price', '
