An ICU plural in one language throws at runtime
A translated ICU message has a brace, keyword or argument the parser will not accept, so the string throws or renders as raw markup for that locale only.
What you see
- •One locale throws a formatting error, or renders `{count, plural, one {...}}` as visible text.
- •The JSON is valid, so the build and the type checker are satisfied.
- •It usually reaches production, because tests run in the source language.
Why it happens
ICU messages are a small language embedded in your strings. Braces have to balance, the plural keywords (`zero`, `one`, `two`, `few`, `many`, `other`) are syntax rather than words, and the argument name has to match what the call site passes. Translators see prose with punctuation in it, and any of those three can be edited without the edit looking like code.
1A plural keyword was translated
The parser looks for the literal keyword `other`. Translated into the target language it is no longer a branch, so selection fails and there is no fallback form.
Tamil translated the `other` keyword to `மற்ற` in four time-remaining strings.
Our report — filed 2026-09-14, labelled by the team the same day →
2Braces doubled or left unbalanced
A doubled opening brace makes the argument a literal; a missing closing brace swallows the rest of the message. Both produce a parse error rather than a wrong word.
German opened a plural with `{{count, plural…`, and a Slovenian string left its braces unbalanced. Ukrainian lost an argument from a title in the same sweep.
Find every instance
Check the locale directory; invalid ICU is reported separately from a dropped placeholder:
npx @shipi18n/cli@latest check ./locales -s en --severity 'missing-key=off,untranslated=off,orphan-key=off'Messages are read through a real ICU parser rather than a regex, so arguments nested inside plural and select branches count as present, and a string that will not parse is reported as such.
✗ de coverage 100.0% 1 error(s), 0 warning(s)
error notification_requests.confirm_accept_multiple.message icu-invalid — could not parse: unexpected {How to fix it
- 1Keep the keywords in English. `one`, `few`, `many` and `other` are syntax; only the text inside each branch is translated.
- 2Add the branches your language needs rather than reusing the English set. Russian and Polish need `few` and `many`; leaving them out silently falls through to `other`.
- 3Run the check in CI so an unparseable message cannot ship in a language your tests never render.
The rules that catch this
How often this actually happens
Invalid ICU was the most severe class our scan found, because it fails loudly for the affected users and silently for everyone else: https://shipi18n.com/oss