Skip to content

Topic · ICU

'Supports ICU' is not a yes-or-no answer.

ICU MessageFormat is how you write a sentence that stays grammatical when the number changes, when the gender changes, or when both change at once. Nearly every library claims support. What varies is how much of that survives the trip from your source file, through a catalog, through a build step, to the string a user reads.

01 — The problem

Where ICU quietly stops working

A message with a plural nested inside a select passes through several systems before it renders: an extractor, a catalog format, a translation tool, a compiler, and a runtime. Each one can flatten, re-order or drop a selector branch. Nothing errors — the message simply renders the wrong branch in one locale, and you find out from a support ticket.

02 — The approach

One vocabulary, and a proof that it survives

Palamedes is ICU MessageFormat throughout — not as an opt-in plugin, and not as a house dialect with ICU available as a setting.

ICU is the format, not an adapter

The same syntax you write in a macro is the syntax in the .po catalog and the syntax the runtime evaluates. There is no translation between an internal representation and ICU, because there is no internal representation.

Nested selectors are the test case, not the edge case

The checked fixture exercises plural nested inside select — the combination that breaks first — across extraction, macro transformation, catalog update, compilation and six executions of the transformed runtime function.

Unsupported formatters fail loudly

Palamedes supports fewer runtime formatter kinds than full ICU. The compiler reports the unsupported ones as errors at build time rather than rendering something plausible and wrong at three in the morning.

The claim stops at our boundary

This proves every stage Palamedes controls. A translation platform is an external boundary — what survives an import and export there depends on the product, the format and the project settings, and no honest table can claim otherwise.

03 — In code

A nested selector, written the way it renders.

Plural inside select

import { plural, select } from "@palamedes/core/macro"

select(gender, {
  female: plural(count, {
    one: "She invited one guest",
    other: "She invited # guests",
  }),
  other: plural(count, {
    one: "They invited one guest",
    other: "They invited # guests",
  }),
})

This structure is what lands in the .po catalog and what the runtime evaluates. The proof asserts that all three representations still agree after a full round trip.

04 — Evidence

An executable proof, not a checkbox

The proof is a fixture in the repository. It compares exact messages and selector structure at each stage rather than asserting that ICU is 'supported'.

What it exercises
Nested select + pluralThe combination that fails first in pipelines that only claim ICU support.
Stages covered
5Extraction, macro transformation, PO catalog update, catalog compilation, runtime rendering.
Runtime executions
6The transformed function is executed across selector combinations and compared against expected output.
Where it runs
In CIChecked in, re-runnable locally, and failing the build when a stage stops preserving structure.
Inspect and re-run the ICU proof

05 — Questions

Questions people actually ask

Answered here rather than buried three pages into the documentation.

What is ICU MessageFormat?
A standard syntax for messages whose wording depends on their data — plural categories, gendered select branches, ordinals, and number and date formatting. It is maintained as part of the International Components for Unicode and is understood well beyond JavaScript, which is what makes a translation vocabulary portable.
Does Palamedes support nested plural inside select?
Yes, and that specific combination is what the checked proof exercises, because it is the one that breaks first when a pipeline only claims ICU support.
Which ICU features are not supported?
Palamedes supports fewer runtime formatter kinds than full ICU. The compiler reports unsupported formatters as build errors rather than failing silently, so you find out before shipping rather than after — but you should check your own catalog against that surface before migrating.
Do .po catalogs preserve ICU structure?
Yes. The ICU string is the message, and the source string is the msgid, so a translator or a gettext-based tool sees exactly what the runtime will evaluate.
Is ICU the same as i18next's interpolation format?
No. i18next ships its own {{variable}} syntax and offers ICU through a plugin that replaces it. Both work; only one of them is a cross-platform standard your translation vendor already understands.

06 — Keep reading

Related pages

Every claim on this page is checked into the repository.