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-stringsApp localisation track
Takes an English-only app to its first extra language in gated steps, from readiness scan and string extraction to formatting fixes, pseudo-localisation, translation hand-off and linguistic QA.
app-localization-trackAutomate translation file sync
Designs the pipeline between a repo and translators, with key extraction, upload to a translation system or vendor, download of finished locales, CI checks for keys and fallbacks, and approvals.
automate-translation-file-syncBuild 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-glossaryDesign international name and address fields
Designs form fields, validation and storage for personal names, postal addresses and phone numbers that work across countries. Use for international sign-up, checkout or shipping forms.
design-international-address-and-name-fields