Repository navigation
ICU Messages
📄 Mirrors
docs/icu-messages.mdin the main repo — the repo version is canonical.
Status: planned / pre-release. This page documents the intended API of next-tongue. Nothing is published yet; names may change before v1.0. See ROADMAP.
next-tongue uses ICU MessageFormat with its own small parser and a documented subset (ADR 0010); no third-party parser is used (ADR 0012). Messages are parsed at build time; invalid syntax fails the build with file, key, line and column. The parser is tested against an ICU conformance corpus vendored as test fixtures (#27).
| Syntax | Example | Inferred arg type |
|---|---|---|
| Argument | Hello {name} |
name: string | number |
| Number |
{n, number} / {n, number, ::percent} / {n, number, price}
|
n: number | bigint |
| Date/time |
{d, date, short} / {d, time}
|
d: Date | number |
Plural (incl. offset:N, =N, #) |
{count, plural, offset:1 =0 {none} one {# item} other {# items}} |
count: number |
| Selectordinal | {pos, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} |
pos: number |
| Select | {gender, select, female {She} male {He} other {They}} |
gender: 'female' | 'male' | (string & {}) |
| Rich tag | Read <b>this</b> |
b: (chunks) => ReactNode (via t.rich) |
| Rich tag (self-closing) | Line<br/>break |
br: () => ReactNode |
| Escaping (apostrophes) |
'{'literal'}', it''s
|
— |
| Area | Supported | Not supported (build error TONGUE_E_UNSUPPORTED_ICU) |
|---|---|---|
| Number styles |
integer, percent, currency (with config currency), named presets from formats.number
|
other legacy styles |
| Number skeletons |
::percent, ::currency/XXX, ::compact-short, ::compact-long, ::.00 / ::.## fraction digits, ::unit/..., ::sign-always, ::group-off
|
all other skeleton tokens |
| Date/time styles |
short, medium, long, full, presets from formats.dateTime
|
— |
| Date skeletons |
::yMMMd-style field skeletons mapped to Intl.DateTimeFormat fields |
pattern strings ('yyyy-MM-dd') |
| Other | — |
choice, spellout, ordinal/duration arg types |
tongue check and the build know CLDR plural rules for each locale. A Polish message missing few/many, or an English one with a useless few, is reported as a mismatch (configurable via strict.mismatch).
{ "inbox": "{count, plural, =0 {No messages} one {# message} other {# messages}}" }compiles (en) to roughly:
let pr; // created lazily on first call, never at module load
export const inbox = ({ count }) =>
count === 0 ? 'No messages' : (pr ??= new Intl.PluralRules('en')).select(count) === 'one' ? `${fmtN(count)} message` : `${fmtN(count)} messages`;No parser ships to the browser. See Architecture.
Nested JSON objects become dotted keys and namespaces: {"home": {"title": "…"}} → namespace home, key title, full key home.title. Keys may not contain ..
Add translator context with a sibling $description:
{ "save": "Save", "save$description": "Button label in the editor toolbar; max 12 chars" }Used by tongue translate and stripped from compiled output. For screenshots and length limits use $meta (Translator workflow).
Guides
- Getting Started
- Configuration
- Routing and Proxy
- Server Components
- Client Components
- Server Actions
- ICU Messages
- Type Safety
- Cache Components and PPR
- Static Export
- Metadata SEO and hreflang
- Database and CMS Messages
- Dev Tools
- Translator Workflow
- Per-user Preferred Language
- CLI
- AI Translation
- Testing
- VS Code Extension
- Migration from next intl
- Migration from next international (deferred)
Reference