Download the PHP package upon/mlang without Composer
On this page you can find all versions of the php package upon/mlang. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Package mlang
Short Description High-performance multi-language support for Laravel with security enhancements, bulk operations, and intuitive API. Efficiently manage translations with minimal database overhead.
License MIT
Homepage https://github.com/reymonzakhary/mlang
Informations about the package mlang
MLang - The Performant Way to Handle Multilingual Laravel Models
Stop using JSON columns. Start using proper database structure for translations.
Quick Start • Why MLang? • Features • Documentation • Contributing
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:
Quick Start
Why MLang?
| Feature | MLang | JSON-based packages |
|---|---|---|
| Database queries on translations | Native SQL queries | Requires JSON functions |
| Index support | Full index support | Limited/None |
| Full-text search | Native support | Complex workarounds |
| Bulk operations | Built-in methods | Manual implementation |
| Translation statistics | getStats(), getCoverage() |
Not available |
| Multi-language insert | Single method call | Multiple inserts |
| Rate limiting | Built-in protection | Not available |
| Security helpers | Input validation & sanitization | Varies |
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
- Version Compatibility
- Installation
- Features
- Database Structure
- Artisan Commands
- Using the Facade
- Query Usage
- Helper Classes
- Configuration Options
- Security Best Practices
- Contributing
- License
Features
Core Features
| Feature | Description |
|---|---|
| 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
| Feature | Description |
|---|---|
| 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 & Performance
| Feature | Description |
|---|---|
| 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 run |
Installation
Requirements
| PHP | Laravel |
|---|---|
| >= 8.1 | 10.x, 11.x, 12.x |
Step 1: Install
Step 2: Configure Models
Step 3: Add Trait to Models
Alternative: You can also extend
Upon\Mlang\Models\MlangModelif you prefer inheritance over traits.
Step 4: Run Migration
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
The package provides several Artisan commands for managing translations:
| Command | Arguments | Options | Description |
|---|---|---|---|
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
Using the Facade
MLang comes with a powerful facade that provides a fluent interface for interacting with the package.
Core Facade Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
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 & Generation Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
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
| Method | Parameters | Returns | Description |
|---|---|---|---|
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 & Analysis
| Method | Parameters | Returns | Description |
|---|---|---|---|
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
🆕 Multi-Language Insert (NEW!)
Create a record in multiple languages simultaneously:
🆕 Translation Statistics (NEW!)
Get insights into your translations:
🆕 Bulk Translation Operations (NEW!)
Manage all translations for a record using its regular ID - no need to know about row_id!
Configuration Options
The package offers various configuration options to fine-tune its behavior:
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:
- Set
'auto_generate' => falsein your config file when using factories before migrations. - The trait will automatically detect missing columns and adjust behavior accordingly.
- 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
Using Artisan Commands
Generate translations for all models:
Generate translations for a specific model:
Generate for a specific language:
Remove a language from a table:
Using the Facade
You can also manage translations programmatically using the facade:
Language Detection
To automatically detect the user's browser language:
- Add the locale middleware to your
app/Http/Kernel.phpfile:
To manually set the language:
Query Usage
Both package versions provide the same query methods for working with multilingual content.
Model Query Scopes
| Scope Method | Parameters | Description | Example |
|---|---|---|---|
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
Querying with Conditions
Route Model Binding
The package enhances Laravel's route model binding to automatically fetch the correct language version:
How it works:
- The
row_idfrom 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
When implementing MLang in your models, it's important to understand the relationship between the interface and trait:
-
The Interface (
MlangContractInterface) defines the contract that your models must fulfill to be MLang-compatible. - The Trait (
MlangTrait) provides the actual implementation of the methods required by the interface.
You must use both together:
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
The package includes organized helper classes for better code organization and reusability.
SecurityHelper Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
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:
LanguageHelper Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
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:
TranslationHelper Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
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:
QueryHelper Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
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:
Security Best Practices
This package includes several security features:
- Input Validation: All user inputs (model names, table names, locales) are validated before use
- SQL Injection Prevention: Uses Laravel's Query Builder exclusively, no raw SQL with user input
- Sanitization: Automatic sanitization of string values to remove null bytes and control characters
- Rate Limiting: Built-in rate limiting for bulk operations to prevent abuse
- Type Safety: Strong typing throughout the codebase with PHP 8.3+ features
Security Guidelines
Contributing
Contributions are welcome! Here's how you can help:
- Star the repo - It helps others discover MLang
- Report bugs - Open an issue with reproduction steps
- Suggest features - We'd love to hear your ideas
- Submit PRs - Bug fixes and improvements welcome
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
MIT License - see LICENSE for details.
Built with care by Charisma Design
Report Bug • Request Feature • Contact
All versions of mlang with dependencies
ext-filter Version *
illuminate/database Version ^v11.44.7|12.*
illuminate/support Version ^v11.44.7|12.*