cakephp/i18n
CakePHP I18n library for localization: set the current locale, load/organize PO translation bundles, and translate messages with ICU formatting. Includes Time and Number helpers to format dates, currencies, and numeric output per locale.
Install the Package
composer require cakephp/i18n
Ensure no CakePHP core package (cakephp/cakephp) is installed to avoid conflicts.
Configure Locale Paths
Update Laravel’s config/app.php or create a custom config file (e.g., config/i18n.php):
'i18n' => [
'paths' => [
base_path('resources/locales'), // Default path for PO files
],
],
Or use Laravel’s Configure equivalent:
use Cake\Core\Configure;
Configure::write('App.paths.locales', [base_path('resources/locales/')]);
Set the Default Locale
In AppServiceProvider@boot():
use Cake\I18n\I18n;
I18n::setLocale(config('app.locale')); // e.g., 'en_US'
First Translation
Create a .po file (e.g., resources/locales/en_US/LC_MESSAGES/default.po) with:
msgid "Hello, {0}!"
msgstr "Hello, %s!"
Use it in a Blade view or controller:
echo __('Hello, {0}!', ['World']); // Outputs: "Hello, World!"
Basic Date/Number Formatting
use Cake\I18n\Time;
use Cake\I18n\Number;
echo Time::now('fr_FR'); // "20/04/2024, 15:30"
echo Number::currency(1000.50, 'EUR', 'fr_FR'); // "1 000,50 €"
Dynamic Locale Switching: Bind a middleware to set the locale from user input (e.g., URL, cookie):
// app/Http/Middleware/SetLocale.php
public function handle($request, Closure $next)
{
$locale = $request->cookie('locale') ?? config('app.fallback_locale');
I18n::setLocale($locale);
return $next($request);
}
Register in app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\SetLocale::class,
// ...
],
];
Fallback Locales:
Configure fallbacks in config/i18n.php:
'fallbacks' => [
'fr_FR' => ['fr', 'en_US'],
'es_ES' => ['es', 'en_US'],
],
Use in code:
I18n::setLocale('fr_FR', ['fr', 'en_US']);
Custom Domains for Plugins:
Register a custom translator for a plugin (e.g., payments):
I18n::translator('payments', 'en_US', function () {
$package = new \Cake\I18n\Package('default', 'default');
$package->setMessages([
'Payment successful' => 'Payment processed',
'Error: {0}' => 'Error: %s',
]);
return $package;
});
Use in views:
echo __d('payments', 'Payment successful'); // "Payment processed"
Domain-Specific PO Files:
Store translations in resources/locales/{locale}/LC_MESSAGES/payments.po:
msgid "Payment successful"
msgstr "Payment processed"
Time Formatting: Parse and format dates dynamically:
$time = Time::parse('2024-04-20', 'en_US', 'Y-m-d');
echo $time->format('M d, Y', 'fr_FR'); // "avr. 20, 2024"
Use with Carbon for hybrid workflows:
use Cake\I18n\Time;
$carbon = Carbon::parse('2024-04-20');
echo Time::forCarbon($carbon, 'ja_JP')->format('Y年m月d日'); // "2024年4月20日"
Number and Currency Formatting:
echo Number::format(1000000, 'en_US'); // "1,000,000"
echo Number::currency(1234.56, 'EUR', 'de_DE'); // "1.234,56 €"
For dynamic locales (e.g., user settings):
$userLocale = $user->locale;
echo Number::currency($amount, $currency, $userLocale);
Blade Directives:
Create a Blade directive for __():
// app/Providers/BladeServiceProvider.php
Blade::directive('trans', function ($expression) {
return "<?php echo __('{$expression}'); ?>";
});
Usage:
@trans('Welcome, {0}!', ['user'])
Form Request Validation: Localize error messages:
// resources/locales/en_US/LC_MESSAGES/validation.po
msgid "The {0} field is required."
msgstr "The {0} field is required."
In a form request:
$this->validate('email', [
'required' => __('The email field is required.'),
]);
API Responses: Format dates/numbers in API responses:
return response()->json([
'amount' => Number::currency($order->amount, $order->currency, $locale),
'created_at' => Time::forCarbon($order->created_at, $locale)->format('Y-m-d H:i'),
]);
For large apps, lazy-load PO files to improve performance:
I18n::translator('app', 'fr_FR', function () {
$package = new \Cake\I18n\Package('default', 'default');
$package->loadMessagesFromFile(
base_path("resources/locales/fr_FR/LC_MESSAGES/default.po")
);
return $package;
});
Combine CakePHP’s PO files with Laravel’s JSON files:
// Fallback to Laravel's trans() if PO file is missing
function __($message, $args = [])
{
try {
return \Cake\I18n\I18n::translate($message, $args);
} catch (\Exception $e) {
return trans($message, $args);
}
}
Mock locales in PHPUnit:
use Cake\I18n\I18n;
public function testTranslation()
{
I18n::setLocale('fr_FR');
$this->assertEquals('Bonjour', __('Hello')); // Assumes PO file exists
}
Test date formatting:
public function testDateFormatting()
{
$time = Time::parse('2024-04-20', 'en_US');
$this->assertEquals('avr. 20, 2024', $time->format('M d, Y', 'fr_FR'));
}
Use in Artisan commands:
use Cake\I18n\I18n;
class LocalizeCommand extends Command
{
protected function handle()
{
I18n::setLocale('es_ES');
$this->info(__('Processing locale: {0}', ['es_ES']));
}
}
en_US (not en-us or en). Use ICU-compliant codes.
// Correct
I18n::setLocale('fr_FR');
// Incorrect (may throw errors)
I18n::setLocale('fr-fr');
pt_BR vs. pt_PT) have vastly different date/number formats.How can I help you explore Laravel packages today?