Zadaj pytanie i otrzymaj streszczenie dokumentu, odwołując się do tej strony i wybranego dostawcy AI
Treść tej strony została przetłumaczona przy użyciu sztucznej inteligencji.
Zobacz ostatnią wersję oryginalnej treści w języku angielskimJeśli masz pomysł na ulepszenie tej dokumentacji, zachęcamy do przesłania pull requesta na GitHubie.
Link do dokumentacji na GitHubieKopiuj dokument Markdown do schowka
Format komunikatów ICU: składnia i typowe pułapki
ICU MessageFormat to składnia ciągów znaków, która pozwala, aby tłumaczenie zawierało własną logikę warunkową: liczbę mnogą, formy zależne od płci oraz formatowanie liczb i dat. Opiera się na założeniu, że gramatyka należy do tłumacza, a nie do programisty piszącego if (count === 1). W tym artykule omawiamy składnię, niuanse językowe sprawiające trudności naiwnym implementacjom oraz sposób, w jaki radzi sobie z tym ekosystem JavaScript.
Spis treści
Problem w praktyce
Oto kod, który większość programistów pisze na samym początku:
Skopiuj kod do schowka
Działa to w języku angielskim, ale zawodzi w niemal każdym innym języku:
- Polski i rosyjski wymagają trzech lub czterech form, a nie dwóch.
- Japoński wymaga tylko jednej, a doklejona spacja jest błędem.
- Arabski wymaga sześciu form, a sama liczba powinna być renderowana w lokalnym systemie liczbowym.
- Francuski wstawia spację niełamliwą przed niektórymi znakami interpunkcyjnymi, co powyższe
+ " "niszczy.
Głębszy problem polega na tym, że zdanie zostało pocięte na fragmenty. Tłumacz widzi odizolowane słowa item i items bez kontekstu i bez możliwości zmiany szyku zdania. ICU MessageFormat rozwiązuje ten problem, zachowując całe zdanie w jednym tłumaczalnym ciągu znaków i udostępniając tłumaczowi operatory warunkowe.
Proste argumenty
Podstawową jednostką jest symbol zastępczy w pojedynczych nawiasach klamrowych:
Skopiuj kod do schowka
Przekazując { name: "Alice" } podczas formatowania, otrzymujesz Hello, Alice!. Nawiasy klamrowe są jedynymi znakami specjalnymi. Aby wyświetlić dosłowny nawias klamrowy, należy otoczyć go pojedynczymi cudzysłowami: '{'.
To cała funkcjonalność interpolacji. Wszystko inne w ICU opiera się na tym mechanizmie.
Liczba mnoga (plural)
Operator plural wybiera gałąź na podstawie wartości liczbowej:
Skopiuj kod do schowka
Trzy kluczowe zasady:
- Znak
#jest zastępowany sformatowaną wartością zmiennejcount, dostosowaną do reguł danej przestrzeni językowej. Zatem1234zamienia się w1,234wen-USoraz w1 234wpl-PL. - Gałąź
otherjest obowiązkowa. Każda implementacja ICU zgłosi błąd lub nie przejdzie walidacji bez niej. Stanowi ona zabezpieczenie, gdy żadna kategoria nie pasuje. - Reguły
=0,=1, … dopasowują dokładne wartości i są sprawdzane przed kategoriami CLDR. Używaj ich dla specyficznych komunikatów ("Brak wiadomości"), a nie jako zamiennika dlaone.
Skopiuj kod do schowka
offset
Parametr offset:n odejmuje wartość n od liczby przed wyborem kategorii i podstawieniem #. Przydaje się we wzorcach typu "Alice i 3 inne osoby polubiły to":
Skopiuj kod do schowka
Dla wartości count: 4 znak # wyrenderuje 3. Opcja offset jest bardzo przydatna, ale poziom jej wsparcia w środowiskach uruchomieniowych bywa zróżnicowany, dlatego warto sprawdzić zgodność przed wdrożeniem.
Kategorie liczby mnogiej zależą od języka
W tym miejscu najczęściej popełniane są błędy. Nazwy kategorii zero, one, two, few, many, other nie są uniwersalnymi pojemnikami identycznymi dla wszystkich języków. Każdy język korzysta z podzbioru zdefiniowanego przez reguły liczby mnogiej CLDR, a reguły te wynikają z gramatyki, a nie prostej intuicji matematycznej.
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Język | Kod | Wykorzystywane kategorie | Liczba |
|---|---|---|---|
| Japoński | ja | other | 1 |
| Chiński | zh | other | 1 |
| Angielski | en | one, other | 2 |
| Niemiecki | de | one, other | 2 |
| Francuski | fr | one, many, other | 3 |
| Czeski | cs | one, few, many, other | 4 |
| Polski | pl | one, few, many, other | 4 |
| Rosyjski | ru | one, few, many, other | 4 |
| Arabski | ar | zero, one, two, few, many, other | 6 |
| Walijski | cy | zero, one, two, few, many, other | 6 |
Dwa wnioski, które często zaskakują:
- Kategoria
onenie oznacza wyłącznie liczby 1. W języku rosyjskimoneobejmuje liczby 1, 21, 31, 101, czyli każdą liczbę kończącą się cyfrą 1 z wyjątkiem kończących się na 11. We francuskim wartość0również wpada do kategoriione. - Dodanie kategorii do angielskiego tekstu źródłowego nic nie zmienia. Angielski komunikat wymaga tylko gałęzi
oneiother. Polskie tłumaczenie wymaga czterech gałęzi, a struktura ta musi znajdować się w polskim tekście. Każdy format zmuszający wszystkie języki do posiadania identycznego układu kluczy powoduje tu komplikacje.
Działanie środowiska można sprawdzić bezpośrednio:
Skopiuj kod do schowka
Interfejs Intl.PluralRules udostępnia dane CLDR we wszystkich nowoczesnych przeglądarkach oraz w Node.js. Biblioteki deklarujące zgodność z CLDR zazwyczaj wywołują to natywne API.
select i selectordinal
Operator select tworzy rozgałęzienia na podstawie dowolnego ciągu znaków: płci, roli użytkownika, statusu czy planu subskrypcji.
Skopiuj kod do schowka
Klucze są porównywane dosłownie, a gałąź other jest również tutaj wymagana. select jest właściwym narzędziem, gdy struktura zdania zależy od wartości wyliczeniowej (enum), ponieważ języki różnią się pod względem elementów wpływających na gramatykę.
Operator selectordinal ma taką samą strukturę jak plural, ale stosuje reguły dla liczb porządkowych, które znajdują się w innej tabeli niż liczebniki główne:
Skopiuj kod do schowka
Język angielski stosuje cztery kategorie porządkowe (1st, 2nd, 3rd, 4th), chociaż dla liczb głównych używa tylko dwóch. Ta asymetria jest powodem, dla którego oba operatory są rozdzielone.
Argumenty liczb, dat i czasu
ICU umożliwia formatowanie interpolowanych wartości bezpośrednio w tekście:
Skopiuj kod do schowka
Nowoczesną formą zapisu jest skeleton, wprowadzony w ICU 60 i oznaczany prefiksem ::. Szkielety dają znacznie większe możliwości niż tradycyjne style:
Skopiuj kod do schowka
Obsługa szkieletów w ekosystemie bywa niejednolita. FormatJS obsługuje je w pełni, podczas gdy niektóre inne środowiska akceptują jedynie tradycyjne zapisy number, currency lub date, long. Przed wdrożeniem na produkcję sprawdź obsługę :: w swoim środowisku.
Zagnieżdżanie a czytelność
Składnia ICU jest modularna. Gałąź plural może zawierać select, który z kolei może zawierać kolejny plural:
Skopiuj kod do schowka
Jest to klasyczny przykład ICU i jednocześnie główny argument przeciwko głębokiemu zagnieżdżaniu. Przy dwóch poziomach tłumacze zaczynają gubić się w nawiasach klamrowych, a edytory TMS przestają ułatwiać pracę. Nie zagnieżdżaj więcej niż dwóch poziomów. Jeśli potrzebujesz trzeciego, podziel zdanie na dwa osobne komunikaty.
Obsługa ICU w bibliotekach JavaScript
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Biblioteka | Wsparcie ICU | Co w praktyce zapisujesz |
|---|---|---|
| react-intl (FormatJS) | Natywne, pełne | Ciągi ICU, w tym szkielety i znaczniki formatowania tekstu |
| next-intl | Natywne | Ciągi ICU poprzez bibliotekę intl-messageformat od FormatJS |
| i18next | Wymaga wtyczki | Sufiksy key_one / key_other oraz {{name}}; ICU przez i18next-icu |
| vue-i18n | Częściowe / własne | Interpolacja {name} i gałęzie plural rozdzielane kreską |
Angular ($localize) | Podzbiór | ICU plural / select w szablonach, wyodrębniane do plików XLIFF |
Kluczowe uwagi do powyższego zestawienia:
- Domyślna składnia i18next to nie ICU, co nie musi być wadą. Klucze z sufiksami (
item_one,item_few) mapują się na kategorieIntl.PluralRulesi często są łatwiejsze do edycji w płaskim pliku JSON. Brakuje w nich jednak operatoraselecti zagnieżdżonych gałęzi, co zmusza do użyciai18next-iculub pisania logiki w kodzie. - Zapis z kreską w vue-i18n domyślnie korzysta z funkcji reguły per-locale, a nie kategorii CLDR. Działa to poprawnie, lecz reguła znajduje się w konfiguracji aplikacji, a nie w samych danych.
- FormatJS stanowi punkt odniesienia w świecie JS. Mówiąc o "ICU MessageFormat" w kontekście JavaScriptu, najczęściej ma się na myśli specyfikację akceptowaną przez FormatJS.
- Pełne wsparcie dla ICU wiąże się z kosztem bundle. Parser i obsługa skeletons dodają około 10 KB skompresowanego JavaScriptu. Zobacz dlaczego ICU nie jest stworzony dla JavaScript.
Rozwiązanie w Intlayer
Intlayer nie stosuje tekstowego DSL. Operatory warunkowe są funkcjami deklarowanymi w plikach zawartości, dzięki czemu struktura jest typowana, a każdy język definiuje wyłącznie te kategorie, których wymaga jego gramatyka:
Skopiuj kod do schowka
Skopiuj kod do schowka
Mapowanie na pojęcia ICU jest bezpośrednie:
Otwórz tabelę w oknie modalnym, aby wyraźnie zobaczyć całą zawartość
| Konstrukcja ICU | Intlayer |
|---|---|
{name} | insert("Hello {{name}}") lub autowykrywanie |
{count, plural, …} | plural({ one, few, many, other }) |
{value, select, …} | select({ draft, published, fallback }) |
gałąź płci w select | gender({ male, female, fallback }) |
gałąź logiczna w select | cond({ true, false }) |
| przedziały liczbowe (nie-CLDR) | enu({ "0": …, ">5": …, fallback: … }) |
{n, number, ::currency/EUR} | useCurrency()(1234.5, { currency: "EUR" }) |
Operator plural deleguje wybór kategorii do Intl.PluralRules, dzięki czemu powyższa tabela CLDR działa bezpośrednio. Formatowanie pozostaje odseparowane: liczby, daty, waluty i listy są obsługiwane przez dedykowane hooki formatujące, zamiast być wbudowane w treść komunikatu.
Ograniczenia:
- Intlayer wymaga etapu budowania: kompilator wyodrębnia deklaracje w trakcie budowy aplikacji. Jeśli preferujesz czysty JSON ładowany dynamicznie w czasie wykonywania, jest to odmienny model.
- Wewnątrz gałęzi
pluralnie można jeszcze zagnieżdżać funkcjit(): topluralumieszcza się wewnątrzt(), a nie odwrotnie. - Ekosystem jest młodszy niż w przypadku i18next, z mniejszą liczbą gotowych integracji TMS.
Dla projektów zawierających już ciągi ICU, adapter zgodności react-intl przetwarza je bezpośrednio: plural, select, selectordinal, # oraz tradycyjne argumenty number, date, time. Szkielety oraz opcja offset: nie są obsługiwane przez ten mechanizm i wymagają weryfikacji podczas migracji. Adapter i18next mapuje natomiast formy z sufiksami (key_one, key_male) na wywołania Intl.PluralRules.
Częste błędy
- Zaszywanie logiki pluralizacji w kodzie JS. Wyrażenie
count === 1 ? a : bzwraca niepoprawny wynik dla 8 z 10 języków w powyższej tabeli. Gdy operator trójargumentowy znajdzie się w kodzie, żaden tłumacz nie jest w stanie go poprawić. - Łączenie przetłumaczonych fragmentów. Szyk wyrazów, odmiana i spacje przed znakami interpunkcyjnymi zależą od języka. Zdanie powinno zawsze stanowić nierozerwalną całość.
- Pomijanie gałęzi
other. Jest to wymóg specyfikacji, a nie opcjonalna konwencja. Większość parserów odrzuci taki komunikat, a pozostałe nie wyświetlą nic. - Zakładanie, że kategorie są uniwersalne. Źródłowy plik angielski z gałęziami
oneiothernie oznacza, że plik polski ma tylko dwa warianty. Każdy język musi definiować własne gałęzie. Zobacz deklarowanie zawartości dla poszczególnych języków. - Stosowanie
=1zamiastone. Zapis=1pasuje wyłącznie do liczby 1. W języku rosyjskim liczba 21 wymaga kategoriione, dla której reguła=1nigdy nie zostanie aktywowana. - Wstawianie znaku
#poza gałęzią liczby mnogiej. Ma on specjalne znaczenie wyłącznie wewnątrz blokówplurallubselectordinal. W innych miejscach jest traktowany jako zwykły znak kratki. - Zapominanie, że
#jest już sformatowany. Jeśli potrzebujesz surowej liczby bez regionalnych separatorów, podstaw argument według nazwy zmiennej.
Więcej informacji
Komentarze
Nie ma jeszcze komentarzy. Bądź pierwszą osobą, która podzieli się swoimi przemyśleniami.
