i18next-icu
An i18next format plugin that swaps i18next's interpolation syntax for full ICU MessageFormat, powered by intl-messageformat.
Repository Health
Technical Analysis
i18next-icu is an official i18next plugin that replaces i18next’s own interpolation/pluralization engine with the ICU MessageFormat spec, delegating all parsing to formatjs’s intl-messageformat library. Instead of i18next’s {{variable}} interpolation and _plural key suffixes, translators write standard ICU syntax such as {count, plural, one {# item} other {# items}}, giving locale-correct plural, select, and gender rules plus ICU number/date formatting for every language Intl supports.
The plugin is a thin adapter: it implements i18next’s i18nFormat interface (init, parse, addLookupKeys, clearCache) and forwards each translation lookup to IntlMessageFormat, with an internal memoization cache keyed by language/namespace/key so repeated t() calls for the same string don’t re-parse the ICU pattern. A recent addition hardens the internal path-walking utilities against prototype-pollution style keys (__proto__, constructor, prototype) and adds an optional escapeVariables mode that HTML-escapes interpolated values before they reach the ICU parser, guarding against strings like user:< breaking parsing or producing unexpected output.
Because only the ICU format is respected once the plugin is active, i18next’s own interpolation and plural helpers stop applying to translations using this plugin — teams adopt it specifically when they want a single, standards-based message format across an app’s translations rather than i18next’s homegrown syntax.
What You Get
- A single
ICUclass you pass toi18next.use()that fully replaces i18next’s built-in interpolation and plural resolution with ICU MessageFormat parsing. - Per-key memoization of compiled ICU message formatters, with configurable cache-clearing on i18next or resource-store events (
bindI18n/bindI18nStore). - A
parseErrorHandlerhook that lets you supply a fallback string (instead of throwing) whenever a translation fails to parse as valid ICU. - A
parseLngForICUhook to remap i18next language codes (e.g. pseudo-locales likeen-ZZ) to a locale ICU can format correctly. - An
escapeVariablesoption that HTML-escapes interpolated variable values before ICU parsing, plus prototype-pollution-safe path handling in the internal cache utilities. - TypeScript type definitions, including guidance for opting translation-key extraction out of i18next’s default
{{variable}}interpolation parser so ICU’s single-brace plural syntax doesn’t produce phantom variables.
Common Use Cases
- Apps that need correct plural/gender rules across many locales (Arabic’s six plural categories, Slavic language plurals, etc.) without hand-writing per-language plural logic.
- Teams migrating translation content from a FormatJS/react-intl-based stack onto i18next, where translators already write ICU MessageFormat strings.
- Products embedding locale-sensitive number, date, and currency formatting directly inside translation strings instead of pre-formatting values in application code.
- React apps combining
react-i18next’s<Trans>component with ICU-formatted strings, relying on the plugin’signoreTaghandling so<0></0>child placeholders don’t conflict with ICU parsing.
Under The Hood
Architecture
The package is a single-responsibility adapter: src/index.js exports an ICU class that satisfies i18next’s i18nFormat plugin contract (init, parse, addLookupKeys, clearCache), so i18next can swap its own interpolation/pluralization engine for this one at i18next.use(ICU).init(...) time. Each parse() call resolves options through a small defaults()-merge chain (i18nextOptions over caller-supplied options over prior instance options over library defaults), lazily compiles an IntlMessageFormat instance for the given translation string and language, and memoizes it in an internal mem object keyed by language.namespace.key via a custom setPath/getPath object-path walker in src/utils.js. That walker is deliberately hardened against __proto__/constructor/prototype path segments, so a maliciously-shaped translation key can’t be used to pollute Object.prototype through the cache. There is no other internal layering — one class, one utility module, and a direct dependency on intl-messageformat for the actual ICU parsing/formatting.
Tech Stack
Written in plain ES2015+ JavaScript, published as dual ESM/CommonJS builds (dist/es, dist/commonjs) plus a UMD bundle (i18nextICU.js / .min.js) produced via Babel and Rollup, with intl-messageformat (>=10.3.3 <12.0.0) declared as the sole peer dependency the plugin wraps. Type definitions ship as a hand-written index.d.ts module declaration rather than being compiler-generated. Development tooling includes Jest for tests, ESLint with the Airbnb config, and tsc --noEmit run as part of npm test purely for type-checking the surface against consumers’ TypeScript configs — there is no separate build-time type emission step.
Code Quality
The repo carries two focused Jest spec files: icu.spec.js covers core ICU parsing behavior (plurals across locales including Arabic’s six categories, custom number formats, error-handler fallback behavior, and react-i18next <Trans> placeholder compatibility), and a dedicated security.spec.js explicitly tests the prototype-pollution guards in the path-walking utilities. Coverage is tracked via Coveralls (.coveralls.yml). tsc --noEmit type-checks the codebase against index.d.ts as part of the test script, and ESLint (Airbnb ruleset) is configured for style enforcement. No GitHub Actions workflow file was present in this shallow clone, so CI configuration beyond the local test/lint scripts couldn’t be directly confirmed.
What Makes It Unique
The library itself is a thin, standard integration — it doesn’t reimplement ICU MessageFormat, it delegates entirely to intl-messageformat — so its value is less technical novelty and more filling a specific gap in i18next’s plugin surface for teams that want ICU-standard message syntax instead of i18next’s own interpolation dialect. The one genuinely deliberate technical choice is defensive: an escapeVariables option that HTML-escapes interpolated values before they reach the ICU parser, paired with the prototype-pollution-safe cache key walker, both added specifically to harden a plugin that sits in the path of user-supplied translation variables.
Used by 5 apps in this directory
Activepieces
Automation · AI Assistants
Open-source AI automation platform that converts 280+ workflow integrations into MCP servers for LLMs, with no-code builders and TypeScript extensibility.
Formbricks
Forms Surveys · Marketing · Analytics
Open-source experience management platform for in-app, website, email, and link surveys — privacy-first and fully self-hostable.
PeerTube
Social Media
A federated, ActivityPub-based video hosting platform built by Framasoft — self-hostable instances interconnect into a network with no vendor lock-in, P2P-assisted streaming, and no ads.
Plane
Productivity · Project Management · Collaboration
Open-source project management platform to replace Jira, Linear, and ClickUp — with built-in cycles, real-time collaborative docs, and full self-hosting.
Rallly
Scheduling
Self-hostable group scheduling polls that eliminate back-and-forth emails and find the best meeting time for everyone.