symfony/translation
Symfony Translation component for internationalizing apps: manage translators, message catalogs, pluralization and locales, load translations from arrays/files, and translate strings with parameters and domains. Install via Composer and integrate in Symfony or standalone PHP.
Install the package:
composer require symfony/translation
Laravel already bundles this component, so no additional installation is needed if using Laravel’s built-in localization.
Configure the translator (typically in config/app.php or a custom config file):
'translator' => [
'default_locale' => 'en',
'fallback_locale' => 'en',
'locales' => ['en', 'fr', 'es', 'de'],
'supported_locales' => [
'en' => 'English',
'fr' => 'Français',
'es' => 'Español',
'de' => 'Deutsch',
],
'loaders' => [
'array',
'yaml',
'json',
'xliff',
'csv',
'php',
'qmo',
'po',
],
],
First use case: Translating a string
// In a controller or view
$translated = trans('messages.welcome', ['name' => 'John'], [], 'fr');
// Outputs: "Bonjour John !" (assuming 'messages.welcome' is defined in fr_FR translation file)
Locate translation files:
Laravel expects files in resources/lang/{locale}/messages.php (or .json, .yaml, etc. based on config). Example:
// resources/lang/fr/messages.php
return [
'welcome' => 'Bonjour :name !',
];
Switch locales dynamically:
app()->setLocale('fr');
$translated = trans('messages.welcome');
For JSON/YAML/CSV files:
Use Laravel’s built-in loaders by placing files in resources/lang/{locale}/{domain}.{ext}.
Example: resources/lang/fr/validation.json for validation messages.
For database-backed translations:
Create a custom loader (e.g., DatabaseLoader) extending Symfony\Component\Translation\Loader\LoaderInterface:
use Symfony\Component\Translation\Loader\LoaderInterface;
use Illuminate\Support\Facades\DB;
class DatabaseLoader implements LoaderInterface
{
public function load($resource, $locale, $domain = 'messages')
{
return DB::table('translations')
->where('locale', $locale)
->where('domain', $domain)
->pluck('translation', 'key')
->toArray();
}
}
Register it in config/app.php under translator.loaders.
For XLIFF files (common in professional translation workflows):
Use the XliffFileLoader for .xlf files. Example structure:
resources/lang/
├── fr/
│ └── messages.xlf
Organize translations by domain (e.g., validation, notifications):
// In config/app.php
'translator' => [
'domains' => ['messages', 'validation', 'notifications'],
],
Access domain-specific translations:
trans('validation.required', [], [], 'validation');
Dynamic domain loading: Useful for plugins or modular apps. Example:
$translator->addResource('yaml', new FileLoader(), 'vendor/package/lang/{locale}/messages.yaml', 'package_messages');
Pluralization rules:
Define rules in resources/lang/{locale}/messages.php:
return [
'apples' => '{0} No apples|{1} One apple|]1,Inf[ {count} apples',
];
Use in code:
trans_choice('messages.apples', 5); // Outputs: "5 apples"
Interpolation with parameters:
trans('messages.greeting', ['name' => 'John']); // "Hello, John!"
config/app.php:
'translator' => [
'fallback_locale' => 'en',
],
If fr_CA is missing a translation, it falls back to fr or en.php artisan translation:dump fr
php artisan translation:extract
php artisan translation:push crowdin --force
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturn('Mocked translation');
$this->app->instance(TranslatorInterface::class, $translator);
$this->assertEquals('Bonjour', trans('greeting'));
@lang('fr')
Bonjour
@endlang
public function handle($request, Closure $next)
{
if ($request->has('locale')) {
app()->setLocale($request->locale);
}
return $next($request);
}
Route::group(['prefix' => '{locale}'], function () {
// Routes here will inherit the locale from the URL
});
Locale vs. Language Code Confusion:
fr_FR for French-France) instead of just language codes (e.g., fr) to avoid ambiguity in regional variations (e.g., fr_FR vs. fr_CA).Missing Fallback Locales:
trans('nonexistent.key'); // Returns 'nonexistent.key' if no fallback exists.
Caching Issues:
php artisan cache:clear
php artisan view:clear
Pluralization Edge Cases:
ru_RU):
'{0} {1} apple|{1} {1} apple|[2,4] {1} apples|[5,Inf] {1} apples'
File Loader Quirks:
Database Loader Performance:
$translator->addCache(new FileCache(), true);
Third-Party Service Integration:
--force flag is used carefully to avoid overwriting partial data (fixed in v8.0.6).Deprecated Methods:
TranslatableMessage::__toString() (deprecated in v8.0.0). Use trans() or getMessage() instead.Enable Debug Mode:
$translator->setFallbackLocales([$locale]); // Debug fallback chain
Or use the TranslationDebugger:
$debugger = new \Symfony\Component\Translation\Debug\TranslationDebugger();
$debugger->setTranslator($translator);
Log Missing Translations: Create a custom logger for missing keys:
$translator->setLogger(new \Monolog\Logger('translations'));
Validate Translation Files:
Use the translation:extract command to ensure all used keys are defined in files.
Check Locale Negotiation:
If app()->getLocale() returns unexpected values, inspect the LocaleMiddleware or LocaleServiceProvider.
LoaderInterface for unsupported formats (e.g., Markdown, Excel):How can I help you explore Laravel packages today?