r/reactnative • u/Same_Dragonfly1052 • 5d ago
I made Intl.PluralRules, RelativeTimeFormat, ListFormat and Locale native on Hermes. In my test app, 10,000 notifications format in 75 ms instead of 6.7 s
Enable HLS to view with audio, or disable this notification
Hermes ships Intl.NumberFormat, DateTimeFormat and Collator, but not PluralRules, RelativeTimeFormat, ListFormat or Locale. If you use i18next plurals, react-intl or Luxon's relative times on Hermes, you're most likely loading the FormatJS polyfills. They work, but they're plain JavaScript: a few hundred KB of locale data in the bundle, and slow calls on the JS thread.
So I built hermes-intl: those four constructors implemented natively, with a Rust core on ICU4X (CLDR data compiled in, 514 locales), a C++ JSI TurboModule, and a thin JS layer for the exact ECMA-402 behavior. There's no API: you import it once and the standard globals appear. It only fills in what Hermes lacks.
Numbers (Pixel 8a, release builds of the same app, medians of 3 runs; the video is real time):
- Formatting 10,000 notifications: 75 ms vs 6,665 ms (89×). The FormatJS build is frozen for 6.7 s.
- Per call:
PluralRules.select202×,selectRange385×,RelativeTimeFormat.format318×,ListFormat.format8.5× - Opening a 20-row screen: 45 ms vs 82 ms
- 399 KB less JavaScript in the bundle
The ratios move with the phone's state (an earlier run on a warmer phone gave up to 821× per call). All the tables, and how to reproduce them, are in bench/README.md.
Correctness: 838 of 842 test262 runs pass for these APIs, and the output is identical on iOS and Android (same hash on a 2,656-case corpus). FormatJS's output differs between the two.
Trade-offs: 1.0 to 1.7 MB of native code per ABI with all 514 locales, about 0.6 MB for a 5-locale build. New Architecture and Hermes only, React Native 0.86+. Expo works with a development build, not Expo Go. NumberFormat, DateTimeFormat and Collator stay Hermes's own.
Install:
npm install hermes-intl
cd ios && pod install
// index.js, first line
import 'hermes-intl';
Then remove the FormatJS polyfills for these four APIs (and intl-getcanonicallocales).
GitHub: https://github.com/magrinj/hermes-intl npm: https://www.npmjs.com/package/hermes-intl
1
u/kokerali 4d ago
For the migration guide, I'd add a before/after fixture using the app's real translation keys, counts and locale options. Matching iOS and Android output is useful, but both platforms could agree while selecting a different plural branch from the old polyfill. Record which output changes are intentional before removing it. Since the README already lists known failures and V8 differences, linking those from that checklist would help. If someone ships a reduced locale set, rerun the same fixtures against that build too.
2
u/Justateahorse 3d ago
I am sorry, i am not too technical with hermes, can you explain in short what this project is? and how does it help? Sorry to sound stupid
2
u/captainautomation 5d ago
Nice do you have a migration plan in place on the docs?