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.
composer require symfony/translation
config/app.php (Laravel 8+ auto-discovers it):
'providers' => [
// ...
Symfony\Component\Translation\TranslationServiceProvider::class,
],
php artisan vendor:publish --provider="Symfony\Component\Translation\TranslationServiceProvider" --tag="config"
config/translation.php:
'locales' => ['en', 'fr', 'es'],
'default_locale' => 'en',
'fallback_locale' => 'en',
// In a controller
$translator = app('translator');
echo $translator->trans('welcome.message', ['%name%' => 'John']);
// In a Blade view
@lang('welcome.message', ['name' => 'John'])
resources/lang/fr/welcome.php:
return [
'message' => 'Bonjour :name, bienvenue !',
];
$translator->trans('welcome.message', ['name' => 'John']); // Outputs: "Bonjour John, bienvenue !"
| Scenario | Loader Class | Example Setup |
|---|---|---|
| Static translations | ArrayLoader |
$translator->addResource('array', $translations, 'fr'); |
| File-based (JSON/YAML/CSV) | YamlFileLoader, JsonFileLoader |
$translator->addResource('yaml', 'path/to/translations.fr.yaml', 'fr'); |
| Database-backed | Custom loader (extend LoaderInterface) |
Use DoctrineDBALLoader or build a custom one for Eloquent. |
| XLIFF (Crowdin/Lokalise) | XliffFileLoader |
$translator->addResource('xliff', 'translations.xlf', 'fr'); |
| Dynamic (API responses) | ArrayLoader + runtime data |
Load translations from an API and cache them. |
Example: Dynamic Loader for API Translations
use Symfony\Component\Translation\Loader\LoaderInterface;
class ApiTranslationLoader implements LoaderInterface
{
public function load($resource, $locale, $domain = 'messages')
{
$response = Http::get("https://api.example.com/translations/{$locale}");
return json_decode($response, true);
}
}
Group translations by context (e.g., validation, notifications):
// In config/translation.php
'default_domain' => 'messages',
'domains' => [
'validation' => 'resources/lang/*/validation.php',
'notifications' => 'resources/lang/*/notifications.php',
],
// Usage
$translator->trans('validation.required', [], 'validation');
Handle plural forms and dynamic content:
// resources/lang/fr/messages.php
'items' => 'Vous avez |{0} aucun article|{1} un article|]1,Inf] {0} articles| articles.',
'greeting' => 'Bonjour, :name!',
// Usage
$translator->trans('items', ['%0%' => 5]); // "Vous avez 5 articles."
$translator->trans('greeting', ['name' => 'John']); // "Bonjour, John!"
@lang('messages.welcome')
@choice('messages.items', $count)
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($data, [
'email' => 'required|email',
], [
'email.required' => trans('validation.email_required'),
]);
Leverage Laravel’s cache to avoid reloading translations:
$translator = app('translator');
$translator->getCatalogue('fr')->setCache($cache); // Use Laravel's cache driver
// app/Http/Middleware/LocaleMiddleware.php
public function handle($request, Closure $next)
{
$locale = $request->segment(1) ?? config('app.locale');
app()->setLocale($locale);
return $next($request);
}
Use Laravel’s testing helpers:
public function test_translations()
{
$this->assertEquals(
'Bonjour John!',
trans('welcome.greeting', ['name' => 'John'])
);
}
Locale Fallback Chain:
fr_CA is requested but only fr exists, ensure fallback_locale is set in config/translation.php to avoid errors.'fallbacks' => [
'fr_CA' => ['fr', 'en'],
'es_MX' => ['es', 'en'],
],
Translation File Caching:
php artisan config:clear
php artisan view:clear
XLIFF File Paths:
$translator->addResource('xliff', urlencode('path/to/translations.xlf'), 'fr');
Pluralization Rules:
// resources/lang/fr/messages.php
'apples' => 'Il y a |{0} zéro pomme|{1} une pomme|]1,Inf] {0} pommes|.',
Namespace Collisions:
messages and messages.notifications). Use unique domains:
$translator->trans('notifications.welcome', [], 'notifications');
CSV Loader Quirks:
$translator->addResource('csv', 'translations.csv', 'fr', 'messages');
Check Loaded Resources:
$catalogue = $translator->getCatalogue('fr');
dump($catalogue->getResources());
Enable Debug Mode:
$translator->setFallbackLocale('en');
$translator->setDebug(true); // Logs missing translations
Validate Translation Files:
Use Laravel’s lang:publish to regenerate files:
php artisan lang:publish
Custom Loaders:
Extend LoaderInterface for database/API-based translations:
class EloquentLoader implements LoaderInterface
{
public function load($resource, $locale, $domain = 'messages')
{
return Translation::where('locale', $locale)
->where('domain', $domain)
->pluck('message', 'id')
->toArray();
}
}
Message Extractors:
Automate translation key extraction with a custom MessageExtractorInterface:
class LaravelMessageExtractor implements MessageExtractorInterface
{
public function extract($file, $locale, $domain)
{
// Parse Blade files for `@lang()` directives
}
}
Translation Dumpers: Export translations to external services (e.g., Crowdin):
use Symfony\Component\Translation\Dumper\XliffFileDumper;
$dumper = new XliffFileDumper();
$dumper->dump($catalogue, 'translations.xlf');
Middleware for Locale Detection: Dynamically set locale based on:
fr.app.com)$translator->getCatalog
How can I help you explore Laravel packages today?