Reference Guide

ICU MessageFormat Syntax

The complete guide to ICU MessageFormat for internationalization. Learn pluralization, gender select, number formatting, and more.

What is ICU MessageFormat?

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.

Simple Arguments

Insert dynamic values into your messages using curly braces.

Syntax
{argumentName}
Examples
Basic variable interpolation
Code
Hello, {name}!
Output

Hello, John!

Multiple variables
Code
Welcome back, {firstName} {lastName}!
Output

Welcome back, John Doe!

Plural Format

Handle singular and plural forms based on a numeric value. ICU uses CLDR plural rules which vary by language.

Syntax
{count, plural, =0 {zero} one {singular} other {plural}}
Examples
Basic plural with exact match (=0) and categories
Code
{count, plural,
  =0 {No messages}
  one {1 message}
  other {# messages}
}
Output

5 messages

Plural embedded in a sentence
Code
You have {count, plural,
  =0 {no items}
  one {one item}
  other {# items}
} in your cart.
Output

You have 3 items in your cart.

Plural with offset for "You and X others" pattern
Code
{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}
}
Output

You and 4 others liked this

Notes
  • The # symbol is replaced with the formatted number
  • CLDR plural categories: zero, one, two, few, many, other
  • English only uses "one" and "other", but Arabic has all 6 categories
  • Exact matches (=0, =1, =2) take precedence over categories
  • The "other" category is required and acts as the fallback

Select Format

Choose different text based on a string value. Commonly used for gender-aware messages.

Syntax
{value, select, option1 {text1} option2 {text2} other {default}}
Examples
Gender-aware pronouns
Code
{gender, select,
  male {He}
  female {She}
  other {They}
} liked your photo.
Output

She liked your photo.

Role-based messaging
Code
{role, select,
  admin {Full access granted}
  editor {Edit access granted}
  viewer {Read-only access}
  other {Limited access}
}
Output

Full access granted

Status messages
Code
{status, select,
  active {Your account is active}
  pending {Verification pending}
  suspended {Account suspended - contact support}
  other {Unknown status}
}
Output

Your account is active

Notes
  • The "other" option is required as a fallback
  • Option names must be valid identifiers (no spaces or special characters)
  • Select is case-sensitive - "Male" and "male" are different options

Select Ordinal Format

Format ordinal numbers (1st, 2nd, 3rd, 4th, etc.) with proper suffixes.

Syntax
{count, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}
Examples
Ranking/position display
Code
You finished in {place, selectordinal,
  one {#st}
  two {#nd}
  few {#rd}
  other {#th}
} place!
Output

You finished in 1st place!

Attempt counter
Code
This is your {attempt, selectordinal,
  one {#st}
  two {#nd}
  few {#rd}
  other {#th}
} attempt.
Output

This is your 3rd attempt.

Notes
  • Uses CLDR ordinal plural rules, not cardinal rules
  • In English: 1st, 2nd, 3rd, then 4th-20th, then 21st, 22nd, etc.
  • Other languages have different ordinal patterns

Number Format

Format numbers with locale-aware formatting, including currencies, percentages, and compact notation.

Syntax
{value, number} or {value, number, style}
Examples
Basic number formatting with thousands separator
Code
Total: {amount, number}
Output

Total: 1,234,567

Currency formatting
Code
Price: {price, number, ::currency/USD}
Output

Price: $99.99

Percentage formatting
Code
Discount: {discount, number, ::percent}
Output

Discount: 25%

Compact notation for large numbers
Code
{views, number, ::compact-short} views
Output

1.2M views

Fixed decimal places
Code
Rating: {rating, number, ::.0}
Output

Rating: 4.5

Notes
  • Number skeleton syntax starts with ::
  • Common currency codes: USD, EUR, GBP, JPY, CNY
  • Compact notations: compact-short (1.2M), compact-long (1.2 million)
  • Formatting is locale-aware (1,234.56 in en-US vs 1.234,56 in de-DE)

Date and Time Format

Format dates and times with locale-aware patterns.

Syntax
{value, date, style} or {value, time, style}
Examples
Short date format
Code
Date: {date, date, short}
Output

Date: 1/15/24

Medium date format
Code
Date: {date, date, medium}
Output

Date: Jan 15, 2024

Long date format
Code
Date: {date, date, long}
Output

Date: January 15, 2024

Full date format with weekday
Code
Date: {date, date, full}
Output

Date: Monday, January 15, 2024

Short time format
Code
Time: {time, time, short}
Output

Time: 3:30 PM

Combined date and time
Code
Posted on {date, date, medium} at {date, time, short}
Output

Posted on Jan 15, 2024 at 3:30 PM

Notes
  • Available styles: short, medium, long, full
  • Date/time formatting is fully locale-aware
  • The same date value can be used for both date and time formatting

Rich Text / Tags

Use XML-like tags for rich text formatting. Tags are passed as functions in your code.

Syntax
<tagName>content</tagName>
Examples
Inline links
Code
Read our <link>Terms of Service</link>.
Output

Read our Terms of Service (as a link).

Bold text
Code
This is <b>very important</b> information.
Output

This is very important information (bold).

Call-to-action link
Code
Don't have an account? <signup>Sign up free</signup>
Output

Don't have an account? Sign up free (as a link)

Keyboard shortcuts
Code
Press <kbd>Ctrl</kbd> + <kbd>S</kbd> to save.
Output

Press Ctrl + S to save (keyboard keys styled).

Notes
  • Tags are passed as render functions in your code
  • Tags can wrap variables: <b>{name}</b>
  • Tags can be nested: <b><link>text</link></b>
  • Self-closing tags are not supported

Escaping Special Characters

Escape special characters when you need them as literal text.

Syntax
Use '' to escape or wrap in quotes
Examples
Escape single quote with double quote
Code
It''s a beautiful day!
Output

It's a beautiful day!

Escape curly braces
Code
Use ''{'' and ''}}'' for literal braces.
Output

Use { and } for literal braces.

Show literal variable syntax
Code
The variable is called '{name}'.
Output

The variable is called {name}.

Notes
  • Single quotes are used for escaping in ICU
  • Double the single quote ('') to get a literal single quote
  • Text inside single quotes is treated as literal

Nested Messages

Combine multiple ICU constructs for complex messages.

Syntax
Nest plural, select, and other formats inside each other
Examples
Gender + Plural combination
Code
{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}
  }}
}
Output

She has 5 messages

Conditional with formatted numbers
Code
{taxable, select,
  yes {Total: {total, number, ::currency/USD} (incl. {tax, number, ::currency/USD} tax)}
  other {Total: {total, number, ::currency/USD}}
}
Output

Total: $99.99 (incl. $8.00 tax)

Notes
  • Nesting can make messages complex - consider breaking into separate keys
  • Each nested level must have its own opening and closing braces
  • The "other" case is always required in nested constructs

CLDR Plural Rules by Language

Different languages have different plural categories. ICU uses CLDR (Common Locale Data Repository) rules to determine which category applies for any given number.

LanguagePlural CategoriesExample
Englishone, other1 item, 2 items
Frenchone, other0 élément, 1 élément, 2 éléments
Russianone, few, many, other1 сообщение, 2 сообщения, 5 сообщений
Arabiczero, one, two, few, many, otherAll 6 forms used
JapaneseotherNo plural forms
Polishone, few, many, other1 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 →

Using ICU in Popular Libraries

ICU MessageFormat is supported by most major i18n libraries. Here's how to use it:

react-intl / FormatJS
import { FormattedMessage } from 'react-intl';

<FormattedMessage
  id="cart.items"
  defaultMessage="{count, plural, one {# item} other {# items}}"
  values={{ count: 5 }}
/>
next-intl
import { useTranslations } from 'next-intl';

const t = useTranslations('cart');
t('items', { count: 5 });
// messages: { "cart": { "items": "{count, plural, one {# item} other {# items}}" } }
i18next (with ICU plugin)
import i18next from 'i18next';
import ICU from 'i18next-icu';

i18next.use(ICU).init({ ... });
i18next.t('cart.items', { count: 5 });
@formatjs/intl
import { createIntl } from '@formatjs/intl';

const intl = createIntl({ locale: 'en', messages });
intl.formatMessage(
  { id: 'cart.items' },
  { count: 5 }
);

Best Practices

Do

  • Always include the "other" fallback case
  • Use # for the formatted number in plurals
  • Test with various numbers (0, 1, 2, 5, 11, 21)
  • Keep messages readable - break complex ones into parts
  • Use descriptive variable names

Don't

  • Forget "other" - it causes runtime errors
  • Assume all languages have the same plural forms
  • Nest more than 2-3 levels deep
  • Use string concatenation for i18n messages
  • Hardcode number/date formats

Ready to try ICU MessageFormat?

Use our free ICU Message Editor to build and test messages with live preview, 100+ templates, and syntax validation.