PHPackages                             preflow/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. [Localization &amp; i18n](/categories/localization)
4. /
5. preflow/i18n

ActiveLibrary[Localization &amp; i18n](/categories/localization)

preflow/i18n
============

Preflow i18n — translations, locale detection, pluralization

v0.13.1(3mo ago)0362MITPHPPHP &gt;=8.4

Since Apr 10Pushed 3mo agoCompare

[ Source](https://github.com/getpreflow/i18n)[ Packagist](https://packagist.org/packages/preflow/i18n)[ RSS](/packages/preflow-i18n/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (3)Versions (32)Used By (2)

Preflow I18n
============

[](#preflow-i18n)

Translations, locale detection, and pluralization for Preflow applications.

Installation
------------

[](#installation)

```
composer require preflow/i18n
```

What it does
------------

[](#what-it-does)

- Loads PHP translation files from `lang/{locale}/{group}.php`
- Resolves dot-notation keys with `:param` replacement and locale fallback
- ICU-style pluralization with exact, range, and simple two-form rules
- PSR-15 middleware for locale detection (URL prefix → cookie → Accept-Language)
- Twig functions for global and component-scoped translations

Configuration
-------------

[](#configuration)

`config/i18n.php`:

```
return [
    'default'      => 'en',
    'available'    => ['en', 'de'],
    'fallback'     => 'en',
    'url_strategy' => 'prefix', // 'prefix' | 'none'
];
```

Translation files
-----------------

[](#translation-files)

`lang/en/blog.php`:

```
return [
    'title'      => 'My Blog',
    'published'  => 'Published on :date',
    'post_count' => '{0} No posts|{1} One post|[2,*] :count posts',
];
```

API
---

[](#api)

### Translator

[](#translator)

```
$translator = new Translator(
    langPath: __DIR__ . '/lang',
    locale: 'en',
    fallbackLocale: 'en',
);

$translator->get('blog.title');                          // "My Blog"
$translator->get('blog.published', ['date' => '2026-01-01']); // "Published on 2026-01-01"
$translator->choice('blog.post_count', 5);              // "5 posts"
$translator->setLocale('de');
$translator->getLocale();                               // "de"
```

### PluralResolver

[](#pluralresolver)

Supports three formats:

```
{0} No posts|{1} One post|[2,*] :count posts   // exact + range
[2,10] Some|[11,*] Many                         // range only
One post|:count posts                           // simple two-form (1 = first, else second)

```

### LocaleMiddleware

[](#localemiddleware)

PSR-15 middleware. Detects locale in priority order: URL prefix → cookie → `Accept-Language` header → configured default. Strips the locale prefix from the request path and sets a `locale` cookie on the response.

```
$middleware = new LocaleMiddleware(
    translator: $translator,
    availableLocales: ['en', 'de'],
    defaultLocale: 'en',
    urlStrategy: 'prefix', // request to /de/blog strips /de, sets locale to "de"
);
```

### TranslationExtension (Twig)

[](#translationextension-twig)

Register with your Twig environment:

```
$twig->addExtension(new TranslationExtension($translator));
```

```
{# Simple key #}
{{ t('blog.title') }}

{# With parameters #}
{{ t('blog.published', { date: '2026-01-01' }) }}

{# Pluralization — third arg is the count #}
{{ t('blog.post_count', { count: 5 }, 5) }}

{# Component-scoped — resolves "my-component.label" #}
{{ tc('label', 'MyComponent') }}
```

`tc()` converts the component name from PascalCase to kebab-case and uses it as the translation group.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance80

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity53

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 ~0 days

Total

31

Last Release

104d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6d8b54efbc79683c32645e3fa8d3590037fa003963a16e0ed989104c6f4a2723?d=identicon)[smyr](/maintainers/smyr)

---

Top Contributors

[![smeyerme](https://avatars.githubusercontent.com/u/1925560?v=4)](https://github.com/smeyerme "smeyerme (7 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

PHPackages © 2026

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