Write ICU plural and select messages
Converts messages with counts, gender or choices into correct ICU MessageFormat for each target locale's plural categories, with test values. Use when strings depend on a number or gender.
English has two plural forms, so English-speaking developers write count === 1 ? "item" : "items" and ship it. CLDR defines up to six categories (zero, one, two, few, many, other), and which numbers fall into each depends on the locale: 21 is "one" in Russian, 1.5 is "one" in French but "other" in English, and Japanese has only "other". ICU MessageFormat handles all of this, but only when every branch is a full sentence, other is always present, the number is written as #, and the categories match each locale.
Convert these messages to ICU MessageFormat for the locales :
- For each message, identify the variables and their kinds: a count (cardinal plural), a rank (ordinal,
selectordinal), gender or another category (select), or plain interpolation. - Write the source-language message first:
- Use
#for the formatted count inside plural branches. - Use
=0(or any exact=N) only for wording that is genuinely special ("No messages"), never as a stand-in for a plural category. - Use
offset:1for patterns like "You and # others". - Put
selectoutside andpluralinside when both apply, and make every branch a complete sentence. Never assemble fragments around a plural. - Every
plural,selectandselectordinalhas anotherbranch. - Escape a literal apostrophe as
''and literal braces with apostrophe quoting.
- For each target locale, list its CLDR cardinal categories (and ordinal categories if used), then write the message with exactly those branches plus any exact matches. Translations: . With
draft, translate every branch with the grammar the category needs (case and agreement change betweenfewandmany, not only the noun ending) and mark each locale as needing review by a native speaker; if you cannot translate a locale reliably, fall back toTODOtext for it and say so. Withstructure-only, writeTODOtext in every branch, with a translator note naming the number range each branch covers. - Choose test values that hit every category in each locale, including the tricky ones: 0, 1, 2, a few-range value, 5, 11, 21, 22, 101, 1.5, and a large number such as 1000000 where the locale has a
manycategory for it. - If a runtime is available, verify categories with
Intl.PluralRules(or the ICU library) and say you did. Otherwise state that the categories come from CLDR rules.
- Never translate ICU keywords, argument names or category names.
- Do not reduce a locale's categories to make messages shorter; missing categories fall back to
otherand read wrongly. - If a message cannot work as ICU without a copy change (for example, a count embedded in a fragment shared across messages), say so and propose the reworded source.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
Messages
For each key: the source message, then one code block per locale, headed by the locale and its category list.
Test values
| Key | Locale | Value | Category | Expected output |
Notes
Copy changes needed, translations to review, and any category you could not confirm.
Key inbox.unread, variable count, locales en and ru.
en (one, other):
{count, plural, =0 {You have no unread messages} one {You have # unread message} other {You have # unread messages}}ru (one, few, many, other):
{count, plural, =0 {У вас нет непрочитанных сообщений} one {У вас # непрочитанное сообщение} few {У вас # непрочитанных сообщения} many {У вас # непрочитанных сообщений} other {У вас # непрочитанного сообщения}}Test values for ru: 1 and 21 are one; 2 and 22 are few; 5, 11 and 100 are many; 1.5 is other.
2 required values still a placeholder; the assistant will ask for them.
details
- kind
- Prompt: a task you run by name to get one finished thing back
- domain
- Software engineering
- category
- Localization (software)
- level
- Intermediate
- made for
- Software engineer, Frontend engineer, Mobile engineer
- risk
- read-only
- version
- v1.0.0 · incubating
- reviewed
- 2026-10-02
- works in
- Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md, ChatGPT, claude.ai
use in
npx @hermes-hq/hodios install write-icu-plural-messages --target claude-codenpx skills add hermes-hq/hodios-dist --skill write-icu-plural-messages -a claude-codeclaude plugin marketplace add hermes-hq/hodios-distclaude plugin install hodios-software-engineering@hodiosThe plugin brings every entry in this domain at once.
pairs well with
All of Localization (software)Translate a software string catalog
Translates a software string file (JSON, PO, XLIFF, ARB, Android or Apple strings) keeping keys, placeholders, plurals and length limits intact. Use when localizing an app's UI text.
translate-string-catalogQA a translated string catalog
QA-checks a translated catalog against its source for placeholder mismatches, broken syntax, truncation risk, terminology drift and untranslated strings. Use before merging translations.
review-translated-stringsBuild a localization glossary
Builds a product term base from UI strings, with definitions, do-not-translate terms and proposed translations per locale, so localization stays consistent. Use before the first translation round.
build-localization-glossaryExtract hard-coded UI strings
Finds hard-coded user-facing strings and moves them into an i18n catalog with meaningful keys and translator comments, without changing behaviour. Use when preparing an app for translation.
extract-ui-stringsPlan and implement right-to-left support
Plans and implements right-to-left layout support (logical properties, mirroring rules, bidi text, icons) for a web or mobile UI. Use when adding Arabic, Hebrew, Persian or Urdu.
plan-rtl-supportReview code for internationalization bugs
Reviews code for internationalization bugs such as concatenated strings, hard-coded date, number and currency formats, naive plurals, text expansion and RTL breakage. Use before adding new locales.
review-i18n-readiness