ICU Syntax & Rich Text Rendering
ts-intl supports standard ICU MessageFormat 1.x syntax.
Plural Support (Pluralization)
Structured Plural Objects
Supports nested object forms following CLDR standard plural categories (zero, one, two, few, many, other):
const messages = {
cart: {
zero: "Your cart is empty",
one: "1 item in your cart",
other: "{count} items in your cart",
},
} as const;
const t = getTranslations("en-US");
t("cart", { count: 0 }); // "Your cart is empty"
t("cart", { count: 1 }); // "1 item in your cart"
t("cart", { count: 8 }); // "8 items in your cart"
Note: In the CLDR specification, English only defines the
oneandotherplural categories (ts-intladditionally provides built-in support forzerowhencount === 0). If you need differentiated text for specific numbers, it is recommended to use the inline ICUpluralsyntax below (such as=0).
Inline ICU plural Syntax
const messages = {
inbox:
"You have {count, plural, =0 {no unread messages} one {# message} other {# messages}}.",
} as const;
const t = getTranslations("en-US");
t("inbox", { count: 0 }); // "You have no unread messages."
t("inbox", { count: 1 }); // "You have 1 message."
t("inbox", { count: 12 }); // "You have 12 messages."
Ordinal Formatting (selectordinal)
Used to handle ordinal expressions (exact match = takes precedence over category rules):
const messages = {
podium:
"You finished {rank, selectordinal, =1 {first} =2 {second} =3 {third} other {#th}}!",
} as const;
const t = getTranslations("en-US");
t("podium", { rank: 1 }); // "You finished first!"
t("podium", { rank: 4 }); // "You finished 4th!"
Conditional Branch Selection (select)
Matches different text based on status enums or category values:
const messages = {
roleBadge:
"{role, select, admin {Administrator} moderator {Moderator} other {Member}}",
} as const;
const t = getTranslations("en-US");
t("roleBadge", { role: "admin" }); // "Administrator"
t("roleBadge", { role: "guest" }); // "Member"
General Rich Text Interpolation t.rich()
Maps text to frontend components or DOM nodes via XML-like tags, avoiding the XSS risks of using dangerouslySetInnerHTML:
const messages = {
notice:
"Please review our <terms>Terms of Service</terms> and accept the <privacy>Privacy Policy</privacy> before submitting.",
} as const;
const t = getTranslations("en-US");
// Map directly to components in React / Vue
const elements = t.rich("notice", {
terms: (children) => <a href="/terms" className="underline">{children}</a>,
privacy: (children) => <a href="/privacy" className="underline">{children}</a>,
});
HTML String Markup Rendering t.markup()
Used to generate strings containing safe HTML tags:
const html = t.markup("notice", {
terms: (c) => `<a href="/terms">${c}</a>`,
privacy: (c) => `<a href="/privacy">${c}</a>`,
});