ICU MessageFormat is the industry-standard syntax for internationalized messages that adapt to language-specific grammar — pluralization, gender, number, and date formatting — using CLDR rules, so one message string renders correctly in every language.
ICU MessageFormat is a syntax for creating internationalized messages that handle pluralization, gender, and other language-specific variations. Developed by theInternational Components for Unicode (ICU)project, it's the industry standard for complex i18n messages.
Unlike simple string interpolation, ICU MessageFormat handles the grammatical complexities of different languages. For example, Russian has different plural forms for 1, 2-4, 5-20, and 21+ items. ICU MessageFormat handles all of this automatically based on CLDR (Common Locale Data Repository) rules.
Insert dynamic values into your messages using curly braces.
{argumentName}Hello, {name}!Hello, John!
Welcome back, {firstName} {lastName}!Welcome back, John Doe!
Handle singular and plural forms based on a numeric value. ICU uses CLDR plural rules which vary by language.
{count, plural, =0 {zero} one {singular} other {plural}}{count, plural,
=0 {No messages}
one {1 message}
other {# messages}
}5 messages
You have {count, plural,
=0 {no items}
one {one item}
other {# items}
} in your cart.You have 3 items in your cart.
{count, plural, offset:1
=0 {Nobody liked this}
=1 {You liked this}
one {You and 1 other liked this}
other {You and # others liked this}
}You and 4 others liked this
Choose different text based on a string value. Commonly used for gender-aware messages.
{value, select, option1 {text1} option2 {text2} other {default}}{gender, select,
male {He}
female {She}
other {They}
} liked your photo.She liked your photo.
{role, select,
admin {Full access granted}
editor {Edit access granted}
viewer {Read-only access}
other {Limited access}
}Full access granted
{status, select,
active {Your account is active}
pending {Verification pending}
suspended {Account suspended - contact support}
other {Unknown status}
}Your account is active
Format ordinal numbers (1st, 2nd, 3rd, 4th, etc.) with proper suffixes.
{count, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}You finished in {place, selectordinal,
one {#st}
two {#nd}
few {#rd}
other {#th}
} place!You finished in 1st place!
This is your {attempt, selectordinal,
one {#st}
two {#nd}
few {#rd}
other {#th}
} attempt.This is your 3rd attempt.
Format numbers with locale-aware formatting, including currencies, percentages, and compact notation.
{value, number} or {value, number, style}Total: {amount, number}Total: 1,234,567
Price: {price, number, ::currency/USD}Price: $99.99
Discount: {discount, number, ::percent}Discount: 25%
{views, number, ::compact-short} views1.2M views
Rating: {rating, number, ::.0}Rating: 4.5
Format dates and times with locale-aware patterns.
{value, date, style} or {value, time, style}Date: {date, date, short}Date: 1/15/24
Date: {date, date, medium}Date: Jan 15, 2024
Date: {date, date, long}Date: January 15, 2024
Date: {date, date, full}Date: Monday, January 15, 2024
Time: {time, time, short}Time: 3:30 PM
Posted on {date, date, medium} at {date, time, short}Posted on Jan 15, 2024 at 3:30 PM
Use XML-like tags for rich text formatting. Tags are passed as functions in your code.
<tagName>content</tagName>
Read our <link>Terms of Service</link>.
Read our Terms of Service (as a link).
This is <b>very important</b> information.
This is very important information (bold).
Don't have an account? <signup>Sign up free</signup>
Don't have an account? Sign up free (as a link)
Press <kbd>Ctrl</kbd> + <kbd>S</kbd> to save.
Press Ctrl + S to save (keyboard keys styled).
Escape special characters when you need them as literal text.
Use '' to escape or wrap in quotes
It''s a beautiful day!
It's a beautiful day!
Use ''{'' and ''}}'' for literal braces.Use { and } for literal braces.
The variable is called '{name}'.The variable is called {name}.
Combine multiple ICU constructs for complex messages.
Nest plural, select, and other formats inside each other
{gender, select,
male {{count, plural,
=0 {He has no messages}
one {He has 1 message}
other {He has # messages}
}}
female {{count, plural,
=0 {She has no messages}
one {She has 1 message}
other {She has # messages}
}}
other {{count, plural,
=0 {They have no messages}
one {They have 1 message}
other {They have # messages}
}}
}She has 5 messages
{taxable, select,
yes {Total: {total, number, ::currency/USD} (incl. {tax, number, ::currency/USD} tax)}
other {Total: {total, number, ::currency/USD}}
}Total: $99.99 (incl. $8.00 tax)
Different languages have different plural categories. ICU uses CLDR (Common Locale Data Repository) rules to determine which category applies for any given number.
| Language | Plural Categories | Example |
|---|---|---|
| English | one, other | 1 item, 2 items |
| French | one, other | 0 élément, 1 élément, 2 éléments |
| Russian | one, few, many, other | 1 сообщение, 2 сообщения, 5 сообщений |
| Arabic | zero, one, two, few, many, other | All 6 forms used |
| Japanese | other | No plural forms |
| Polish | one, few, many, other | 1 wiadomość, 2 wiadomości, 5 wiadomości |
Important: Always include the "other" category as a fallback. Some languages like Japanese have only "other", while Arabic uses all 6 categories.View all CLDR plural rules →
ICU MessageFormat is supported by most major i18n libraries. Here's how to use it:
import { FormattedMessage } from 'react-intl';
<FormattedMessage
id="cart.items"
defaultMessage="{count, plural, one {# item} other {# items}}"
values={{ count: 5 }}
/>import { useTranslation } from '@/hooks/useTranslation';
const { t } = useTranslation('cart');
t('items', { count: 5 });
// messages: { "cart": { "items": "{count, plural, one {# item} other {# items}}" } }import i18next from 'i18next';
import ICU from 'i18next-icu';
i18next.use(ICU).init({ ... });
i18next.t('cart.items', { count: 5 });import { createIntl } from '@formatjs/intl';
const intl = createIntl({ locale: 'en', messages });
intl.formatMessage(
{ id: 'cart.items' },
{ count: 5 }
);ICU MessageFormat is a standardized syntax for writing internationalized messages that adapt to language-specific grammar — pluralization, gender, number, and date formatting. Developed by the Unicode ICU project, it uses CLDR plural rules so a single message string renders correctly across languages, from English (one, other) to Arabic (all six plural categories).
ICU plural syntax is written as {count, plural, one {# item} other {# items}}, where the engine picks a branch based on the CLDR plural category for the number. The # symbol is replaced by the formatted number, exact matches like =0 take precedence over categories, and the "other" branch is required as a fallback. Categories are zero, one, two, few, many, and other.
The select format chooses text based on a string value rather than a number, written as {gender, select, male {He} female {She} other {They}}. It is most often used for gender-aware pronouns or role/status messages. Like plural, an "other" branch is required, and option names are case-sensitive identifiers.
Wrap literal text in single quotes to escape special characters: '{' and '}' produce literal braces, and a doubled single quote ('') produces a literal apostrophe — for example "It''s a beautiful day!". Text inside single quotes is treated as literal and is not parsed as ICU syntax.
react-intl (FormatJS) and @formatjs/intl support ICU MessageFormat natively. next-intl supports it out of the box, and i18next supports it through the i18next-icu plugin. The same ICU string works across all of them, which makes ICU a portable choice for shared message catalogs.
Yes, for any user-facing text that varies by count, gender, or locale. Simple interpolation cannot express that Russian has four plural forms or that French and English pluralize differently, so it produces grammatically wrong translations. ICU MessageFormat encodes those rules in one string, which is why it is the industry standard for complex i18n.