Задайте питання та отримайте підсумок документа, вказавши цю сторінку та обраного вами постачальника штучного інтелекту
Вміст цієї сторінки перекладено за допомогою штучного інтелекту.
Переглянути останню версію оригінального вмісту англійськоюЯкщо у вас є ідея щодо покращення цієї документації, будь ласка, долучіться, надіславши pull request на GitHub.
Посилання на документацію на GitHubСкопіювати документацію у форматі Markdown в буфер обміну
Формат повідомлень ICU: синтаксис і типові підводні камені
ICU MessageFormat є синтаксисом рядків, що дозволяє перекладу містити власну логіку розгалуження: форми множини, узгодження за родом, форматування чисел і дат. Він базується на тому, що граматика належить перекладачеві, а не розробнику, який пише if (count === 1). У цій статті розглядаються синтаксис, мовні особливості, які ламають наївні реалізації, та способи роботи екосистеми JavaScript із цим стандартом.
Зміст
Проблема на практиці
Ось код, який майже кожен розробник пише на самому початку:
Скопіюйте код у буфер обміну
Це працює в англійській мові, але призводить до помилок практично в усіх інших:
- Українська та польська потребують трьох або чотирьох форм, а не двох.
- Японська потребує лише однієї форми, а доданий пробіл виявляється недоречним.
- Арабська вимагає шість форм, а саме число має відображатися за місцевою системою числення.
- Французька вимагає нерозривний пробіл перед деякими розділовими знаками, що руйнується конкатенацією
+ " ".
Глибша проблема полягає в тому, що речення було розбито на ізольовані частини. Перекладач бачить окремі слова item та items без контексту і позбавлений можливості змінити порядок слів у реченні. ICU MessageFormat вирішує це, зберігаючи все речення в одному рядку для перекладу та надаючи перекладачеві оператори розгалуження.
Прості аргументи
Найменшою одиницею є заповнювач (placeholder) у фігурних дужках:
Скопіюйте код у буфер обміну
Під час форматування передається { name: "Alice" }, що дає Hello, Alice!. Фігурні дужки є єдиними спеціальними символами. Щоб вивести звичайну фігурну дужку, її слід взяти в одинарні лапки: '{'.
Це вся функціональність інтерполяції. Усі інші можливості ICU побудовані поверх неї.
Множина (plural)
Оператор plural обирає гілку на основі числового значення:
Скопіюйте код у буфер обміну
Три правила, які необхідно знати:
#замінюється відформатованим значеннямcountз урахуванням локалі. Наприклад,1234перетворюється на1,234вen-USта на1 234вuk-UA.- Гілка
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 |
| Українська | uk | 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 є ідеальним інструментом, коли структура речення залежить від значення enum, оскільки в різних мовах граматичні форми залежать від різних факторів.
Оператор selectordinal має таку саму структуру, як і plural, але використовує правила для порядкових числівників (1-й, 2-й тощо), що відрізняються від кількісних:
Скопіюйте код у буфер обміну
Англійська мова використовує чотири категорії для порядкових числівників (1st, 2nd, 3rd, 4th), хоча для кількісних застосовує лише дві. Ця асиметрія є причиною, чому ці два оператори існують окремо.
Аргументи чисел, дат і часу
ICU дозволяє форматувати інтерпольовані значення прямо в тексті:
Скопіюйте код у буфер обміну
Сучасним форматом є скелетон (skeleton), представлений в ICU 60 із префіксом ::. Скелетони надають значно більшу гнучкість, ніж застарілі назви стилів:
Скопіюйте код у буфер обміну
Підтримка скелетонів в екосистемі різна. FormatJS підтримує їх у повному обсязі, тоді як деякі інші середовища приймають лише традиційні формати на кшталт number, currency або date, long. Обов'язково перевірте роботу :: у вашому середовищі перед релізом.
Вкладеність і читабельність
Синтаксис ICU є модульним і підтримує комбінування. Гілка plural може містити select, який своєю чергою може містити інший plural:
Скопіюйте код у буфер обміну
Це класичний приклад ICU і водночас головний аргумент проти надмірної вкладеності. Починаючи з двох рівнів, перекладачі легко припускаються помилок із фігурними дужками, а редактори TMS втрачають ефективність. Не використовуйте більше двох рівнів вкладеності. Якщо потрібен третій рівень, розділіть речення на два окремих повідомлення.
Підтримка ICU в бібліотеках JavaScript
Відкрийте таблицю в модальному вікні, щоб чітко переглянути всі дані
| Бібліотека | Підтримка 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. Коли в контексті JavaScript згадують "ICU MessageFormat", зазвичай мають на увазі стандарт, що підтримується 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.
Поширені помилки
- Жорстке кодування правил множини в JS. Конструкція
count === 1 ? a : bдає хибний результат для 8 із 10 мов у наведеній вище таблиці. Щойно тернарний оператор з'являється в коді, перекладач не може на нього вплинути. - Об'єднання перекладених фрагментів. Порядок слів, граматичні узгодження та пробіли перед знаками пунктуації залежать від локалі. Завжди зберігайте речення як єдине ціле.
- Пропуск гілки
other. Це сувора вимога специфікації, а не рекомендація. Більшість парсерів видадуть помилку, а решта нічого не покаже. - Припущення, що всі мови мають однакові категорії. Якщо в англійському джерелі є лише
oneтаother, це не означає, що польський чи український переклад матиме лише дві гілки. Кожна мова повинна мати можливість оголошувати власні гілки. Дивіться оголошення контенту за локалями. - Використання
=1замістьone. Форма=1відповідає виключно точному числу 1. В українській мові число 21 потребує категоріїone, і правило=1ніколи для нього не спрацює. - Розміщення символу
#поза межами гілки plural. Він має спеціальне значення лише всередині блоківpluralабоselectordinal. В інших місцях він сприймається як звичайний символ решітки. - Забування про те, що
#уже відформатовано. Якщо потрібне число без регіональних розділювачів розрядів, інтерполюйте аргумент за його назвою.
Корисні матеріали
Коментарі
Поки що немає коментарів. Будьте першим, хто поділиться своїми думками.
