Спросите свой вопрос и получите сводку документа, используя эту страницу и выбранного вами поставщика AI
Содержимое этой страницы было переведено с помощью ИИ.
Смотреть последнюю версию оригинального контента на английскомЕсли у вас есть идея по улучшению этой документации, не стесняйтесь внести свой вклад, подав запрос на вытягивание на GitHub.
Ссылка на документацию GitHubКопировать Markdown документа в буфер обмена
Почему ICU MessageFormat не подходит для JavaScript
ICU MessageFormat представляет собой проверенный временем стандарт. Он функционален, переводчики хорошо его знают, а большинство систем управления переводами (TMS) умеют с ним работать. Проблема кроется в среде выполнения, для которой он создавался. ICU родом из C++ и Java, где полноценный парсер и форматтер сообщений создают пренебрежимо малую нагрузку на фоне всей программы. В браузерном же бандле за этот парсер приходится платить при каждой загрузке страницы.
В этой статье рассматривается история возникновения ICU, причины избыточности его синтаксиса для форм множественного числа, а также то, почему стопроцентная совместимость утяжеляет любую JavaScript-библиотеку интернационализации. Если вам требуется непосредственно описание синтаксиса, обратитесь к руководству по ICU Message Format.
От IBM к консорциуму Unicode
Аббревиатура ICU расшифровывается как International Components for Unicode. Синтаксис форматирования сообщений зародился в Java: компания Taligent, совместное предприятие Apple и IBM, разработала базовые классы интернационализации для JDK 1.1 (1997 год), включая java.text.MessageFormat. IBM продолжила их развитие под именем ICU4J, портировала на C/C++ как ICU4C и открыла исходный код в 1999 году. В 2016 году проект перешел под управление консорциума Unicode, который также развивает CLDR, основу данных локализации для ICU.
Для чего он предназначался
Главной целью было серверное и настольное программное обеспечение: корпоративные приложения на Java, продукты IBM, а позднее и операционные системы. Сообщения размещались в .properties-файлах Java и загружались через ResourceBundle, либо хранились в собственном формате ресурсов ICU для C/C++:
Копировать код в буфер обмена
Копировать код в буфер обмена
В начальной версии JDK ключевое слово plural отсутствовало. Для ветвления применялся оператор choice с числовыми интервалами ({0,choice,0#no files|1#one file|1<{0} files}), что подходило только для языков со структурой плюрализации английского типа. ICU внедрил конструкцию plural на основе правил CLDR в 2008 году (ICU 4.0), а директиву select добавил в 2010 году (ICU 4.4).
Отличие от формата .po
ICU нередко путают с gettext, хотя это две абсолютно разные школы. Файлы .po происходят из GNU gettext (C, Linux, затем PHP и Python). В .po содержатся простые пары строк msgid / msgstr, а выбор формы множественного числа рассчитывается выражением на C в заголовке файла (Plural-Forms: nplurals=2; plural=(n > 1);). Внутри самого текста сообщения нет условий ветвления. ICU же помещает всю логику ветвлений непосредственно внутрь строки, поэтому одно сообщение может одновременно сочетать plural, select и форматирование чисел.
Где ICU работает в настоящее время
Библиотека ICU4C встроена в Android, iOS, macOS, Windows, Node.js и движки JavaScript браузеров Chrome и Firefox. Стандартные API Intl в браузере в значительной степени построены именно на ней. Соответственно, браузер уже содержит правила плюрализации, форматирования чисел и дат из ICU. Чего в нем нет, так это встроенного парсера сообщений: спецификация Intl.MessageFormat все еще находится на ранней стадии предложений TC39, базируется на новом синтаксисе MessageFormat 2 и не совместима с ICU MessageFormat 1.
Эта ретроспектива объясняет архитектуру:
- Ориентация на серверные и десктопные среды. Парсинг строки во время выполнения там дешев, а сама библиотека устанавливается один раз на уровне ОС, не загружаясь по сети каждым посетителем.
- Специализированный язык (DSL) внутри строки. Ветвления, вывод чисел, даты и вложенность описаны единым синтаксисом, доступным переводчикам без погружения в программный код.
- Стремление к абсолютной полноте. Под любые грамматические задачи переводчика предусмотрены отдельные операторы.
Все эти решения вполне обоснованы. Они просто исходят из условий окружения, существенно отличающихся от реалий браузера.
Громоздкость конструкций множественного числа
Самая частотная конструкция ICU оказывается наиболее перегруженной. Описание счетчика с нулевым значением выглядит следующим образом:
Копировать код в буфер обмена
Здесь требуется указать имя аргумента, ключевое слово plural, метку для каждой ветки, вложенные фигурные скобки и знак #, действующий как специальный токен только внутри секций множественного числа. Если добавить учет грамматического рода, глубина вложенности возрастает:
Копировать код в буфер обмена
Девять строк из пятнадцати выполняют исключительно роль синтаксического каркаса. В польском языке для каждого из этих трех родов требуется по четыре формы множественного числа. В итоге переведенная строка превращается в лабиринт из фигурных скобок, где одна потерянная закрывающая скобка ломает все сообщение, причем часто это обнаруживается лишь в рантайме.
В JavaScript ту же самую структуру можно представить обычными данными: объектом, ключи которого соответствуют категориям множественного числа, проверяемым компилятором TypeScript и редактором кода без промежуточных парсеров.
Полнота возможностей формирует накладные расходы
Спецификация ICU включает обширный перечень механизмов:
pluralс точными числовыми совпадениями (=0) и смещениями (offset:)selectordinalс собственной таблицей порядковых числительных CLDRselectс неограниченной глубиной вложенности- Аргументы
number,dateиtimeкак в классическом стиле (number, currency), так и в виде скелетонов (::currency/EUR compact-short) - Правила экранирования и кавычек (
'{','') - Теги форматирования rich-text в ряде реализаций (
<b>…</b>)
Библиотека, заявляющая полную совместимость с ICU формата 1:1, обязана включать все перечисленные модули, поскольку на этапе сборки невозможно предугадать, какие конструкции задействованы в ваших текстах. На практике это влечет за собой:
- Парсер, трансформирующий строку в AST с обработкой синтаксических ошибок в скобках.
- Парсер скелетонов для синтаксиса
::применительно к числам и датам, представляющий собой отдельный компактный язык. - Форматтер, обходящий AST и связывающий узлы с
Intl.PluralRules,Intl.NumberFormatиIntl.DateTimeFormat.
Третья составляющая компактна, ведь современный JavaScript уже имеет реализацию CLDR в Intl. Первые две служат лишь одной цели: разобрать строковый синтаксис. В библиотеке intl-messageformat от FormatJS, выступающей основой для react-intl и next-intl, этот функционал занимает около 10 КБ сжатого JavaScript-кода, загружаемого каждым пользователем еще до получения самих переводов.
Большинство проектов задействует малую долю возможностей: подстановку {name} и пару блоков plural. Однако они вынуждены загружать полный парсер для скелетонов, порядковых числительных и смещений, ведь парсинг строк в рантайме не позволяет бандлеру удалить неиспользуемый код.
next-intl пришел к аналогичным выводам
Это не просто теоретические рассуждения. Библиотека next-intl, входящая в число популярнейших решений на базе ICU, пришла к тем же выводам. В версии 4.8 (январь 2026 года) разработчики внедрили экспериментальную опцию precompile. Она преобразует сообщения ICU на этапе сборки в компактное дерево AST и заменяет рантайм-парсер лаконичным интерпретатором. По данным разработчиков проекта, это позволило сократить размер бандла примерно на 9 КБ сжатого JavaScript.
Этот компромисс демонстрирует ограничения строкового подхода: метод t.raw перестает работать при включенной предкомпиляции, поскольку исходная строка ICU больше не существует во время исполнения. Как только браузер перестает парсить строку, вы по сути больше не распространяете чистый ICU. Вы отдаете скомпилированное представление, а сам строковый синтаксис остается лишь промежуточным форматом написания.
Здесь возникает закономерный вопрос: если браузер никогда не читает исходную строку, зачем разработчикам и переводчикам писать сообщения в таком виде?
Как устроен нативный подход для JavaScript
JavaScript уже решает наиболее сложные задачи на уровне платформы. Intl.PluralRules знает, что в польском языке четыре счетные категории, а в английском четыре порядковые. Intl.NumberFormat и Intl.DateTimeFormat отвечают за валюты, единицы измерения, компактную нотацию и календари. Задача сводится лишь к выбору нужной ветки и подстановке значений, что занимает считанные строки, если структура оформлена как структурированные данные, а не текст.
Именно этот принцип лежит в основе Intlayer. Ветвление реализуется функциями внутри типизированной декларации контента, и каждая локаль описывает только те категории, которые обусловлены ее грамматикой:
Копировать код в буфер обмена
Копировать код в буфер обмена
Ключевые отличия по сравнению с ICU:
- Отсутствие парсера в клиентском бандле. Структура представляет собой готовый объект к моменту попадания в браузер. Функция
pluralвыбирает нужный ключ через стандартныйIntl.PluralRules. - Выявление ошибок на этапе сборки. Пропущенная форма или опечатка в ключе вызовет ошибку типов TypeScript, предотвращая сбои в продакшене.
- Форматирование вынесено за пределы сообщения. Числа, даты и валюты форматируются через хуки форматтеров, работающие с
Intlнапрямую, минуя парсер скелетонов. Неиспользуемый функционал не влияет на размер бандла. Если в проекте не используется
gender, бандлер удалит его через tree-shaking.
Безусловно, у такого подхода есть свои особенности: необходим шаг сборки, файлы контента являются программным кодом, а отдельные инструменты TMS, жестко ориентированные на ICU, не способны читать файлы определений TypeScript напрямую.
Когда ICU остается оптимальным решением
ICU по-прежнему предпочтителен в ситуациях, когда:
- Ваш процесс локализации жестко привязан к нему. Множество инструментов TMS ориентированы на импорт и экспорт строк ICU, а штат переводчиков привык работать именно с этим синтаксисом.
- Сообщения синхронизируются между несколькими платформами. Единый каталог переводов для приложений на iOS, Android и веб-интерфейса служит весомым аргументом в пользу универсального формата.
- В проекте уже накоплена огромная база сообщений в формате ICU. Переписывать тысячи строк с нуля редко бывает экономически оправданно.
В последнем случае вовсе не обязательно выбирать между трудоемким переписыванием и тяжелым парсером. Адаптер совместимости react-intl от Intlayer способен считывать существующие строки ICU (plural, select, selectordinal, #, классические форматы number / date / time). Это дает возможность мигрировать постепенно, сохраняя накладные расходы на ICU только для устаревших сообщений.
Заключение
ICU MessageFormat успешно решил важную задачу: управление грамматикой должно находиться в руках переводчиков, а не реализовываться ветвлениями if (count === 1) в программном коде. Это решение было оптимальным для платформ, где разбор текстового DSL обходится практически бесплатно. В веб-браузере полная поддержка вынуждает доставлять код парсера для возможностей, которые большинству приложений попросту не нужны, и даже авторы библиотек на базе ICU переходят на опережающую компиляцию, чтобы избавиться от этих затрат.
JavaScript уже содержит правила CLDR в интерфейсе Intl. Единственное, что требуется от современного формата интернационализации, это логика ветвления условий, и ее гораздо эффективнее представлять в виде структурированных типизированных данных.
Дополнительные материалы
Комментарии
Пока нет комментариев. Будьте первым, кто поделится своими мыслями.
