PHP internationalization: building robust multilingual applications with Intl and ICU

A practical guide to separating text from code, selecting locales, and handling catalogues, plurals, numbers, currencies and dates with PHP Intl.

PHP internationalization: building robust multilingual applications with Intl and ICU

Making a PHP application multilingual is not just replacing a few strings with an array. A reliable system separates content from application logic, selects language and region predictably, formats quantities, currencies and dates correctly, and retains a fallback language when a translation is missing.

Internationalization and localization

Internationalization prepares software for multiple languages and cultural conventions; localization supplies the text and formats for a particular audience. Code should use stable keys such as checkout.total, while catalogues contain translated sentences. Do not use the Italian sentence as the key: an editorial correction should not require a code change.

Enable Intl and define locales

The intl extension exposes ICU features to PHP. In production, check extension_loaded('intl') and the available ICU version. Use explicit identifiers such as it-IT and en-GB. Never depend on the server default, which may differ between environments. A whitelist blocks unsupported values and arbitrary file paths.

<?php
$supported = ['it-IT', 'en-GB', 'de-DE', 'fr-FR', 'es-ES', 'pt-PT'];
$requested = $_GET['lang'] ?? 'it-IT';
$locale = in_array($requested, $supported, true) ? $requested : 'it-IT';

$catalogue = require __DIR__ . '/lang/' . $locale . '.php';

function trans(string $key, array $vars = []): string {
    global $catalogue, $locale;
    $pattern = $catalogue[$key] ?? $key;
    $formatted = MessageFormatter::formatMessage($locale, $pattern, $vars);
    return $formatted === false ? $pattern : $formatted;
}

Catalogues may be PHP or JSON files, or data delivered by a service. Keep them outside public directories, validate them during deployment and require a shared minimum set of keys. Define fallback behavior in one central component.

Dynamic messages and plurals

Concatenating “You have ”, a number and “ messages” forces English word order onto every translation. MessageFormatter uses ICU patterns so each language controls order, plural rules and embedded formats. Pass structured values to the formatter and log Intl errors instead of silently producing broken output.

Numbers, currencies and dates

Store neutral data: monetary values in minor units or a reliable decimal representation, dates in UTC and currencies as ISO codes. Apply presentation only at the boundary. NumberFormatter changes separators and currency-symbol placement but does not perform exchange-rate conversion. IntlDateFormatter applies the selected language, calendar and an explicit time zone.

<?php
$price = new NumberFormatter($locale, NumberFormatter::CURRENCY);
echo $price->formatCurrency(1299.90, 'EUR');

$date = new IntlDateFormatter(
    $locale,
    IntlDateFormatter::LONG,
    IntlDateFormatter::SHORT,
    'Europe/Rome'
);
echo $date->format(new DateTimeImmutable('2026-11-06 10:30:00'));

Selecting a language

A practical priority is saved user preference, validated session setting, signed cookie, Accept-Language, then application fallback. A browser header is a hint, not a safe path segment. Store the canonical locale and generate consistent localized URLs.

HTML, security and SEO

Escape values at rendering time for the correct HTML, attribute or JavaScript context. Translations must not be trusted HTML by default. Set the document lang attribute, publish hreflang links for indexable variants and maintain a stable map for translated slugs.

Testing and maintenance

Automate checks for valid JSON, missing keys, incompatible placeholders and untranslated text. Test zero, one and many quantities, plus a language with long words. Freeze the time zone in tests. Log missing keys without exposing them to visitors, and give translators context and screenshots.

Conclusion

A sound multilingual architecture keeps business logic independent from prose and delegates cultural formatting to ICU. With validated locales, versioned catalogues, explicit fallbacks and automated tests, adding a language becomes a controlled editorial task rather than a risky code change.

Share

What's Your Reaction?

Like Like 0
Dislike Dislike 0
Love Love 0
Funny Funny 0
Angry Angry 0
Sad Sad 0
Wow Wow 0