bermudaphp / polyglot
A powerful, flexible internationalization (i18n) and localization (l10n) library for PHP 8.4+ applications with support for ICU message formatting, pluralization, caching, and more.
Requires
- php: ^8.4
- psr/http-message: ^1.0|^2.0
- psr/http-server-middleware: ^1.0
- psr/simple-cache: ^1.0|^2.0|^3.0
Requires (Dev)
- mikey179/vfsstream: ^1.6.12
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^11.5
Suggests
- ext-intl: Required for ICU message formatting with native implementation
- symfony/cache: For PSR-16 cache implementation
This package is auto-updated.
Last update: 2025-05-22 13:28:23 UTC
README
Polyglot is a powerful, flexible internationalization (i18n) and localization (l10n) library for PHP 8.4+ applications with support for ICU message formatting, pluralization, caching, and more.
Read documentation in Russian (Документация на русском)
Table of Contents
- Installation
- Basic Usage
- Key Concepts
- Creating a Translator
- Locales
- Translation Files
- Pluralization
- ICU Message Format
- Message Builders
- Caching
- Locale Detection
- PSR-15 Middleware
- License
Installation
composer require bermudaphp/polyglot
Basic Usage
// Create an I18n instance using the factory $i18n = I18nFactory::create('/path/to/translations', 'en', 'en'); // Basic translation echo $i18n->translate('welcome'); // "Welcome!" // Translation with parameters echo $i18n->translate('greeting', ['name' => 'John']); // "Hello, John!" // Translation with pluralization echo $i18n->translatePlural('items', 1); // "You have 1 item" echo $i18n->translatePlural('items', 5); // "You have 5 items" // Shorthand methods echo $i18n->t('welcome'); echo $i18n->tp('items', 3); // Change locale $i18n->setLocale('fr'); echo $i18n->t('welcome'); // "Bienvenue !"
Key Concepts
Polyglot uses several core concepts:
- Locale: Identifies a specific language and region (e.g., 'en_US', 'fr_FR')
- Domain: Groups related translations (e.g., 'messages', 'errors', 'admin')
- Translation Key: Unique identifier for a translatable string
- Fallback Locale: Used when a translation is missing in the primary locale
- Plurality: Different forms of words based on quantity (e.g., "1 item" vs "5 items")
- ICU Format: International Components for Unicode message format for complex translations
Creating a Translator
The simplest way to create a translator is using the factory:
use Bermuda\Polyglot\I18nFactory; $i18n = I18nFactory::create( resourcesPath: '/path/to/translations', defaultLocale: 'en', fallbackLocale: 'en', availableLocales: ['en', 'fr', 'de', 'es'], cache: null // Optional cache implementation );
For more control, you can create components manually:
use Bermuda\Polyglot\Translator; use Bermuda\Polyglot\Loader\PhpArrayMessageLoader; use Bermuda\Polyglot\Formatter\IcuMessageFormatter; use Bermuda\Polyglot\PluralRule\CldrPluralRuleProvider; use Bermuda\Polyglot\Cache\InMemoryCache; // Create loaders $loader = new PhpArrayMessageLoader('/path/to/translations'); // Create formatters $formatter = new IcuMessageFormatter(new CldrPluralRuleProvider()); // Create plural rule provider $pluralRuleProvider = new CldrPluralRuleProvider(); // Create cache (optional) $cache = new InMemoryCache(); // Create translator $translator = new Translator( locale: 'en', fallbackLocale: 'en', loader: $loader, formatter: $formatter, pluralRuleProvider: $pluralRuleProvider, cache: $cache ); // Create I18n instance $i18n = new I18n($translator);
Locales
Polyglot provides multiple ways to handle locale codes:
- As simple strings ('en', 'en_US', 'fr_FR')
- Using the
LocaleEnum
typed enum - Using the
Locale
value object
LocaleEnum
use Bermuda\Polyglot\LocaleEnum; $locale = LocaleEnum::ENGLISH_US; $i18n->setLocale($locale); // Get language code $language = $locale->getLanguageCode(); // 'en' // Get region code $region = $locale->getRegionCode(); // 'US' // Create from string $locale = LocaleEnum::fromString('en-US'); // Handles both 'en-US' and 'en_US' formats
Locale Value Object
use Bermuda\Polyglot\Locale; $locale = new Locale('en_US'); // Access components echo $locale->language; // 'en' echo $locale->region; // 'US' echo $locale->variant; // null // Convert to string echo $locale; // 'en_US' // Get fallbacks $fallbacks = $locale->getFallbacks(); // ['en']
Translation Files
Polyglot supports multiple formats for translation files:
PHP Array Files
// /path/to/translations/en/messages.php return [ 'welcome' => 'Welcome!', 'greeting' => 'Hello, {name}!', 'items' => [ 'one' => 'You have 1 item', 'other' => 'You have {count} items' ], 'user' => [ 'greeting' => 'Welcome back, {name}!', 'profile' => [ 'title' => 'User Profile' ] ] ];
JSON Files
{ "welcome": "Welcome!", "greeting": "Hello, {name}!", "items": { "one": "You have 1 item", "other": "You have {count} items" }, "user": { "greeting": "Welcome back, {name}!", "profile": { "title": "User Profile" } } }
Accessing Nested Keys
Nested keys can be accessed using dot notation:
echo $i18n->t('user.profile.title'); // "User Profile"
Pluralization
Polyglot provides robust support for pluralization based on the CLDR (Common Locale Data Repository) rules:
// Translation file return [ 'items' => [ 'one' => 'You have 1 item', 'other' => 'You have {count} items' ], 'apples' => [ 'one' => '{count} apple', 'few' => '{count} apples', // For languages like Russian, Polish 'many' => '{count} apples', // For languages like Russian, Polish 'other' => '{count} apples' ] ]; // Usage echo $i18n->translatePlural('items', 1); // "You have 1 item" echo $i18n->translatePlural('items', 5); // "You have 5 items" // With parameters echo $i18n->translatePlural('items', 5, ['color' => 'red']); // "You have 5 red items"
ICU Message Format
Polyglot supports the ICU (International Components for Unicode) message format, a powerful standard for handling translations with variables, pluralization, and more.
Parameter Substitution
// Translation 'greeting' => 'Hello, {name}!' // Usage echo $i18n->t('greeting', ['name' => 'John']); // "Hello, John!"
Number Formatting
// Translation 'price' => 'Price: {amount, number, currency}' // Usage echo $i18n->t('price', ['amount' => 1234.56]); // "Price: $1,234.56" (in en-US)
Date Formatting
// Translation 'today' => 'Today is {date, date, medium}' // Usage echo $i18n->t('today', ['date' => new DateTime()]); // "Today is Jan 1, 2023"
Plural Formatting
// Translation 'items' => '{count, plural, one{# item} other{# items}}' // Usage echo $i18n->t('items', ['count' => 1]); // "1 item" echo $i18n->t('items', ['count' => 5]); // "5 items"
Select Formatting
// Translation 'gender' => '{gender, select, male{He} female{She} other{They}} will attend.' // Usage echo $i18n->t('gender', ['gender' => 'female']); // "She will attend."
Nested Formatting
// Translation 'nested' => '{gender, select, male{He has {count, plural, one{# apple} other{# apples}}} female{She has {count, plural, one{# apple} other{# apples}}} other{They have {count, plural, one{# apple} other{# apples}}}}' // Usage echo $i18n->t('nested', ['gender' => 'female', 'count' => 3]); // "She has 3 apples"
Message Builders
Polyglot provides a set of builders to programmatically create ICU message templates.
ICU Message Builder
use Bermuda\Polyglot\Generator\IcuMessage; // Create a message builder for a specific locale $builder = IcuMessage::for('en'); // Create a simple message with placeholders $message = $builder->message('Hello, {name}!'); // Create a gender-specific message $gender = IcuMessage::gender( 'gender', 'He will attend', // male 'She will attend', // female 'They will attend' // other );
Plural Builder
use Bermuda\Polyglot\Generator\IcuMessage; // Create a plural builder for English $pluralBuilder = IcuMessage::plural('count', 'en'); // Add cases manually $pluralBuilder ->when('one', 'You have # item') ->when('other', 'You have # items'); // Get the ICU message $icuMessage = $pluralBuilder->build(); // Result: "{count, plural, one{You have # item} other{You have # items}}" // Using withInflections for common plurals $builder = IcuMessage::plural('count', 'en') ->withInflections('You have # item', [ 'one' => '', 'other' => 's' ]); // For languages with more complex pluralization (e.g., Russian) $builder = IcuMessage::plural('count', 'ru') ->withInflections('У вас # товар', [ 'one' => '', 'few' => 'а', 'many' => 'ов' ]);
Select Builder
use Bermuda\Polyglot\Generator\IcuMessage; // Create a select builder $selectBuilder = IcuMessage::select('role'); // Add cases $selectBuilder ->when('admin', 'You have full access') ->when('manager', 'You have limited access') ->otherwise('You have basic access'); // Get the ICU message $icuMessage = $selectBuilder->build(); // Result: "{role, select, admin{You have full access} manager{You have limited access} other{You have basic access}}" // Using with nested plural $builder = IcuMessage::select('gender') ->when('male', function($b) { return $b->plural('count', 'en') ->when('one', 'He has # apple') ->when('other', 'He has # apples'); }) ->when('female', function($b) { return $b->plural('count', 'en') ->when('one', 'She has # apple') ->when('other', 'She has # apples'); });
Caching
Polyglot supports caching of loaded translations to improve performance:
use Bermuda\Polyglot\Cache\PsrCacheAdapter; use Symfony\Component\Cache\Adapter\FilesystemAdapter; // Create a PSR-16 compatible cache $psr16Cache = new \Symfony\Component\Cache\Psr16Cache( new FilesystemAdapter() ); // Create a cache adapter $cache = new PsrCacheAdapter($psr16Cache); // Use with I18n factory $i18n = I18nFactory::create( resourcesPath: '/path/to/translations', defaultLocale: 'en', fallbackLocale: 'en', cache: $cache );
Also included is an InMemoryCache
for simple use cases:
use Bermuda\Polyglot\Cache\InMemoryCache; $cache = new InMemoryCache();
Locale Detection
Polyglot can automatically detect the user's preferred locale:
use Bermuda\Polyglot\Detector\HttpAcceptLanguageDetector; use Bermuda\Polyglot\Detector\QueryLocaleDetector; use Bermuda\Polyglot\Detector\PathLocaleDetector; use Bermuda\Polyglot\Detector\LocaleDetectorChain; // Available locales $availableLocales = ['en', 'fr', 'de', 'es']; // Create detectors $httpDetector = new HttpAcceptLanguageDetector($availableLocales); $queryDetector = new QueryLocaleDetector($availableLocales, 'locale'); $pathDetector = new PathLocaleDetector($availableLocales); // Create a chain of detectors (checked in order) $detector = new LocaleDetectorChain([ $pathDetector, // First check URL path (/en/page) $queryDetector, // Then check query param (?locale=en) $httpDetector // Finally check Accept-Language header ]); // Use with I18n $i18n = new I18n($translator, $detector); // Detect and set locale $i18n->detectAndSetLocale('en'); // 'en' is the default if no locale is detected
PSR-15 Middleware
Polyglot includes a PSR-15 middleware for detecting the locale from HTTP requests:
use Bermuda\Polyglot\I18nMiddleware; // Create middleware $middleware = I18nFactory::createMiddleware($i18n, 'en'); // Add to your middleware stack $app->pipe($middleware);
The middleware:
- Detects the locale from the request
- Sets it in the I18n instance
- Adds the locale as a request attribute
- Passes control to the next middleware
License
The Polyglot library is open-sourced software licensed under the MIT license.