PHPackages                             upon/mlang - 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. upon/mlang

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

upon/mlang
==========

High-performance multi-language support for Laravel with security enhancements, bulk operations, and intuitive API. Efficiently manage translations with minimal database overhead.

v2.1.1(5mo ago)28891[1 PRs](https://github.com/reymonzakhary/mlang/pulls)MITPHPPHP ^8.3

Since Jul 28Pushed 5mo ago1 watchersCompare

[ Source](https://github.com/reymonzakhary/mlang)[ Packagist](https://packagist.org/packages/upon/mlang)[ Docs](https://github.com/reymonzakhary/mlang)[ RSS](/packages/upon-mlang/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (3)Versions (25)Used By (0)

MLang - The Performant Way to Handle Multilingual Laravel Models
================================================================

[](#mlang---the-performant-way-to-handle-multilingual-laravel-models)

**Stop using JSON columns. Start using proper database structure for translations.**

[![Total Downloads](https://camo.githubusercontent.com/d4e39a83e0419bc6520a129c859dbbfaba630aad06fffa2d4caa1b471d8d80b6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f75706f6e2f6d6c616e67)](https://packagist.org/packages/upon/mlang)[![Latest Stable Version](https://camo.githubusercontent.com/678e89ac62b784b1073ec316719344312a1d7b013915ae2275cb280311051e09/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f75706f6e2f6d6c616e67)](https://packagist.org/packages/upon/mlang)[![License](https://camo.githubusercontent.com/7b1a1138c8142c9174af4512735fbce0445285cb69b3418a00e4a612661bd12d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f75706f6e2f6d6c616e67)](https://packagist.org/packages/upon/mlang)[![PHP Version](https://camo.githubusercontent.com/89e10bab4ca7d71352068c70d4bb44c0590e83fd0ebfe2bcab71aa78a705d0fa/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f75706f6e2f6d6c616e67)](https://packagist.org/packages/upon/mlang)[![GitHub Stars](https://camo.githubusercontent.com/ddf17fe85536df63998ca9be0df59a7c78c09ff8a2f4b373fc3b8ba7db14f4ee/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f7265796d6f6e7a616b686172792f6d6c616e673f7374796c653d736f6369616c)](https://github.com/reymonzakhary/mlang)

[Quick Start](#quick-start) • [Why MLang?](#why-mlang) • [Features](#features) • [Documentation](#documentation) • [Contributing](#contributing)

---

Why Another Translation Package?
--------------------------------

[](#why-another-translation-package)

Most Laravel translation packages store translations in **JSON columns**. This works, but has serious limitations:

- **Can't query translations efficiently** - Want to search products by name in French? Good luck with JSON.
- **No database indexing** - JSON columns can't be indexed properly, making searches slow.
- **Complex migrations** - Adding a new translatable field means updating JSON structure.

**MLang takes a different approach:** Each translation is a proper database row with `row_id` (linking translations together) and `iso` (language code). This means:

```
// Find all products with "laptop" in the name - in ANY language
Product::where('name', 'like', '%laptop%')->get();

// Works with standard Laravel queries, indexes, and full-text search!
```

Quick Start
-----------

[](#quick-start)

```
composer require upon/mlang
php artisan vendor:publish --tag="mlang"
php artisan mlang:migrate
```

```
// Add to your model
use Upon\Mlang\Contracts\MlangContractInterface;
use Upon\Mlang\Models\Traits\MlangTrait;

class Product extends Model implements MlangContractInterface
{
    use MlangTrait;
}

// Create product in multiple languages at once
MLang::forModel(Product::class)->createMultiLanguage(
    attributes: ['price' => 99.99],
    languages: ['en', 'fr', 'de'],
    translatedAttributes: [
        'en' => ['name' => 'Laptop', 'description' => 'Powerful laptop'],
        'fr' => ['name' => 'Ordinateur', 'description' => 'Ordinateur puissant'],
        'de' => ['name' => 'Laptop', 'description' => 'Leistungsstarker Laptop'],
    ]
);

// Query in current language - automatically filtered!
$products = Product::trWhere('status', 'active')->get();
```

Why MLang?
----------

[](#why-mlang)

FeatureMLangJSON-based packages**Database queries on translations**Native SQL queriesRequires JSON functions**Index support**Full index supportLimited/None**Full-text search**Native supportComplex workarounds**Bulk operations**Built-in methodsManual implementation**Translation statistics**`getStats()`, `getCoverage()`Not available**Multi-language insert**Single method callMultiple inserts**Rate limiting**Built-in protectionNot available**Security helpers**Input validation &amp; sanitizationVaries### When to Use MLang

[](#when-to-use-mlang)

**Choose MLang if you need:**

- Search/filter by translated fields (e-commerce, CMS, catalogs)
- Database-level performance for translations
- Translation coverage analytics
- Bulk translation operations

**Stick with JSON-based packages if:**

- You only display translations (no querying)
- You have very few translatable models
- You prefer simpler table structure

Documentation
-------------

[](#documentation)

- [Version Compatibility](#version-compatibility)
- [Installation](#installation)
- [Features](#features)
- [Database Structure](#database-structure)
- [Artisan Commands](#artisan-commands)
- [Using the Facade](#using-the-facade)
- [Query Usage](#query-usage)
- [Helper Classes](#helper-classes)
- [Configuration Options](#configuration-options)
- [Security Best Practices](#security-best-practices)
- [Contributing](#contributing)
- [License](#license)

Features
--------

[](#features)

### Core Features

[](#core-features)

FeatureDescription**Row-based translations**Each translation is a database row - queryable, indexable, searchable**Auto language scoping**Queries automatically filter by current locale**Route model binding**Works seamlessly with Laravel's route model binding**Fallback support**Automatic fallback to default language when translation missing**Browser detection**Middleware to detect user's preferred language### Developer Experience

[](#developer-experience)

FeatureDescription**One-line multi-language insert**`createMultiLanguage()` creates all translations at once**Bulk operations**Update/delete all translations for a record in one call**Translation statistics**`getStats()` and `getCoverage()` for analytics**Artisan commands**`mlang:migrate`, `mlang:generate` for easy management**Facade API**Fluent interface: `MLang::forModel(Product::class)->...`### Security &amp; Performance

[](#security--performance)

FeatureDescription**Input validation**Built-in validation for locales, table names, model classes**SQL injection prevention**All queries use Laravel Query Builder**Rate limiting**Built-in protection for bulk operations**Safe with factories**Works before migrations runInstallation
------------

[](#installation)

### Requirements

[](#requirements)

PHPLaravel&gt;= 8.110.x, 11.x, 12.x### Step 1: Install

[](#step-1-install)

```
composer require upon/mlang
composer require doctrine/dbal  # Required dependency
php artisan vendor:publish --tag="mlang"
```

### Step 2: Configure Models

[](#step-2-configure-models)

```
// config/mlang.php
'models' => [
    \App\Models\Category::class,
    \App\Models\Product::class,
]
```

### Step 3: Add Trait to Models

[](#step-3-add-trait-to-models)

```
use Upon\Mlang\Contracts\MlangContractInterface;
use Upon\Mlang\Models\Traits\MlangTrait;

class Product extends Model implements MlangContractInterface
{
    use MlangTrait;
}
```

> **Alternative:** You can also extend `Upon\Mlang\Models\MlangModel` if you prefer inheritance over traits.

### Step 4: Run Migration

[](#step-4-run-migration)

```
php artisan mlang:migrate
```

This adds two columns to your tables:

- `row_id` - Links translations together (same row\_id = same content in different languages)
- `iso` - Language code (en, fr, de, etc.)

Artisan Commands
----------------

[](#artisan-commands)

The package provides several Artisan commands for managing translations:

CommandArgumentsOptionsDescription`mlang:migrate`-`--table=TABLE_NAME`
`--rollback`Add MLang columns to tables
Use `--rollback` to remove columns`mlang:generate``{model?}`
`{locale?}`-Generate translations for models
Optionally specify model name and locale### Command Examples

[](#command-examples)

```
# Migrate all configured models
php artisan mlang:migrate

# Migrate specific table
php artisan mlang:migrate --table=categories

# Rollback all migrations
php artisan mlang:migrate --rollback

# Rollback specific table
php artisan mlang:migrate --table=categories --rollback

# Generate translations for all models and languages
php artisan mlang:generate

# Generate for specific model
php artisan mlang:generate Category

# Generate for specific model and language
php artisan mlang:generate Category fr

# Generate for all models, specific language
php artisan mlang:generate all fr
```

Using the Facade
----------------

[](#using-the-facade)

MLang comes with a powerful facade that provides a fluent interface for interacting with the package.

### Core Facade Methods

[](#core-facade-methods)

MethodParametersReturnsDescription`forModel()``object|string $model``Mlang`Set the model to work with (chainable)`getModelName()`-`string`Get the current model name`getTableName()`-`string|null`Get the table name for current model`getTableNames()`-`array`Get all table names from configured models`getModels()`-`array`Get all configured model class names`getCurrentModel()`-`string|null`Get current model class name`getModelInstance()`-`object|null`Get instance of current model### Migration &amp; Generation Methods

[](#migration--generation-methods)

MethodParametersReturnsDescription`migrate()``?string $table = null``Mlang`Add MLang columns to table (chainable)`rollback()``?string $table = null``Mlang`Remove MLang columns from table (chainable)`generate()``?string $model = null, ?string $locale = null``Mlang`Generate translations for model (chainable)### 🆕 Multi-Language Operations

[](#-multi-language-operations)

MethodParametersReturnsDescription`createMultiLanguage()``array $attributes, ?array $languages = null, ?array $translatedAttributes = null``array`Create record in multiple languages at once`getAllTranslations()``int|string $id``Collection`Get all language versions of a record by ID`updateAllTranslations()``int|string $id, array $attributes``int`Update all translations by ID (returns count)`deleteAllTranslations()``int|string $id``int`Delete all translations by ID (returns count)`copyToLanguage()``Model|int|string $sourceModelOrId, string $targetLanguage, array $overrideAttributes = []``Model|null`Copy record to another language (accepts ID or model)### 🆕 Translation Statistics &amp; Analysis

[](#-translation-statistics--analysis)

MethodParametersReturnsDescription`getStats()`-`array`Get translation statistics (total, unique, per language)`getCoverage()`-`float`Get translation coverage percentage`getIncompleteTranslations()`-`Collection`Get records with missing translations### Basic Usage Examples

[](#basic-usage-examples)

```
use Upon\Mlang\Facades\MLang;
use App\Models\Category;

// Specify which model to work with
MLang::forModel(Category::class)->migrate();

// Chain multiple operations together
MLang::forModel(Category::class)
    ->migrate()
    ->generate();

// Get model information
$modelName = MLang::forModel(Category::class)->getModelName(); // Returns "Category"
$tableName = MLang::forModel(Category::class)->getTableName(); // Returns the table name

// You can also use a model instance
$category = new Category();
MLang::forModel($category)->generate();
```

### 🆕 Multi-Language Insert (NEW!)

[](#-multi-language-insert-new)

Create a record in multiple languages simultaneously:

```
use Upon\Mlang\Facades\MLang;
use App\Models\Product;

// Create product in all configured languages
$records = MLang::forModel(Product::class)->createMultiLanguage([
    'name' => 'Product Name',
    'description' => 'Product Description',
    'price' => 99.99
]);

// Create product in specific languages only
$records = MLang::forModel(Product::class)->createMultiLanguage(
    attributes: [
        'name' => 'Product Name',
        'price' => 99.99
    ],
    languages: ['en', 'fr', 'de']
);

// Create with language-specific content
$records = MLang::forModel(Product::class)->createMultiLanguage(
    attributes: [
        'price' => 99.99,
        'status' => 'active'
    ],
    languages: ['en', 'fr'],
    translatedAttributes: [
        'en' => [
            'name' => 'English Product Name',
            'description' => 'English Description'
        ],
        'fr' => [
            'name' => 'Nom du Produit Français',
            'description' => 'Description Française'
        ]
    ]
);
```

### 🆕 Translation Statistics (NEW!)

[](#-translation-statistics-new)

Get insights into your translations:

```
use Upon\Mlang\Facades\MLang;
use App\Models\Category;

// Get translation statistics
$stats = MLang::forModel(Category::class)->getStats();
// Returns:
// [
//     'total_records' => 150,
//     'unique_records' => 50,
//     'languages' => [
//         'en' => 50,
//         'fr' => 48,
//         'de' => 45
//     ]
// ]

// Get translation coverage percentage
$coverage = MLang::forModel(Category::class)->getCoverage(); // Returns: 94.67

// Get records with incomplete translations
$incomplete = MLang::forModel(Category::class)->getIncompleteTranslations();
```

### 🆕 Bulk Translation Operations (NEW!)

[](#-bulk-translation-operations-new)

Manage all translations for a record using its regular ID - no need to know about `row_id`!

```
use Upon\Mlang\Facades\MLang;
use App\Models\Product;

// Get all translations for a specific product (pass the regular ID)
$productId = 1;
$translations = MLang::forModel(Product::class)->getAllTranslations($productId);
// Returns collection of all language versions (English, French, German, etc.)

// Update all translations at once (common fields like price, status)
$updated = MLang::forModel(Product::class)->updateAllTranslations($productId, [
    'price' => 149.99,
    'status' => 'sale'
]);
// Updates the price and status for all language versions

// Delete all translations for a record (all languages)
$deleted = MLang::forModel(Product::class)->deleteAllTranslations($productId);

// Copy a record to another language - multiple ways:
// 1. Using model instance
$product = Product::find(1);
$frenchProduct = MLang::forModel(Product::class)->copyToLanguage($product, 'fr', [
    'name' => 'Nom du Produit Français'
]);

// 2. Using just the ID (much easier!)
$frenchProduct = MLang::forModel(Product::class)->copyToLanguage(1, 'fr', [
    'name' => 'Nom du Produit Français',
    'description' => 'Description en Français'
]);
```

Configuration Options
---------------------

[](#configuration-options)

The package offers various configuration options to fine-tune its behavior:

```
// config/mlang.php

// Control automatic generation of translations
'auto_generate' => false,

// Enable automatic migration after Laravel migrations
'auto_migrate' => false,

// Control observer behavior during console operations
'observe_during_console' => false,

// Auto-generate translations after seeding
'auto_generate_after_seed' => false,

// Specify which models to process after migrations/seeding
'auto_generate_models' => 'all',

// Control rollback behavior
'auto_rollback' => true,

// Toggle debug output
'debug_output' => true,
```

Working with Factories
----------------------

[](#working-with-factories)

The package is designed to work safely with factories even before migrations have run. It intelligently detects when MLang columns don't exist and avoids trying to use them in such cases.

To ensure smooth operation with factories:

1. Set `'auto_generate' => false` in your config file when using factories before migrations.
2. The trait will automatically detect missing columns and adjust behavior accordingly.
3. After running migrations, you can enable auto-generation features.

**Trait-based approach benefits:**The trait-based approach makes it easy to work with factories before migrations, with enhanced column existence detection and graceful fallbacks.

Managing Translations
---------------------

[](#managing-translations)

### Using Artisan Commands

[](#using-artisan-commands)

Generate translations for all models:

```
php artisan mlang:generate
```

Generate translations for a specific model:

```
php artisan mlang:generate {model}
# Example: php artisan mlang:generate Category
# Or for nested models: php artisan mlang:generate Shop\\Product
```

Generate for a specific language:

```
php artisan mlang:generate {model|all} {locale}
# Example: php artisan mlang:generate all fr
```

Remove a language from a table:

```
php artisan mlang:remove {table} {locale}
# Example: php artisan mlang:remove categories fr
```

### Using the Facade

[](#using-the-facade-1)

You can also manage translations programmatically using the facade:

```
use Upon\Mlang\Facades\Mlang;
use App\Models\Category;

// Generate translations for a specific model
Mlang::forModel(Category::class)->generate();

// Generate translations for a specific model and language
Mlang::generate('Category', 'fr');

// Run migrations for a specific model
Mlang::forModel(Category::class)->migrate();

// Roll back migrations for a specific model
Mlang::forModel(Category::class)->rollback();
```

### Language Detection

[](#language-detection)

To automatically detect the user's browser language:

1. Add the locale middleware to your `app/Http/Kernel.php` file:

```
protected $middlewareGroups = [
    'web' => [
        // ...
        \Upon\Mlang\Middleware\DetectUserLanguageMiddleware::class,
    ],
    // ...
];
```

To manually set the language:

```
app()->setLocale('fr');
```

Query Usage
-----------

[](#query-usage)

Both package versions provide the same query methods for working with multilingual content.

### Model Query Scopes

[](#model-query-scopes)

Scope MethodParametersDescriptionExample`trFind()``int|string $id, ?string $iso = null`Find record by row\_id in current (or specified) language`Category::trFind(1)``trWhere()``array|string|Closure $conditions`Query with auto language filter and id→row\_id mapping`Category::trWhere('status', 'active')`### Finding Records

[](#finding-records)

```
// Find by row_id in current language
$category = Category::trFind(1);

// Find in specific language
$category = Category::trFind(1, 'fr');
```

### Querying with Conditions

[](#querying-with-conditions)

```
// Query in current language
$categories = Category::trWhere(['name' => 'test'])->get();

// Chain other query methods
$categories = Category::trWhere('status', 'active')
                     ->orderBy('created_at', 'desc')
                     ->paginate(10);

// Complex queries
$categories = Category::trWhere(function($query) {
                         $query->where('price', '>', 100)
                               ->orWhere('featured', true);
                     })->get();
```

### Route Model Binding

[](#route-model-binding)

The package enhances Laravel's route model binding to automatically fetch the correct language version:

```
// routes/web.php
Route::get('/categories/{category}', function (Category $category) {
    // $category will be automatically fetched in the current application language
    return view('categories.show', compact('category'));
});
```

**How it works:**

- The `row_id` from the URL is used to find the record
- The current application locale (`app()->getLocale()`) determines the language
- Returns 404 if no translation exists in the current language

Understanding Interface and Trait Relationship
----------------------------------------------

[](#understanding-interface-and-trait-relationship)

When implementing MLang in your models, it's important to understand the relationship between the interface and trait:

1. **The Interface (`MlangContractInterface`)** defines the contract that your models must fulfill to be MLang-compatible.
2. **The Trait (`MlangTrait`)** provides the actual implementation of the methods required by the interface.

You must use both together:

```
class Category extends Model implements MlangContractInterface
{
    use MlangTrait;

    // Your model code...
}
```

This provides several benefits:

- **Contract Enforcement**: Ensures all required methods are available
- **Implementation Reuse**: Reuses code through the trait
- **Flexibility**: Allows customization by overriding trait methods
- **Type Safety**: Provides better IDE support and static analysis

🆕 Helper Classes
----------------

[](#-helper-classes)

The package includes organized helper classes for better code organization and reusability.

### SecurityHelper Methods

[](#securityhelper-methods)

MethodParametersReturnsDescription`validateLocale()``string $locale``bool`Validate locale format (throws exception if invalid)`validateTableName()``string $table``bool`Validate table name (throws exception if invalid)`validateColumnName()``string $column``bool`Validate column name (throws exception if invalid)`validateModelClass()``string $model``bool`Validate model class exists (throws exception if invalid)`validateLocales()``array $locales``bool`Validate multiple locales at once`tableExists()``string $table``bool`Check if table exists in database`columnExists()``string $table, string $column``bool`Check if column exists in table`sanitizeValue()``mixed $value``mixed`Remove null bytes and control characters`sanitizeAttributes()``array $attributes``array`Sanitize all values in array`isValidRowId()``mixed $rowId``bool`Check if value is valid row\_id (positive integer)`checkRateLimit()``string $key, int $maxAttempts = 60, int $decayMinutes = 1``bool`Rate limiting check for bulk operations**Example Usage:**

```
use Upon\Mlang\Helpers\SecurityHelper;

// Validate inputs
SecurityHelper::validateLocale('en');
SecurityHelper::validateTableName('users');
SecurityHelper::validateModelClass(Product::class);

// Check database structure
if (SecurityHelper::tableExists('categories')) {
    if (SecurityHelper::columnExists('categories', 'row_id')) {
        // Proceed with operation
    }
}

// Sanitize user inputs
$clean = SecurityHelper::sanitizeValue($userInput);
$cleanAttributes = SecurityHelper::sanitizeAttributes($request->all());

// Rate limiting
if (SecurityHelper::checkRateLimit('bulk_operation', 100, 1)) {
    // Proceed with bulk operation
}
```

---

### LanguageHelper Methods

[](#languagehelper-methods)

MethodParametersReturnsDescription`getConfiguredLanguages()`-`array`Get all configured languages from config`getFallbackLanguage()`-`string`Get fallback language from config`getCurrentLocale()`-`string`Get current application locale`isLanguageConfigured()``string $locale``bool`Check if language is in configuration`parseAcceptLanguageHeader()``?string $header``string`Parse Accept-Language header to get best match`validateAndGetLocale()``?string $locale = null``string`Validate locale or return fallback`getMissingLanguages()``array $existingLanguages``array`Get languages not in existing array`getLanguageName()``string $locale``string`Get human-readable language name`sortLanguagesByPriority()``array $languages``array`Sort languages (current first, then config order)`isAutoGenerateEnabled()`-`bool`Check if auto-generation is enabled`shouldObserveDuringConsole()`-`bool`Check if observer runs during console`getConfiguredModels()`-`array`Get all configured model classes**Example Usage:**

```
use Upon\Mlang\Helpers\LanguageHelper;

// Get language configuration
$languages = LanguageHelper::getConfiguredLanguages(); // ['en', 'fr', 'de']
$fallback = LanguageHelper::getFallbackLanguage(); // 'en'
$current = LanguageHelper::getCurrentLocale(); // Current app locale

// Check configuration
if (LanguageHelper::isLanguageConfigured('fr')) {
    // French is configured
}

// Parse browser language
$locale = LanguageHelper::parseAcceptLanguageHeader('fr-FR,fr;q=0.9,en;q=0.8');

// Get missing translations
$missing = LanguageHelper::getMissingLanguages(['en', 'fr']); // ['de']

// Get language name
$name = LanguageHelper::getLanguageName('fr'); // 'French'
```

---

### TranslationHelper Methods

[](#translationhelper-methods)

MethodParametersReturnsDescription`getExistingTranslations()``Model $model, int|string $rowId``array`Get array of existing locale codes for row\_id`createMultiLanguageRecord()``Model $model, array $attributes, array $languages, ?array $translatedAttributes = null``array`Create record in multiple languages`generateRowId()``Model $model``int`Generate new unique row\_id`handleUniqueConstraints()``Model $model, array $attributes, string $language``array`Handle unique constraints by appending suffixes`getUniqueIndexes()``string $table``array`Get unique indexes for table (DB-agnostic)`copyToLanguage()``Model $sourceModel, string $targetLanguage, array $overrideAttributes = []``Model|null`Copy record to another language`deleteAllTranslations()``Model $model, int|string $rowId``int`Delete all translations for row\_id`updateAllTranslations()``Model $model, int|string $rowId, array $attributes``int`Update all translations for row\_id`getTranslationStats()``Model $model``array`Get statistics (total, unique, per language)**Example Usage:**

```
use Upon\Mlang\Helpers\TranslationHelper;

// Get existing translations
$existing = TranslationHelper::getExistingTranslations($model, $rowId);
// Returns: ['en', 'fr'] if those exist

// Create multi-language records
$records = TranslationHelper::createMultiLanguageRecord(
    $model,
    ['name' => 'Product', 'price' => 99.99],
    ['en', 'fr', 'de']
);

// Copy to another language
$newRecord = TranslationHelper::copyToLanguage($product, 'fr', [
    'name' => 'Nom Français'
]);

// Bulk operations
$count = TranslationHelper::updateAllTranslations($model, $rowId, ['price' => 149.99]);
$deleted = TranslationHelper::deleteAllTranslations($model, $rowId);

// Get statistics
$stats = TranslationHelper::getTranslationStats($model);
// Returns: ['total_records' => 150, 'unique_records' => 50, 'languages' => [...]]
```

---

### QueryHelper Methods

[](#queryhelper-methods)

MethodParametersReturnsDescription`applyLanguageFilter()``Builder $query, ?string $locale = null``Builder`Add language filter to query`applyRowIdFilter()``Builder $query, int|string $rowId``Builder`Add row\_id filter to query`getAllTranslations()``Model $model, int|string $rowId``Collection`Get all language versions of record`findByRowIdAndLocale()``Model $model, int|string $rowId, ?string $locale = null``Model|null`Find specific translation`getRecordsWithIncompleteTranslations()``Model $model``Collection`Get records missing some translations`scopeCurrentLanguage()``Builder $query``Builder`Scope to current language only`scopeWithCompleteTranslations()``Builder $query``Builder`Scope to records with all translations`buildMlangQuery()``Model $model, array $conditions = [], ?string $locale = null``Builder`Build query with language awareness`getTranslationCoverage()``Model $model``float`Get coverage percentage (0-100)**Example Usage:**

```
use Upon\Mlang\Helpers\QueryHelper;

// Apply filters to query
$query = Product::query();
QueryHelper::applyLanguageFilter($query, 'fr');
QueryHelper::applyRowIdFilter($query, 123);
$products = $query->get();

// Find specific translation
$product = QueryHelper::findByRowIdAndLocale($model, $rowId, 'fr');

// Get all translations for a record
$translations = QueryHelper::getAllTranslations($model, $rowId);

// Get incomplete translations
$incomplete = QueryHelper::getRecordsWithIncompleteTranslations($model);

// Build MLang-aware query
$query = QueryHelper::buildMlangQuery($model, ['status' => 'active'], 'fr');

// Get coverage percentage
$coverage = QueryHelper::getTranslationCoverage($model); // e.g., 94.67
```

Security Best Practices
-----------------------

[](#security-best-practices)

This package includes several security features:

1. **Input Validation**: All user inputs (model names, table names, locales) are validated before use
2. **SQL Injection Prevention**: Uses Laravel's Query Builder exclusively, no raw SQL with user input
3. **Sanitization**: Automatic sanitization of string values to remove null bytes and control characters
4. **Rate Limiting**: Built-in rate limiting for bulk operations to prevent abuse
5. **Type Safety**: Strong typing throughout the codebase with PHP 8.3+ features

### Security Guidelines

[](#security-guidelines)

```
// ✅ GOOD: Using facade methods (automatically validated)
MLang::forModel(Category::class)->migrate();

// ✅ GOOD: Validated locale
MLang::forModel(Product::class)->generate(locale: 'fr');

// ❌ BAD: Don't use raw SQL with MLang operations
DB::raw("..."); // Not recommended with user input

// ✅ GOOD: Use sanitization for user inputs
$attributes = SecurityHelper::sanitizeAttributes($request->all());
MLang::forModel(Product::class)->createMultiLanguage($attributes);
```

Contributing
------------

[](#contributing)

Contributions are welcome! Here's how you can help:

1. **Star the repo** - It helps others discover MLang
2. **Report bugs** - Open an issue with reproduction steps
3. **Suggest features** - We'd love to hear your ideas
4. **Submit PRs** - Bug fixes and improvements welcome

Support the Project
-------------------

[](#support-the-project)

If MLang helps you build multilingual Laravel apps, please consider:

- Giving it a star on GitHub
- Sharing it with other Laravel developers
- Writing about your experience using it

License
-------

[](#license)

MIT License - see [LICENSE](LICENSE) for details.

---

Built with care by [Charisma Design](https://charisma-design.eu)

[Report Bug](https://github.com/reymonzakhary/mlang/issues) • [Request Feature](https://github.com/reymonzakhary/mlang/issues) • [Contact](mailto:reymon@charisma-design.eu)

###  Health Score

45

—

FairBetter than 93% of packages

Maintenance71

Regular maintenance activity

Popularity18

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 96.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~50 days

Total

18

Last Release

176d ago

Major Versions

v1.0.12 → v2.0.02025-05-03

PHP version history (2 changes)v1.0PHP ^8.1

v2.0.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/f17586b62f293f4eefa42e0507a335588b2ae590858fa7412ef7e5b6cf2c66e0?d=identicon)[reymonzakhary](/maintainers/reymonzakhary)

---

Top Contributors

[![reymonzakhary](https://avatars.githubusercontent.com/u/11016339?v=4)](https://github.com/reymonzakhary "reymonzakhary (57 commits)")[![ahazem-chd](https://avatars.githubusercontent.com/u/119852400?v=4)](https://github.com/ahazem-chd "ahazem-chd (1 commits)")[![khegazy-chd](https://avatars.githubusercontent.com/u/114858172?v=4)](https://github.com/khegazy-chd "khegazy-chd (1 commits)")

---

Tags

laravellocalizationi18nlanguagedatabaseperformancetranslationeloquentmultilingualmulti-language

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/upon-mlang/health.svg)

```
[![Health](https://phpackages.com/badges/upon-mlang/health.svg)](https://phpackages.com/packages/upon-mlang)
```

###  Alternatives

[richan-fongdasen/laravel-i18n

Simple route and eloquent localization / translation in Laravel

1296.6k](/packages/richan-fongdasen-laravel-i18n)[toponepercent/baum

Baum is an implementation of the Nested Set pattern for Eloquent models.

3154.7k](/packages/toponepercent-baum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
