symfony/intl
Symfony Intl component provides access to ICU localization data in PHP: locales, languages, scripts, regions, currencies, and more. Includes tooling to compress bundled data (with zlib) for smaller installs and faster lookups.
Install the Package:
composer require symfony/intl
For compressed ICU data (recommended for production):
php vendor/symfony/intl/Resources/bin/compress
First Use Case: Localized Number Formatting
use Symfony\Component\Intl\NumberFormatter;
$formatter = NumberFormatter::create('en_US', NumberFormatter::CURRENCY);
echo $formatter->format(1234.56); // Output: "$1,234.56"
$formatter = NumberFormatter::create('de_DE', NumberFormatter::CURRENCY);
echo $formatter->format(1234.56); // Output: "1.234,56 €"
Key Entry Points:
NumberFormatter: For currencies, decimals, and percentages.DateFormatter: For localized dates/times.IntlDateFormatter: Advanced date formatting (e.g., calendars).Locale: Validate and normalize locale strings.Transliterator: Emoji/character transliteration.Pattern: Use NumberFormatter with Laravel’s app() helper for dependency injection.
use Symfony\Component\Intl\NumberFormatter;
// In a Laravel service/controller:
$formatter = app(NumberFormatter::class)->create('ja_JP', NumberFormatter::CURRENCY);
$formatted = $formatter->format(1000); // "¥1,000"
Integration Tip: Store formatted values in a formatted_value column in the DB (e.g., for e-commerce) and cache results with Illuminate\Support\Facades\Cache.
Pattern: Combine with Laravel’s Request to auto-detect user locale.
use Symfony\Component\Intl\Locale;
$locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
$formatter = NumberFormatter::create($locale, NumberFormatter::DECIMAL);
Workflow:
Request::getPreferredLanguage() (Laravel 9+) or Accept-Language header.app()->getLocale() if unsupported.Cache::remember("formatter_{$locale}", ...)).Pattern: Use DateFormatter for user-facing dates.
use Symfony\Component\Intl\DateFormatter;
$date = new \DateTime('2023-12-25');
$formatter = DateFormatter::create(
'ar_EG', // Arabic (Egypt)
DateFormatter::LONG,
DateFormatter::LONG,
'Asia/Cairo',
DateFormatter::GREGORIAN
);
echo $formatter->format($date); // "25 ديسمبر 2023"
Integration Tip: Create a Laravel macro for Carbon:
use Carbon\Carbon;
use Symfony\Component\Intl\DateFormatter;
Carbon::macro('formatLocalized', function (string $locale, string $format = DateFormatter::LONG) {
$formatter = DateFormatter::create($locale, $format, $format);
return $formatter->format($this);
});
Usage:
Carbon::now()->formatLocalized('fr_FR'); // "25 décembre 2023"
Pattern: Use IntlPluralRules for grammatically correct UI.
use Symfony\Component\Intl\IntlPluralRules;
$rules = IntlPluralRules::create('ru_RU');
$count = 5;
$rule = $rules->select($count);
// Output: "one", "few", "many", or "other"
Use Case: Dynamic messages like:
$message = match ($rule) {
'one' => '1 элемент',
default => "$count элементов",
};
Pattern: Convert emoji to text for compatibility.
use Symfony\Component\Intl\Transliterator;
$transliterator = Transliterator::create('Any-Latin; Latin-ASCII');
echo $transliterator->transliterate('Hello 👋'); // "Hello [hand]"
Integration Tip: Sanitize user input (e.g., comments) to remove unsupported emoji.
Register formatters as Laravel services:
// app/Providers/AppServiceProvider.php
use Symfony\Component\Intl\NumberFormatter;
public function register()
{
$this->app->singleton(NumberFormatter::class, function () {
return new NumberFormatter('en_US', NumberFormatter::CURRENCY);
});
}
Dynamic Binding:
$this->app->bind(NumberFormatter::class, function ($app, $locale) {
return new NumberFormatter($locale, NumberFormatter::CURRENCY);
});
Usage:
$formatter = app(NumberFormatter::class, 'ja_JP');
Create a Blade directive for localized formatting:
// app/Providers/BladeServiceProvider.php
use Illuminate\Support\Facades\Blade;
Blade::directive('localize', function ($locale) {
return "<?php echo app(\\Symfony\\Component\\Intl\\NumberFormatter::class, '{$locale}')->format(";
});
Usage in Blade:
@localize('de_DE')(1234.56) @endlocalize
Extend Laravel’s validation with Intl rules:
use Illuminate\Validation\Rule;
use Symfony\Component\Intl\Locale;
Rule::macro('validLocale', function ($locale = null) {
return function ($attribute, $value, $fail) use ($locale) {
if (!Locale::acceptFromHttp($value) && !$locale) {
$fail('The :attribute must be a valid locale.');
}
};
});
Usage:
'locale' => ['required', 'validLocale'],
Format responses dynamically:
use Symfony\Component\Intl\NumberFormatter;
return response()->json([
'price' => [
'raw' => 1234.56,
'formatted' => app(NumberFormatter::class, request()->locale)->format(1234.56),
],
]);
if (!Locale::hasLocale($locale)) {
$locale = app()->getLocale(); // Fallback
}
Locale::getRegion() to check if a locale is supported before formatting.compress command) must match your PHP environment’s ICU version. Re-run compression after updating PHP/ICU.php vendor/symfony/intl/Resources/bin/compress --test
DateFormatter requires IANA time zones (e.g., America/New_York). Avoid generic names like EST.Carbon to normalize time zones:
$date = Carbon::now()->timezone('Asia/Tokyo');
Transliterator may not support all emoji. Test edge cases (e.g., skin tones, regional indicators).$transliterator = Transliterator::create('Any-Latin');
$text = $transliterator->transliterate($input);
$text = preg_replace('/\[([^\]]+)\]/', '[EMOJI:$1]', $text);
NumberFormatter per request for dynamic locales is slow. Cache instances:
Cache::remember("formatter_{$locale}", now()->addHours(1), function () use ($locale) {
return NumberFormatter::create($locale, NumberFormatter::
How can I help you explore Laravel packages today?