Спросите свой вопрос и получите сводку документа, используя эту страницу и выбранного вами поставщика AI
Содержимое этой страницы было переведено с помощью ИИ.
Смотреть последнюю версию оригинального контента на английскомЕсли у вас есть идея по улучшению этой документации, не стесняйтесь внести свой вклад, подав запрос на вытягивание на GitHub.
Ссылка на документацию GitHubКопировать Markdown документа в буфер обмена
Формат сообщений ICU: синтаксис и типичные ошибки
ICU MessageFormat представляет собой синтаксис строк, позволяющий переводу содержать собственную логику ветвления: множественные числа, грамматический род, форматирование чисел и дат. Этот подход основан на том, что грамматика находится в компетенции переводчика, а не разработчика, пишущего if (count === 1). В этой статье рассматриваются синтаксис, языковые нюансы, на которых спотыкаются простые реализации, и работа экосистемы JavaScript с этим стандартом.
Содержание
Проблема на конкретном примере
Вот код, который большинство разработчиков пишет в первую очередь:
Копировать код в буфер обмена
Это работает в английском языке, но дает сбой практически везде:
- В русском и польском требуется три или четыре формы, а не две.
- В японском нужна только одна форма, а конкатенированный пробел оказывается лишним.
- В арабском используется шесть форм, а само число должно отображаться в локальной числовой системе.
- Во французском перед некоторыми знаками препинания ставится неразрывный пробел, который разрушается выражением
+ " ".
Фундаментальная проблема заключается в том, что предложение разбивается на фрагменты. Переводчик видит изолированные слова item и items без контекста и не имеет возможности изменить порядок слов в предложении. ICU MessageFormat решает эту задачу, сохраняя все предложение в виде единой строки и предоставляя переводчику операторы ветвления.
Простые аргументы
Базовой единицей является заполнитель в одинарных фигурных скобках:
Копировать код в буфер обмена
При передаче { name: "Alice" } в процессе форматирования получается Hello, Alice!. Фигурные скобки являются единственными специальными символами. Чтобы вывести скобку буквально, ее нужно обернуть в одинарные кавычки: '{'.
Это вся функциональность интерполяции. Все остальные возможности ICU построены поверх нее.
Множественные формы (plural)
Оператор plural выбирает ветку на основе числового значения:
Копировать код в буфер обмена
Три важных правила:
- Символ
#заменяется отформатированным значениемcountс учетом локали: так1234преобразуется в1,234дляen-USи в1 234дляru-RU. - Ветка
otherобязательна. Любая реализация ICU выдаст ошибку или не пройдет валидацию при ее отсутствии. Она служит запасным вариантом, если ни одна категория не подошла. - Формы
=0,=1, … соответствуют точным значениям и проверяются до категорий CLDR. Используйте их для особых случаев ("Нет сообщений"), а не как замену категорииone.
Копировать код в буфер обмена
offset
Параметр offset:n вычитает число n из значения перед выбором категории и подстановкой #. Он применяется для шаблонов вида "Алисе и еще 3 пользователям понравилось это":
Копировать код в буфер обмена
При count: 4 символ # отобразит 3. Параметр offset очень полезен, однако его поддержка различается в зависимости от среды выполнения, поэтому проверьте совместимость перед использованием.
Категории множественного числа зависят от языка
Здесь чаще всего возникают ошибки. Названия категорий zero, one, two, few, many, other не являются универсальным набором, общим для всех языков. Каждая локаль использует собственное подмножество, определенное правилами множественного числа CLDR, и эти правила грамматические, а не математические.
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Язык | Тег | Используемые категории | Всего |
|---|---|---|---|
| Японский | ja | other | 1 |
| Китайский | zh | other | 1 |
| Английский | en | one, other | 2 |
| Немецкий | de | one, other | 2 |
| Французский | fr | one, many, other | 3 |
| Чешский | cs | one, few, many, other | 4 |
| Польский | pl | one, few, many, other | 4 |
| Русский | ru | one, few, many, other | 4 |
| Арабский | ar | zero, one, two, few, many, other | 6 |
| Валлийский | cy | zero, one, two, few, many, other | 6 |
Два следствия, которые часто вызывают вопросы:
- Категория
oneне означает число 1. В русском языкеoneохватывает 1, 21, 31, 101: любое число, оканчивающееся на 1, кроме чисел на 11. Во французском языке число0также относится к категорииone. - Добавление категорий в исходный английский текст ничего не дает. Английскому сообщению нужны только
oneиother. Польскому переводу требуется четыре ветки, и эта структура должна находиться в польской строке. Любой формат, заставляющий все языки использовать одинаковый набор ключей, создаст проблемы.
Поведение среды выполнения можно проверить напрямую:
Копировать код в буфер обмена
Интерфейс Intl.PluralRules содержит данные CLDR во всех современных браузерах и в Node.js. Любая библиотека с поддержкой CLDR под капотом вызывает именно этот нативный API.
select и selectordinal
Оператор select ветвится по произвольной строке: полу, роли, статусу или тарифному плану.
Копировать код в буфер обмена
Ключи сопоставляются буквально, и ветка other здесь также обязательна. select незаменим, когда построение предложения зависит от значения перечисления, поскольку в разных языках такие параметры влияют на структуру по-разному.
Оператор selectordinal имеет такую же структуру, как и plural, но использует правила порядковых числительных, которые отличаются от количественных:
Копировать код в буфер обмена
В английском языке задействовано четыре категории порядковых числительных (1st, 2nd, 3rd, 4th), хотя для количественных числительных их всего две. Именно из-за этого различия операторы разделены.
Аргументы чисел, дат и времени
ICU позволяет форматировать интерполируемые значения:
Копировать код в буфер обмена
Современный формат строится на понятии skeleton, появившемся в ICU 60 и обозначаемом префиксом ::. Скелетоны значительно выразительнее традиционных стилей:
Копировать код в буфер обмена
Поддержка скелетонов в экосистеме неравномерна. FormatJS поддерживает их полностью, в то время как другие среды выполнения распознают только устаревшие стили number, currency или date, long. Проверяйте возможности вашей среды выполнения перед запуском в продакшн.
Вложенность и читаемость
Конструкции ICU можно комбинировать. Ветка plural может содержать select, который в свою очередь может включать еще один plural:
Копировать код в буфер обмена
Это канонический пример ICU и одновременно главный аргумент против глубокой вложенности. Уже на втором уровне переводчики начинают делать ошибки в фигурных скобках, а редакторы TMS перестают быть полезными. Не используйте больше двух уровней вложенности. Если требуется третий уровень, разделите предложение на два отдельных сообщения.
Поддержка ICU в библиотеках JS
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Библиотека | Поддержка ICU | Что вы пишете на практике |
|---|---|---|
| react-intl (FormatJS) | Нативная, полная | Строки ICU, включая скелетоны и теги форматирования |
| next-intl | Нативная | Строки ICU через пакет intl-messageformat от FormatJS |
| i18next | Требуется плагин | Суффиксы ключей key_one / key_other и {{name}}; ICU через i18next-icu |
| vue-i18n | Частичная / своя | Интерполяция {name} и ветки плюрализации через пайп |
Angular ($localize) | Подмножество | ICU plural / select внутри шаблонов, экспортируемые в XLIFF |
Пояснения к таблице:
- Синтаксис по умолчанию в i18next не является ICU, и в этом есть свои плюсы. Суффиксы (
item_one,item_few) соответствуют категориямIntl.PluralRulesи часто удобнее для редактирования в обычном JSON. Однакоselectи вложенные ветвления отсутствуют, поэтому приходится подключатьi18next-icuили переносить логику в код. - Ветвления через пайп в vue-i18n по умолчанию опираются на функцию правила для каждой локали, а не на категории CLDR. Это работает, но логика плюрализации хранится в конфигурации приложения, а не в самих данных.
- FormatJS выступает эталоном в мире JS. Когда говорят об "ICU MessageFormat" в контексте JavaScript, чаще всего подразумевают именно реализацию FormatJS.
- Полная поддержка ICU увеличивает размер бандла. Парсер и обработка скелетонов добавляют около 10 КБ сжатого JavaScript. См. почему ICU не подходит для JavaScript.
Подход Intlayer
Intlayer не использует строковый DSL. Операторы ветвления представляют собой типизированные функции внутри файлов объявления контента, поэтому каждая локаль объявляет только те категории, которые требуются ее грамматике:
Копировать код в буфер обмена
Копировать код в буфер обмена
Соответствие концепциям ICU является прямым:
Открыть таблицу в модальном окне для четкого просмотра всех данных
| Конструкция ICU | Intlayer |
|---|---|
{name} | insert("Hello {{name}}") или автоопределение |
{count, plural, …} | plural({ one, few, many, other }) |
{value, select, …} | select({ draft, published, fallback }) |
ветка рода в select | gender({ male, female, fallback }) |
логическая ветка в select | cond({ true, false }) |
| числовые диапазоны (не CLDR) | enu({ "0": …, ">5": …, fallback: … }) |
{n, number, ::currency/EUR} | useCurrency()(1234.5, { currency: "EUR" }) |
Оператор plural делегирует выбор категории функции Intl.PluralRules, поэтому приведенная выше таблица CLDR работает без изменений. Форматирование остается обособленным: числа, даты, валюты и списки обрабатываются через хуки форматирования, а не встраиваются в текст сообщения.
Особенности и ограничения:
- Intlayer требует этапа сборки: компилятор извлекает объявления во время билда. Если вам требуется простой JSON, загружаемый динамически в рантайме, это другая модель.
- Внутри веток
pluralпока нельзя напрямую вкладыватьt(): нужно оборачиватьpluralвt(), а не наоборот. - Экосистема моложе, чем у i18next: доступно меньше готовых интеграций с TMS и обсуждений на профильных ресурсах.
При переходе с кодовой базы, уже содержащей строки ICU, адаптер совместимости react-intl разбирает их напрямую: plural, select, selectordinal, # и классические аргументы number, date, time. Скелетоны и опция offset: пока не поддерживаются этим резолвером, поэтому проверьте такие сообщения при миграции. Адаптер i18next сопоставляет суффиксы (key_one, key_male) через Intl.PluralRules.
Распространенные ошибки
- Жесткое кодирование плюрализации в коде. Конструкция
count === 1 ? a : bдает некорректный результат для 8 из 10 языков в таблице выше. Если тернарный оператор зашит в код, переводчик не сможет ничего исправить. - Склеивание переведенных фрагментов. Порядок слов, грамматические согласования и пробелы перед знаками препинания зависят от языка. Храните предложение целиком.
- Пропуск ветки
other. Это строгое требование спецификации, а не рекомендация. Большинство парсеров выбросят ошибку, а остальные ничего не отобразят. - Предположение об одинаковых категориях. Если в английском источнике указаны
oneиother, это не означает, что в польском или русском файле будет две ветки. Каждая локаль должна объявлять собственные категории. См. объявление контента по локалям. - Использование
=1вместоone. Форма=1совпадает только с точным числом 1. В русском языке для 21 требуется категорияone, и правило=1никогда не сработает для таких чисел. - Размещение символа
#вне ветки plural. Он имеет специальное значение только внутри блоковpluralилиselectordinal. В остальных местах это обычный символ решетки. - Забывание о том, что
#уже отформатирован. Если требуется сырое число без региональных разделителей разрядов, интерполируйте аргумент по имени.
Дополнительные материалы
Комментарии
Пока нет комментариев. Будьте первым, кто поделится своими мыслями.
