PHPackages                             phpdot/i18n - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Caching](/categories/caching)
4. /
5. phpdot/i18n

ActiveLibrary[Caching](/categories/caching)

phpdot/i18n
===========

Internationalization with ICU MessageFormat, pluggable loaders, PSR-16 caching.

v1.2.3(4d ago)013MITPHPPHP &gt;=8.4

Since Apr 4Pushed 2mo agoCompare

[ Source](https://github.com/phpdot/i18n)[ Packagist](https://packagist.org/packages/phpdot/i18n)[ RSS](/packages/phpdot-i18n/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (14)Versions (8)Used By (0)

phpdot/i18n
===========

[](#phpdoti18n)

Internationalization with ICU MessageFormat, pluggable loaders, PSR-16 caching. Standalone.

Install
-------

[](#install)

```
composer require phpdot/i18n
```

Architecture
------------

[](#architecture)

 ```
graph LR
    subgraph Translator
        direction TB
        T[Translator]
        T --- translate
        T --- exposed
        T --- setLocale
        T --- clearCache
    end

    T -->|injected| CACHE[PSR-16 CacheInterface]
    T -->|injected| LOADER[LoaderInterface]
    T -->|uses internally| FMT[MessageFormatterext-intl]

    PHP[PhpArrayLoader] -.->|implements| LOADER
    JSON[JsonLoader] -.->|implements| LOADER
    CL[ChainLoader] -.->|implements| LOADER
    CL -->|wraps any| LOADER

    V[ICUValidator] -->|uses| FMT2[MessageFormatterext-intl]

    style T fill:#2d3748,color:#fff
    style V fill:#2d3748,color:#fff
    style CACHE fill:#4a5568,color:#fff
    style LOADER fill:#4a5568,color:#fff
    style FMT fill:#4a5568,color:#fff
    style FMT2 fill:#4a5568,color:#fff
    style PHP fill:#718096,color:#fff
    style JSON fill:#718096,color:#fff
    style CL fill:#718096,color:#fff
```

      Loading How It Works
------------

[](#how-it-works)

### Translation flow

[](#translation-flow)

 ```
flowchart TD
    A["translate(key, params)"] --> B{In-memory cache?}
    B -->|hit| F[Get template]
    B -->|miss| C{PSR-16 cache?}
    C -->|hit| D[Store in memory]
    D --> F
    C -->|miss| E["LoaderInterface.loadAll()"]
    E --> G[Store in PSR-16 + memory]
    G --> F
    F --> H{Key found incurrent language?}
    H -->|yes| K[Use template]
    H -->|no| I{Key found indefault language?}
    I -->|yes| K
    I -->|no| J["Return [key]Track in getMissing()"]
    K --> L["Inject _locale_, _region_, _lang_"]
    L --> M["MessageFormatter.format(params)"]
    M --> N[Return formatted string]

    style A fill:#2d3748,color:#fff
    style J fill:#9b2c2c,color:#fff
    style N fill:#276749,color:#fff
```

      Loading ### Caching

[](#caching)

Translations are cached per language at three levels:

1. **In-memory array** — avoids repeated cache reads within a single request/worker
2. **PSR-16 cache** — any backend (Redis, File, APCu via phpdot/cache or any PSR-16 implementation)
3. **Loader** — reads files/DB only on cold start or after `clearCache()`

Cache key format: `i18n.{language}` (e.g. `i18n.en`, `i18n.ar`).

```
// Admin changes a translation → clear cache → next request rebuilds
$translator->clearCache('ar');   // clear one language
$translator->clearCache();       // clear all languages
```

Usage
-----

[](#usage)

### Basic

[](#basic)

```
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']);
// → مرحباً Omar!
```

### Translation files

[](#translation-files)

**PHP format** (`lang/en/messages.php`):

```
return [
    'welcome' => 'Welcome, {name}!',
    'items' => '{count, plural, one {# item} other {# items}}',
];
```

**JSON format** (`lang/en/messages.json`):

```
{
    "welcome": "Welcome, {name}!",
    "items": "{count, plural, one {# item} other {# items}}"
}
```

Keys are prefixed by filename: `messages.welcome`, `messages.items`.

### ICU MessageFormat

[](#icu-messageformat)

All translations use ICU MessageFormat — one syntax for everything:

```
// Simple replacement
'Welcome, {name}!'

// Pluralization
'{count, plural, one {# item} other {# items}}'

// Gender/Select
'{gender, select, male {He} female {She} other {They}} liked this.'

// Regional variants (auto-injected _region_)
'{_region_, select, JO {رقم الموبايل} SA {رقم الجوال} other {رقم الهاتف}}'

// Number formatting
'{amount, number, currency}'

// Date formatting
'{date, date, long}'
```

### Auto-injected context

[](#auto-injected-context)

Every `translate()` call automatically injects:

ParamValueExample`_locale_`Full locale`ar_JO``_lang_`Language code`ar``_region_`Region code`JO`Use in ICU `select` for regional variants without separate files.

### Fallback

[](#fallback)

```
Current language → Default language → [key] placeholder

```

Missing keys are tracked via `getMissing()`.

### With DB overrides

[](#with-db-overrides)

```
use PHPdot\I18n\I18nConfig;
use PHPdot\I18n\Loader\ChainLoader;
use PHPdot\I18n\Loader\PhpArrayLoader;

$config = new I18nConfig(
    default: 'en',
    supported: ['en', 'ar'],
    path: '/app/lang',
);

$translator = new Translator(
    loader: new ChainLoader([
        new PhpArrayLoader($config),      // file defaults
        new DbLoader($db, $tenantId),     // admin overrides (app-level)
    ]),
    cache: $cache,
    config: $config,
);
```

Last loader wins for duplicate keys. Admin saves a translation → validate with `ICUValidator` → upsert to DB → `clearCache()`.

Frontend Exposure
-----------------

[](#frontend-exposure)

`exposed()` returns raw ICU templates for frontend rendering via [intl-messageformat](https://www.npmjs.com/package/intl-messageformat).

### Pattern syntax

[](#pattern-syntax)

Patterns use dot-separated segments with wildcard support:

PatternMatchesDoesn't match`js.buttons``js.buttons` + all children`js.errors.required``js.buttons.*``js.buttons.save`, `js.buttons.cancel``js.buttons.save.label``js.*.save``js.buttons.save`, `js.forms.save``js.save``*.welcome``messages.welcome``messages.goodbye``*.*.*`all 3-segment keys2-segment keys`js.**`everything under `js.` at any depth`messages.welcome``**`all translations—`*` matches exactly one segment. `**` matches one or more segments (recursive). No wildcard = prefix match (exact key + all children).

### Examples

[](#examples)

```
// Direct children of js.buttons
$translator->exposed(['js.buttons.*']);
// → ['js.buttons.save' => 'Save', 'js.buttons.cancel' => 'Cancel']

// All js translations at any depth
$translator->exposed(['js.**']);
// → ['js.buttons.save' => '...', 'js.buttons.cancel' => '...', 'js.errors.required' => '...']

// Wildcard in the middle
$translator->exposed(['js.*.save']);
// → ['js.buttons.save' => 'Save', 'js.forms.save' => 'Save']

// Mix patterns and exact prefixes
$translator->exposed(['messages.welcome', 'js.buttons.*']);
// → ['messages.welcome' => '...', 'js.buttons.save' => '...', 'js.buttons.cancel' => '...']

// Everything
$translator->exposed(['**']);
```

`exposed()` uses the same 3-level cache as `translate()` — translations are loaded once, then filtered in memory. Current language merged on top of default language (current wins for duplicate keys).

### Validating templates

[](#validating-templates)

```
use PHPdot\I18n\ICUValidator;

$validator = new ICUValidator();

if (!$validator->isValid($template)) {
    $errors = $validator->validate($template);
    // ['Syntax error in ICU message pattern']
}
```

### Auditing missing translations

[](#auditing-missing-translations)

```
$missing = $translator->getMissing();
// ['ar' => ['settings.notifications', 'errors.rate_limit']]
```

Loaders
-------

[](#loaders)

LoaderSourceFormat`PhpArrayLoader``lang/{language}/*.php`PHP arrays`JsonLoader``lang/{language}/*.json`JSON files`ChainLoader`Multiple loadersMerged (last wins)CustomImplement `LoaderInterface`Any source`LoaderInterface` has one method: `loadAll(string $language): array`. Returns a flat key → ICU template map.

Framework Integration
---------------------

[](#framework-integration)

When used with PHPdot, classes are annotated with container attributes:

- `I18nConfig` — `#[Config('i18n')]` → hydrated from `config/i18n.php`
- `PhpArrayLoader` — `#[Singleton]` `#[Binds(LoaderInterface::class)]` → default loader
- `ICUValidator` — `#[Singleton]`
- `Translator` — `#[Scoped]` → fresh per request

Create `config/i18n.php` in your app:

```
return [
    'default' => 'en',
    'supported' => ['en', 'ar', 'fr'],
    'path' => __DIR__ . '/../lang',
    'ttl' => 3600,
];
```

Resolve from container:

```
$translator = $container->get(Translator::class);
$translator->setLocale('ar_JO');
echo $translator->translate('messages.welcome', ['name' => 'Omar']);
```

Attributes are inert metadata. Without the framework scanner, nothing reads them. Standalone usage works the same as before.

Requirements
------------

[](#requirements)

- PHP &gt;= 8.3
- ext-intl
- phpdot/contracts ^1.3
- psr/simple-cache ^3.0

`phpdot/container` is only needed if you want the lifecycle attributes (`#[Singleton]`, `#[Scoped]`, `#[Binds]`, `#[Config]`) to be honored. Without it, the attributes sit as inert metadata and the package works the same way through plain `new Translator(...)`.

Cross-package contract
----------------------

[](#cross-package-contract)

`Translator` implements `PHPdot\Contracts\I18n\MessageTranslatorInterface`. Other packages (e.g. `phpdot/error`) can depend on the interface alone — they do not need to require `phpdot/i18n`. Any custom translator with a matching `translate(string $key, array $params = []): string` signature works wherever the interface is consumed.

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance92

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~14 days

Recently: every ~21 days

Total

7

Last Release

4d ago

PHP version history (2 changes)v1.0.0PHP &gt;=8.3

v1.2.3PHP &gt;=8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/62e82421bda4b5d6ba9a47ba6d88caca060dcd0d1a2862f351f3a97657385db0?d=identicon)[phpdot](/maintainers/phpdot)

---

Top Contributors

[![phpdot](https://avatars.githubusercontent.com/u/252500?v=4)](https://github.com/phpdot "phpdot (10 commits)")

---

Tags

internationalizationi18nicutranslationpsr-16localemessageformat

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/phpdot-i18n/health.svg)

```
[![Health](https://phpackages.com/badges/phpdot-i18n/health.svg)](https://phpackages.com/packages/phpdot-i18n)
```

###  Alternatives

[tractorcow/silverstripe-fluent

Simple localisation for Silverstripe

91437.9k29](/packages/tractorcow-silverstripe-fluent)[skillshare/formatphp

Internationalize PHP apps. This library provides an API to format dates, numbers, and strings, including pluralization and handling translations.

8032.4k](/packages/skillshare-formatphp)[delight-im/i18n

Internationalization and localization for PHP

595.4k3](/packages/delight-im-i18n)[jrmajor/fluent

Fluent localization system for PHP

2918.2k7](/packages/jrmajor-fluent)[jrmajor/laravel-fluent

Fluent translations for Laravel

208.8k](/packages/jrmajor-laravel-fluent)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
