BabelizeBabelize
SDK

ICU Message Format

Translate ICU MessageFormat messages with the Babelize SDK: plural, select, and selectordinal stay intact while only the human-readable text changes.

The SDK supports ICU MessageFormat — the standard for complex pluralization, gender selection, and ordinal numbers. Use babelize.icu() for messages that need more than a simple singular/plural split.

Basic Usage

import { babelize } from "@babelize/sdk";

babelize.icu(
  "{count, plural, one {# item} other {# items}}",
  { count: 3 },
);

How It Works

babelize.icu():

  1. Parses the message into an AST — no fragile string splitting.
  2. Translates the leaf text inside each plural/select/selectordinal option through the normal Babelize translation pipeline (cache → /v1/translate).
  3. Preserves the ICU structure exactly: # (pound), {arg} placeholders, number/date/time formatters, and rich-text tags are never touched.
  4. Formats the translated message with your values via Intl.MessageFormat.

If the string isn't valid ICU (or has no ICU syntax), it falls back to a plain t() translation.

The build plugins don't extract babelize.icu() messages into the lockfile. Each option's text is looked up like any babelize() string, so it is translated only when that exact text is in the lockfile or the SDK was initialized with an API key (server-side). In the browser, without a key, use babelize.p() or babelize.plural() for plurals that should be translated at build time.

Plural

babelize.icu("{count, plural, one {# item} other {# items}}", { count: 1 }); // "1 item"
babelize.icu("{count, plural, one {# item} other {# items}}", { count: 5 }); // "5 items"

Note the # is ICU's placeholder for the plural value — it's resolved automatically.

Select (gender, etc.)

babelize.icu(
  "{gender, select, male {He} female {She} other {They}}",
  { gender: "male" },
);

SelectOrdinal

babelize.icu("{n, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}", { n: 3 }); // "3rd"

Nested Plural + Select

babelize.icu(
  `{gender, select,
    male   {He has {n, plural, one {# apple} other {# apples}}}
    female {She has {n, plural, one {# apple} other {# apples}}}
    other  {They have {n} apples}}`,
  { gender: "female", n: 2 },
);

Arguments and Formatters Are Preserved

babelize.icu(
  "{count, plural, one {# item} other {# items}} — {total, number} in {folder}",
  { count: 2, total: 1234.5, folder: "inbox" },
);

The {total, number} formatter and {folder} placeholder pass through translation untouched and are formatted per the active locale.

In React

import { useBabelize } from "@babelize/sdk/react";

function CartItemCount({ count }: { count: number }) {
  const babelize = useBabelize();

  return (
    <p>
      {babelize.icu(
        "{count, plural, one {# item} other {# items}}",
        { count },
      )}
    </p>
  );
}

Translation Semantics

ICU structure is preserved exactly; only the human-readable text inside options is translated. This means a message authored once in English can be translated and formatted correctly in any locale — including languages whose plural rules differ from English — because the plural/select options are already expanded and the target translation is applied per option.

Further Reading

Last updated: 2026-11-01

How is this guide?

On this page