symfony/translation
Symfony Translation component helps internationalize PHP apps with a powerful Translator, loaders, and resources (arrays, files, etc.). Define locales, add message catalogs, and translate strings with parameters and pluralization support across your application.
Installation:
composer require symfony/translation
For Laravel, use symfony:translation as a dependency (often included via symfony/http-kernel or symfony/framework-bundle).
Basic Setup:
resources/lang/{locale}/{domain}.php (Laravel convention) or custom files.config/app.php or a service provider):
$translator = new \Symfony\Component\Translation\Translator('en');
$translator->addLoader('php', new \Symfony\Component\Translation\Loader\PhpFileLoader());
$translator->addResource('php', __DIR__.'/../resources/lang/en/messages.php', 'en');
app()->singleton('translator', fn() => $translator);
First Use Case: Translate a string in a Blade view or controller:
// Blade: @lang('messages.welcome')
// PHP: app('translator')->trans('messages.welcome', ['%name%' => 'John']);
vendor/symfony/translation/README.md for loader examples (PHP, YAML, JSON, XLIFF).Translator facade (trans() helper) which wraps Symfony’s translator.config/app.php for locale defaults and fallback chains.Translation Loading:
PhpFileLoader, YamlFileLoader, or JsonFileLoader for structured catalogs.
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
$translator->addResource('yaml', $loader->load($filePath), 'en');
LoaderInterface).Message Domains:
Organize translations by domain (e.g., validation, auth) for modularity:
$translator->addResource('yaml', $loader->load($filePath), 'en', 'validation');
trans('validation.required', [], 'validation'); // Domain specified last
Pluralization and Interpolation: Handle plural rules (e.g., Arabic, Russian) and dynamic values:
// messages.php
'apples' => 'You have {count, plural, =0 {no apples} one {one apple} other {# apples}}.',
trans('messages.apples', ['%count%' => 5]); // "You have 5 apples."
Fallback Chains: Define fallback locales to avoid missing translations:
$translator->setFallbackLocales(['en' => ['fr', 'es']]);
Caching: Cache compiled translations for performance:
$translator->setCacheDir(__DIR__.'/../var/cache/translations');
Service Provider Binding: Bind Symfony’s translator to Laravel’s container in a service provider:
public function register()
{
$this->app->singleton('translator', function ($app) {
$translator = new \Symfony\Component\Translation\Translator($app['config']['app.locale']);
$translator->addLoader('php', new \Symfony\Component\Translation\Loader\PhpFileLoader());
$translator->addResource('php', __DIR__.'/../../resources/lang/'.$app['config']['app.locale'].'/messages.php', $app['config']['app.locale']);
return $translator;
});
}
Blade Directives:
Use Laravel’s @lang directive or trans() helper:
@lang('messages.welcome', ['name' => $user->name])
Validation Messages: Extend Laravel’s validator with custom translation domains:
$validator = Validator::make($data, $rules, [], [], [
'messages' => trans('validation.messages'),
]);
Middleware for Locale Switching: Dynamically switch locales based on user preferences or headers:
public function handle($request, Closure $next)
{
$locale = $request->header('Accept-Language') ?: config('app.locale');
app('translator')->setLocale($locale);
return $next($request);
}
Translation Dump: Export translations for external tools (e.g., Crowdin, Lokalise):
php artisan translation:dump --format=json --domain=messages --locale=fr
TranslatorInterface to inject translations into controllers or services.$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturn('Mocked translation');
$this->app->instance(TranslatorInterface::class, $translator);
TranslationPushCommand to sync translations with external services (e.g., Crowdin):
php artisan translation:push --service=crowdin --token=your_token
Loader Registration Order:
addResource:
// Wrong: Loader not registered for 'en' before adding resource
$translator->addResource('yaml', $filePath, 'en');
// Correct:
$translator->addLoader('yaml', new YamlFileLoader());
$translator->addResource('yaml', $filePath, 'en');
Caching Issues:
php artisan cache:clear
php artisan view:clear
var/cache/translations.Pluralization Rules:
Intl for complex rules (e.g., Arabic’s 6 forms):
$translator->setFallbackLocale('en'); // Fallback if pluralization fails
File Paths in Loaders:
$translator->addResource('php', realpath(__DIR__.'/../../resources/lang/en/messages.php'), 'en');
Locale Negotiation:
AppServiceProvider may override Symfony’s locale. Ensure consistency:
// In AppServiceProvider boot()
$locale = request()->header('X-Locale') ?: config('app.locale');
app('translator')->setLocale($locale);
XLIFF Loader Quirks:
XliffFileLoader requires valid XLIFF 1.2 files. Invalid files may throw cryptic errors. Validate with:
xmllint --noout your_file.xlf
StaticMessage Deprecation:
TranslatableMessage::__toString() is deprecated. Use getMessage() or trans() directly:
// Avoid:
echo $message; // Deprecated
// Use:
echo $message->getMessage();
Missing Translations:
if (!$translator->has('messages.welcome', 'fr')) {
dd($translator->getFallbackLocales()); // Debug fallback chain
}
file_exists() before loading.Loader Errors:
try {
$translator->addResource('yaml', $loader->load($filePath), 'en');
} catch (\Exception $e) {
Log::error('Translation load error: '.$e->getMessage());
}
Performance Bottlenecks:
$translator->setCacheDir(sys_get_temp_dir().'/translation_cache');
Pluralization Debugging:
Intl library directly:
use Symfony\Component\Intl\Intl;
Intl::getMessageFormatter('en', '{count, plural, =0 {zero} one {one} other {many}}', 5)->getMessage();
LoaderInterface for databases or APIs:
class DatabaseLoader implements LoaderInterface {
public function load
How can I help you explore Laravel packages today?