phpdot / i18n
Internationalization with ICU MessageFormat, pluggable loaders, PSR-16 caching.
Requires
- php: >=8.5
- ext-intl: *
- phpdot/contracts: ^0.1
- psr/simple-cache: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpdot/container: ^0.1
- phpstan/phpstan: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^13.0
README
Internationalization built on ICU MessageFormat (via ext-intl): one syntax for interpolation,
pluralization, and selection. Translations load through pluggable loaders (PHP arrays, JSON, or a chain),
compiled maps are cached through any PSR-16 cache, and the translator implements the
PHPdot\Contracts\I18n\MessageTranslatorInterface so the rest of the ecosystem depends only on the
contract.
Table of Contents
Requirements
| Requirement | Constraint |
|---|---|
| PHP | >= 8.5 |
ext-intl |
* |
phpdot/contracts |
^0.1 |
psr/simple-cache |
^3.0 |
phpdot/container is a dev-only suggestion — the #[Config('i18n')] / binding attributes are inert
until a phpdot application reflects them.
Installation
composer require phpdot/i18n
Usage
use PHPdot\I18n\I18nConfig; use PHPdot\I18n\Translator; use PHPdot\I18n\Loader\PhpArrayLoader; $config = new I18nConfig( default: 'en', supported: ['en', 'ar', 'fr'], path: '/app/lang', ); $translator = new Translator( loader: new PhpArrayLoader($config), cache: $cache, // any PSR-16 implementation config: $config, ); $translator->setLocale('ar_JO'); echo $translator->translate('messages.welcome', ['name' => 'Omar']);
Translation files
Keys are prefixed by filename, so lang/en/messages.php gives messages.welcome:
// lang/en/messages.php return [ 'welcome' => 'Welcome, {name}!', 'items' => '{count, plural, one {# item} other {# items}}', ];
JsonLoader reads the same shape from .json files; ChainLoader layers several loaders so later
sources (e.g. database overrides) win. Every template is ICU MessageFormat — interpolation,
plural, and select all share one syntax — and ICUValidator can check a template before it ships.
Architecture
Translator resolves a key to its ICU template through the configured LoaderInterface, formats it
with the current locale using ext-intl's MessageFormatter, and caches the compiled per-language map in
a PSR-16 cache. Unknown keys are recorded so missing translations can be audited.
graph TD
APP["Application / Contracts MessageTranslatorInterface"]
TRANSLATOR["Translator<br/><br/>resolve key → ICU template → format for locale"]
LOADER["LoaderInterface<br/><br/>PhpArrayLoader / JsonLoader / ChainLoader"]
CACHE["PSR-16 cache<br/><br/>compiled per-language maps"]
ICU["ext-intl MessageFormatter<br/><br/>interpolation, plural, select"]
APP --> TRANSLATOR
TRANSLATOR --> LOADER
TRANSLATOR --> CACHE
TRANSLATOR --> ICU
Loading
Testing
composer install composer test # PHPUnit composer analyse # PHPStan, level max + strict rules composer cs-check # PHP-CS-Fixer composer check # All three
License
MIT.
This repository is a read-only mirror, generated by CI from phpdot/monorepo. Pull requests and issues belong in the monorepo.