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 { useTranslations } from 'next-intl';
const t = useTranslations('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 }
);