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 (Документация на русском)
- Installation
- Basic Usage
- Key Concepts
- Creating a Translator
- Locales
- Translation Files
- Pluralization
- ICU Message Format
- Message Builders
- Caching
- Locale Detection
- PSR-15 Middleware
- License
composer require bermudaphp/polyglot
// 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 !"
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
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);
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
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
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']
Polyglot supports multiple formats for translation 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'
]
]
];
{
"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"
}
}
}
Nested keys can be accessed using dot notation:
echo $i18n->t('user.profile.title'); // "User Profile"
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"
Polyglot supports the ICU (International Components for Unicode) message format, a powerful standard for handling translations with variables, pluralization, and more.
// Translation
'greeting' => 'Hello, {name}!'
// Usage
echo $i18n->t('greeting', ['name' => 'John']); // "Hello, John!"
// Translation
'price' => 'Price: {amount, number, currency}'
// Usage
echo $i18n->t('price', ['amount' => 1234.56]); // "Price: $1,234.56" (in en-US)
// Translation
'today' => 'Today is {date, date, medium}'
// Usage
echo $i18n->t('today', ['date' => new DateTime()]); // "Today is Jan 1, 2023"
// 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"
// Translation
'gender' => '{gender, select, male{He} female{She} other{They}} will attend.'
// Usage
echo $i18n->t('gender', ['gender' => 'female']); // "She will attend."
// 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"
Polyglot provides a set of builders to programmatically create ICU message templates.
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
);
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' => 'ов'
]);
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');
});
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();
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
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
The Polyglot library is open-sourced software licensed under the MIT license.