Skip to content

fix(ios): localize plural counts without Intl.PluralRules (Hermes gap) - #113

Merged
os-zhuang merged 1 commit into
mainfrom
fix/ios-plural-i18n
Jun 11, 2026
Merged

fix(ios): localize plural counts without Intl.PluralRules (Hermes gap)#113
os-zhuang merged 1 commit into
mainfrom
fix/ios-plural-i18n

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Found via iOS Simulator testing

On device the Apps screen showed "1 app installed" (English) while everything else was Chinese. Diagnostic on the iPhone 17 sim confirmed the cause:

Intl.PluralRules type: undefined
new Intl.PluralRules("zh") → THREW: Cannot read property 'prototype' of undefined

Hermes (React Native's JS engine) ships without Intl.PluralRules, and i18next v25's plural resolver depends on it — so every plural key (apps.installed, search.resultCount, search.lookingAcross, records.recordCount) falls back to the fallback language (en) on device, while non-plural keys localize fine. Web has Intl.PluralRules, which is why it never reproduced there.

Why not the polyfill

@formatjs/intl-pluralrules can't install here: Hermes declares Intl.PluralRules as a non-configurable stub, so the polyfill's defineProperty(Intl, "PluralRules", …) throws "property is not writable" (also crashed the app). i18next v25 always uses Intl.PluralRules regardless of compatibilityJSON v3/v4, so switching that flag doesn't help either.

Fix

A tiny tCount(baseKey, count) helper in lib/i18n.ts that:

  • selects the English _one/_other form manually (no Intl),
  • uses the single _other form for the other locales,
  • interpolates {{n}} (renamed from {{count}}) so i18next's own plural machinery never triggers.

Routed the 4 plural call sites through it. No new dependency.

Verified

On the iPhone 17 simulator the Apps screen now renders "已安装 1 个应用". e2e (33) + unit (1298) pass, tsc + lint clean.

🤖 Generated with Claude Code

Hermes (React Native's JS engine) ships without `Intl.PluralRules`, and
i18next's plural resolver depends on it — so on device every plural key fell
back to English ("1 app installed" instead of "已安装 1 个应用") while
non-plural keys localized fine. Web has `Intl.PluralRules`, so it never showed
there. Found via iPhone 17 simulator testing; diagnostic confirmed
`Intl.PluralRules === undefined` on Hermes.

The standard `@formatjs/intl-pluralrules` polyfill can't install here — Hermes
declares `Intl.PluralRules` as a non-configurable stub, so the polyfill's
`defineProperty` throws ("property is not writable"). Instead add a tiny
`tCount(baseKey, count)` helper that selects the English one/other form
manually (no Intl) and uses the single "other" form for the other locales; the
strings interpolate `{{n}}` so i18next's own plural machinery never triggers.

Routed the four plural call sites through it: apps installed count, search
result count, search "looking across N objects", and the record-list count.

Verified on device: "已安装 1 个应用" now renders. e2e (33) + unit (1298) pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit 4912ce6 into main Jun 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant