ts-intl
Главы:Синтаксис ICU и рендеринг форматированного текста

Синтаксис ICU и рендеринг форматированного текста

ts-intl поддерживает стандартный синтаксис ICU MessageFormat 1.x.

Поддержка множественного числа (Pluralization)

Структурированные объекты множественного числа

Поддерживается формат вложенных объектов, соответствующий стандартным категориям множественного числа CLDR (zero, one, two, few, many, other):

const messages = {
  cart: {
    zero: "Ваша корзина пуста",
    one: "{count} товар в вашей корзине",
    few: "{count} товара в вашей корзине",
    many: "{count} товаров в вашей корзине",
    other: "{count} товара в вашей корзине",
  },
} as const;

const t = getTranslations("ru-RU");

t("cart", { count: 0 }); // "Ваша корзина пуста"
t("cart", { count: 1 }); // "1 товар в вашей корзине"
t("cart", { count: 3 }); // "3 товара в вашей корзине"
t("cart", { count: 8 }); // "8 товаров в вашей корзине"

Примечание: В спецификации CLDR для русского языка определены категории one, few, many и other (а ts-intl дополнительно предоставляет встроенную поддержку категории zero при count === 0). Если вам требуется особый текст для конкретных чисел, рекомендуется использовать синтаксис инлайн-выражений ICU plural, приведенный ниже (например, =0).

Инлайн-синтаксис ICU plural

const messages = {
  inbox:
    "У вас {count, plural, =0 {нет непрочитанных сообщений} one {# непрочитанное сообщение} few {# непрочитанных сообщения} many {# непрочитанных сообщений} other {# непрочитанных сообщений}}.",
} as const;

const t = getTranslations("ru-RU");

t("inbox", { count: 0 }); // "У вас нет непрочитанных сообщений."
t("inbox", { count: 1 }); // "У вас 1 непрочитанное сообщение."
t("inbox", { count: 4 }); // "У вас 4 непрочитанных сообщения."
t("inbox", { count: 12 }); // "У вас 12 непрочитанных сообщений."

Форматирование порядковых числительных (selectordinal)

Используется для обработки порядковых числительных (точное совпадение = имеет приоритет над категориальными правилами):

const messages = {
  podium:
    "Вы заняли {rank, selectordinal, =1 {1-е} =2 {2-е} =3 {3-е} other {#-е}} место!",
} as const;

const t = getTranslations("ru-RU");

t("podium", { rank: 1 }); // "Вы заняли 1-е место!"
t("podium", { rank: 4 }); // "Вы заняли 4-е место!"

Условный выбор веток (select)

Выбирает соответствующий текст на основе перечислений (enum) статуса или категориальных значений:

const messages = {
  roleBadge:
    "{role, select, admin {Администратор} moderator {Модератор} other {Участник}}",
} as const;

const t = getTranslations("ru-RU");

t("roleBadge", { role: "admin" }); // "Администратор"
t("roleBadge", { role: "guest" }); // "Участник"

Универсальная интерполяция форматированного текста t.rich()

Сопоставляет фрагменты текста с компонентами фронтенда или DOM-узлами с помощью XML-подобных тегов, исключая риски XSS при использовании dangerouslySetInnerHTML:

const messages = {
  notice:
    "Пожалуйста, ознакомьтесь с <terms>Условиями использования</terms> и примите <privacy>Политику конфиденциальности</privacy> перед отправкой.",
} as const;

const t = getTranslations("ru-RU");

// Прямой маппинг в компоненты в 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-разметки в виде строки t.markup()

Используется для генерации строк, содержащих безопасные HTML-теги:

const html = t.markup("notice", {
  terms: (c) => `<a href="/terms">${c}</a>`,
  privacy: (c) => `<a href="/privacy">${c}</a>`,
});