Lokalizacja silnika
Tekst silnika przeznaczony dla człowieka jest w 17 językach obok angielskiego: nl, pl, pt-BR, pt-PT, fi, sv, da, nb, cs, uk, ru, de, fr, es, et, ja, zh-Hans. Źródłem jest angielski; en-XA to pseudolokalizacja, która akcentuje, ujmuje w nawiasy i dopełnia każdy komunikat, aby test mógł zobaczyć, czy tekst został przeniesiony do katalogu. Framework to Project Fluent (fluent-bundle, unic-langid); kod to src/i18n/.
Co jest lokalizowane, a co nigdy#
| Lokalizowane (czyta człowiek) | Nigdy nielokalizowane (czyta program) |
|---|---|
| komunikaty i błędy wiersza poleceń | klucze i wartości JSON w result.json i report.json |
| wiersz podsumowania każdego przebiegu | nagłówki i wiersze CSV |
| tekst wykresów SVG (tytuły, osie, legendy, podpisy) | nazwy plików |
report.html: nagłówki, tabele, uwagi, podpisy |
kody błędów (E-CLOCK-VERSION) |
| słowa werdyktów (VALIDATED, MODELLED, PARTIAL) | macierz weryfikacji, definicje jednostek, pochodzenie |
opisy rodzajów scenariuszy (list_kinds(lang)) |
|
oświadczenia doradcze, zastrzeżenia i informacje (engine-legal-*) |
te same teksty wewnątrz JSON, które pozostają po angielsku |
report.html kampanii iq, strony porównania badań, tabeli wyników bench, raportu odporności |
summary.html pakietu dowodowego i PNG obserwatorium: oba są skrótowane do manifestu (pakiet jest dodatkowo podpisany), więc powstają po angielsku niezależnie od języka |
Liczby w tekście przeznaczonym dla człowieka używają znaku dziesiętnego danego języka (przecinek w 15 z 18 języków; kropka w en, ja, zh-Hans i en-XA). Liczby w plikach maszynowych zawsze używają kropki. Licznik (zmienna o nazwie n, count, n_*, num_* lub *_count) trafia do Fluent jako liczba, więc tłumaczenie może zapisać { $n -> [one] ... [few] ... *[other] ... }.
Wybór języka#
- Wiersz poleceń:
--lang <tag>, potemKSHANA_LANG,LC_ALL,LC_MESSAGES,LANG, a następnie angielski.ptto pt-BR; tylkopt-PToznacza Portugalię.zhto uproszczony; tradycyjny jest odrzucany z listą obsługiwanych znaczników. - Biblioteka:
api::run_toml_lang(toml, lang),run_toml_bilingual,run_toml_human,run_scenario_lang,list_scenario_kinds_json_lang(lang). - Python:
lang=Nonewrun,run_full,run_typed,validate_toml,error_kind,list_kinds,scenario_kinds;run_human(toml, lang);error_info(toml, lang). - WebAssembly: opcjonalny końcowy
langwrun_typed,run,chart_svg,summary,table_csv,run_all,error_kind,list_kinds,scenario_kinds;run_human(toml, lang);error_info(toml, lang);register_catalogue(tag, ftl_text). - MCP:
langwrun_scenarioilist_scenario_kinds.
Błędy i ich kody#
Każdy komunikat o błędzie ma stabilny kod, czyli identyfikator komunikatu wielkimi literami (e-clock-version to E-CLOCK-VERSION), taki sam w każdym języku. Angielski tekst wiersza poleceń pozostaje bez zmian. W innym języku wiersz ma postać text [E-CODE]; z --verbose w każdym języku następuje drugi wiersz code: E-CODE. API udostępnia KshanaError::code() i error_info ({kind, code, message}); błędy MCP niosą {kind, code, message} jako dane błędu. Dopasowuj po kodzie, nigdy po tekście.
Jak działa przebieg w innym języku (zasada podwójnego przebiegu)#
Plik maszynowy nie może zależeć od języka. Przebieg w innym języku wykonuje więc scenariusz dwa razy: raz po angielsku, a jego result.json, report.json i CSV są plikami maszynowymi, i raz w języku użytkownika, dla podsumowania, wykresu i report.html. Angielski to jeden przebieg, bajt w bajt taki, jaki był wcześniej.
Zmierzone, natywnie, kompilacja wydaniowa, angielski, a potem --lang de (sekundy): leo-navmsg-fit-interval-trade 9,2 -> 17,9, cislunar-arc-recovery 7,8 -> 14,1, lunar-station-coverage 7,8 -> 15,0, clock-ensemble 5,8 -> 11,6, leo-navmsg-model-comparison 5,1 -> 10,5, leo-ppp-convergence 2,8 -> 6,2; każdy inny dołączony scenariusz trwa poniżej 4 s; 173 scenariuszy dołączonych w chwili pomiaru zajęło razem 64 s po angielsku. Koszt dla innego języka jest dokładnie dwukrotny. WebAssembly nie zostało zmierzone (brak tu wasm-bindgen); przy współczynniku przeglądarki około 4 to minuta dla najwolniejszego rodzaju.
Strona unika oczekiwania, pokazując od razu wynik angielski i wywołując potem run_human(toml, lang): to jest sam drugi przebieg, zwraca {summary, svg}, a Studio może wyświetlać „tłumaczenie...” do chwili jego nadejścia.
Pozycja 0,41: jeden przebieg. Wygenerować oba z zapisu (message id, arguments). Rozważono to i nie zbudowano: wyniki t! są sprawdzane, mierzone i skrótowane po ich utworzeniu (szerokości tekstu wykresów, skróty pochodzenia nad tekstem wyników), a num() formatuje w miejscu wywołania, więc zapis wprowadziłby do plików maszynowych znaczniki zastępcze i uzależniłby ich bajty od odtworzenia. Wymaga to t!/num zwracających znaczniki, finalizatora w każdym zapisującym oraz dowodu identyczności bajtów dla każdego zapisującego.
WebAssembly#
Kompilacja wasm zawiera tylko katalog angielski. Pozostałe szesnaście dodałoby około 150 kB gzip każdy. Strona pobiera i18n/<lang>/engine.ftl po wyborze języka i wywołuje register_catalogue(tag, text); jeśli pobranie lub rejestracja się nie powiedzie, strona pozostaje po angielsku. Natywnie wiersz poleceń i pakiet Pythona zawierają wszystkie 18.
Rozmiar, nieoptymalizowany .wasm z cargo build --release --target wasm32-unknown-unknown --features wasm --lib (przed wasm-bindgen i wasm-opt), podstawa bc04e98f wobec tej gałęzi: 18,71 MB -> 19,88 MB surowo, 5,43 MB -> 5,79 MB gzip, +357 kB gzip. Katalog angielski ma 521 kB surowo, z czego 153 kB gzip; reszta to fluent-bundle i jego zależności. Przyjęto to tak, jak zmierzono.
Katalog#
i18n/<tag>/engine.ftl zawiera komunikaty, z identyfikatorami z przedrostkiem engine-. i18n/en/engine.ftl jest źródłem; pozostałe pliki należą do tłumaczy i mogą nie zawierać komunikatów (każdy komunikat w razie braku wraca do angielskiego). Listy towarzyszące w i18n/en/: engine-counts.txt (komunikaty ze zmiennymi licznikowymi, dla form liczby), engine-numbers.txt (liczby przekazywane jako placeables), engine-fragments.txt (komunikaty będące fragmentami zdania składanego w czasie działania: do scalenia w komunikaty pełnych zdań, nigdy nie do powiększania).
Testy#
tests/i18n_catalogue.rs dowodzi poprawności angielskiego: każdy komunikat ma zapisany szablon oryginału i renderuje się dokładnie do tego tekstu; każdy identyfikator jest używany; zmienne się zgadzają; pseudolokalizacja renderuje każdy komunikat. i18n_api.rs, i18n_kinds.rs, i18n_legal.rs obejmują punkty wejścia, opisy rodzajów i teksty prawne. i18n_ratchet.rs kończy się niepowodzeniem, jeśli nowy literał łańcuchowy, który czytałby człowiek, omija katalog: dopuszczenie dla pliku w tests/fixtures/i18n/allow.json, które może maleć i nigdy rosnąć. scripts/i18n_compare.py uruchamia całe scenariusze plikiem binarnym bazowym i bieżącym i porównuje każdy plik wyjściowy.
Jak przenieść łańcuch do katalogu: docs/design/i18n-migration.md.
Zależności i licencje#
Framework dodaje fluent-bundle 0,16 (Apache-2.0 OR MIT) i unic-langid 0,9 (MIT OR Apache-2.0), z ich tranzytywnymi crate'ami: fluent-syntax i fluent-langneg (Apache-2.0 OR MIT), intl-memoizer (Apache-2.0 OR MIT), intl_pluralrules (Apache-2.0/MIT), type-map (MIT/Apache-2.0), self_cell (Apache-2.0 OR GPL-2.0-only: używana jest alternatywa Apache-2.0, którą deny.toml dopuszcza), tinystr i zerovec (Unicode-3.0), rustc-hash, smallvec, thiserror, displaydoc (MIT OR Apache-2.0). Każda z nich jest permisywna według listy allow z deny.toml; thiserror występuje w dwóch wersjach (1 i 2), o czym multiple-versions = "warn" tylko informuje. Listę licencji odczytano z cargo metadata; cargo deny check nie jest zainstalowane w środowisku autorskim i działa w CI integracyjnym.