MultiTranslationArrayRequest Trait
The MultiTranslationArrayRequest trait helps you build validation rules and friendly attribute labels for multi-locale translation payloads under a single input root named translation. It is designed to work with models using the HasTranslation trait and a locale provider (via Language::all()).
When to Use MultiTranslationArrayRequest
Use MultiTranslationArrayRequest when you need:
- Multi-locale validation: Validate translations for all active locales from
Language::all() - Automatic locale detection: Automatically generates rules for all available locales
- Array-based structure: Your translation payload uses a simple array format
- Primary field uniqueness: Enforces uniqueness for a primary field across all locales
- Wildcard field support: Works with models that allow all fields (
['*']) or specific fields - Comprehensive validation: Validates all locales at once, ensuring complete translation coverage
Example scenarios:
- E-commerce product forms requiring translations for all active languages
- CMS content management with mandatory translations for all locales
- Multi-language blog post creation with validation for all supported languages
- Category management forms that require translations in all active locales
- Content forms where all locales must be provided and validated together
No database or tests required here. This document focuses on request-side validation and attribute labeling, with practical, copy‑pasteable examples including expected output.
What It Generates
For each locale returned by Language::all(), this trait will generate:
- A container rule for
translation.{locale}(array) - A primary field rule with uniqueness enforcement via
TranslationFieldExistRule - Rules for other allowed fields:
- If the model allows all fields (
['*']):translation.{locale}.* => string|nullable|sometimes - Otherwise: adds
string|nullable|sometimesfor each explicit allowed field
- If the model allows all fields (
It can also generate human-friendly attribute labels (e.g., for error messages) for each translation.{locale}.{field} key.
Requirements (Conceptual)
- A model that uses
HasTranslation, so the trait can query allowed fields throughgetTranslatableFields(). - A language provider (e.g.,
Language::all()) that returns locales likeen,fa, etc. - The rule class
TranslationFieldExistRuleto apply uniqueness checks for the primary translated field per locale.
The trait does not access your database here; it only builds arrays for validation and attributes.
Quick Start (Typical Usage)
1) Put the trait in your FormRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use JobMetric\Translation\Http\Requests\MultiTranslationArrayRequest;
class StorePostRequest extends FormRequest
{
use MultiTranslationArrayRequest;
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$rules = [
'slug' => 'required|string',
];
// Build translation rules for App\Models\Post
$this->renderMultiTranslationFiled(
$rules,
\App\Models\Post::class, // model using HasTranslation
'title', // primary field to enforce uniqueness on
null, // object_id: exclude current record on update (null for create)
-1, // parent_id: leave -1 to ignore
[] // parent_where: extra constraints (optional)
);
return $rules;
}
public function attributes(): array
{
$attributes = [
'slug' => 'Slug',
];
// Create readable labels. You can pass a translation key that contains "{field}"
// Example value: "Translation ({field})"
$this->renderMultiTranslationAttribute(
$attributes,
\App\Models\Post::class,
null // or 'validation.attributes.translation_field' containing "{field}"
);
return $attributes;
}
}
Note: The method name is
renderMultiTranslationFiled(with a trailing “d”), keep it exactly as implemented in your trait.
Example: Resulting rules() Output
Assume:
Language::all()returns locales:en,faApp\Models\PostusesHasTranslationwithprotected array $translatables = ['title','summary'];- You set
field_name = 'title'as the primary field
Code (from rules()):
$rules = [
'slug' => 'required|string',
];
$this->renderMultiTranslationFiled(
$rules,
\App\Models\Post::class,
'title',
null,
-1,
[]
);
return $rules;
Expected Output (print_r($rules)):
Array
(
[slug] => required|string
[translation] => array
[translation.en] => array
[translation.en.title] => Array
(
[0] => string
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.en.summary] => string|nullable|sometimes
[translation.fa] => array
[translation.fa.title] => Array
(
[0] => string
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.fa.summary] => string|nullable|sometimes
)
Wildcard Mode Example
If the model allows all fields (getTranslatableFields() returns ['*']), then instead of enumerating each field you will get a wildcard:
Expected Output (excerpt):
[translation.en.*] => string|nullable|sometimes
[translation.fa.*] => string|nullable|sometimes
Example: Resulting attributes() Output
Assume the same setup as above and no translation key passed ($trans_scope = null).
Code (from attributes()):
$attributes = [
'slug' => 'Slug',
];
$this->renderMultiTranslationAttribute(
$attributes,
\App\Models\Post::class,
null
);
return $attributes;
Expected Output (print_r($attributes)):
Array
(
[slug] => Slug
[translation.en.title] => title
[translation.en.summary] => summary
[translation.fa.title] => title
[translation.fa.summary] => summary
)
Using a Translation Key with {field}
If you pass a key that contains {field}, the token will be replaced per field. For example, if your translation key renders to Translation ({field}), labels would look like:
Expected Output (excerpt):
[translation.en.title] => Translation (title)
[translation.en.summary] => Translation (summary)
...
Wildcard Mode for Attributes
If the model allows all fields (['*']), you will get a wildcard label per locale:
Expected Output (excerpt):
[translation.en.*] => translation en
[translation.fa.*] => translation fa
API Reference
renderMultiTranslationFiled(array &$rules, string $model_name, string $field_name = 'name', ?int $object_id = null, ?int $parent_id = -1, array $parent_where = []): void
Builds validation rules for each available locale under translation.{locale}.
Parameters
&$rules: The rules array (passed by reference) to be populated.model_name: FQCN of the model that usesHasTranslation(e.g.,App\Models\Post::class).field_name: The primary translatable field to enforce uniqueness on (defaultname).object_id: Current record id to exclude during updates (usenullfor create).parent_id: Optional parent constraint (use-1to ignore).parent_where: Additional constraints (array) used by your uniqueness rule logic.
Result
- Adds entries like
translation.en,translation.en.title, etc., to your$rulesarray. - Primary field gets the
TranslationFieldExistRuleinstance. - Non-primary allowed fields get
string|nullable|sometimes.
renderMultiTranslationAttribute(array &$params, string $model_name, ?string $trans_scope = null): void
Builds user-friendly attribute labels for each translation.{locale}.{field} (or wildcard).
Parameters
&$params: The attributes array (passed by reference) to be populated.model_name: FQCN of the model that usesHasTranslation.trans_scope(optional): A translation key whose resulting string may include{field}which will be replaced. If omitted, raw field keys or a simple "translation {locale}" label is used.
Result
- Adds readable attribute labels suitable for validation messages.
Complete Copy‑Paste Example
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use JobMetric\Translation\Http\Requests\MultiTranslationArrayRequest;
class StorePostRequest extends FormRequest
{
use MultiTranslationArrayRequest;
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$rules = [
'slug' => 'required|string',
];
$this->renderMultiTranslationFiled(
$rules,
\App\Models\Post::class,
'title',
null,
-1,
[]
);
return $rules;
}
public function attributes(): array
{
$attributes = [
'slug' => 'Slug',
];
$this->renderMultiTranslationAttribute(
$attributes,
\App\Models\Post::class,
null
);
return $attributes;
}
}
Expected Output (rules):
Array
(
[slug] => required|string
[translation] => array
[translation.en] => array
[translation.en.title] => Array
(
[0] => string
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.en.summary] => string|nullable|sometimes
[translation.fa] => array
[translation.fa.title] => Array
(
[0] => string
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.fa.summary] => string|nullable|sometimes
)
Expected Output (attributes):
Array
(
[slug] => Slug
[translation.en.title] => title
[translation.en.summary] => summary
[translation.fa.title] => title
[translation.fa.summary] => summary
)
Tips
- Use the primary field (
$field_name) for uniqueness (e.g.,title,name, orslug). - Pass
object_idwhen updating a record to exclude it from uniqueness checks. - Keep
parent_idas-1if you don't need any parent scoping. - Use
renderMultiTranslationAttributeto produce clean labels for validation errors per-locale and per-field. - If your model allows
['*'], you receive wildcard rules and wildcard attribute labels automatically.
Summary
MultiTranslationArrayRequest makes multi-locale validation of translated fields straightforward:
- Builds locale-aware rules under
translation.{locale} - Enforces per-locale uniqueness on a primary field
- Adds user-friendly attribute labels
- Respects the model’s allowed fields (
['*']or explicit list) viaHasTranslation
Copy the snippets above into your FormRequest classes, and you're ready to validate multi-locale translation payloads with clear error messages and clean structure.
Related Documentation
- HasTranslation - Core trait for multilingual models
- TranslationArrayRequest - Simpler version for single or dynamic locales
- TranslationFieldExistRule - Validation rule for unique translations
- TranslationTypeObjectRequest - Typeify-based validation for complex schemas
- MultiTranslationTypeObjectRequest - Multi-locale Typeify validation
- TranslationResource - JSON resource for API responses