BookTranslator
BookTranslator

Jak przetłumaczyć plik językowy JSON bez psucia kluczy i placeholderów

Zobacz, które wartości w pliku en.json są tłumaczone, co tłumacz plików przepisuje (komentarze, kolejność kluczy, duże identyfikatory) i jak sprawdzić placeholdery, liczby mnogie ICU oraz klucze za pomocą darmowego skryptu i pliku testowego.

BookTranslator

BookTranslator Team

12 min czytania

Aby przetłumaczyć plik językowy JSON, taki jak en.json, przekaż cały plik tłumaczowi, który rozumie JSON. Wklejanie surowego tekstu do okna czatu lub tłumacza internetowego to ryzykowny sposób. Tłumacz obsługujący JSON odczytuje plik, tłumaczy tylko wartości tekstowe i zapisuje te same klucze z powrotem, dzięki czemu Twoja aplikacja nadal może wczytać plik. Kopiowanie i wklejanie może przetłumaczyć klucz, zmienić cudzysłów lub zgubić nawias klamrowy. Każda z tych sytuacji psuje plik.

Zachowanie kluczy to łatwa część. Większość problemów pojawia się później: placeholder {name}, który wraca jako {nom}, liczba mnoga ICU, która traci przypadek other, adres URL wysłany do tłumaczenia jak zwykły tekst oraz kilka zmian, które tłumacz wprowadza w samym pliku. Właśnie tym zajmuje się ten przewodnik.

Stworzyliśmy plik testowy zawierający każdy z tych przypadków i napisaliśmy darmowy walidator, który porównuje plik źródłowy z jego tłumaczeniem. Przepuściliśmy również plik testowy przez lokalną kopię kodu parsowania i zapisu JSON używanego przez BookTranslator, ze znacznikiem w miejscu modelu, abyś mógł zobaczyć dokładnie, które wartości są wysyłane i jak plik wraca.

Szybka odpowiedź: co dzieje się z każdą częścią pliku

Część plikuPrzykład z naszego testuCo robi BookTranslatorCo sprawdzić
Klucze"greeting", "error"Zachowane bez zmianNic
Wartości tekstowe"Reading list"PrzetłumaczoneZnaczenie, długość na ekranie
Wartości z placeholderami"Welcome back, {name}!", "Saved %s of %d chapters"Przetłumaczone, placeholdery uwzględnione w wysyłanym tekścieNazwy i liczba placeholderów (robi to walidator)
Komunikaty liczby mnogiej ICU"{count, plural, one {# book} other {# books}}"Wysyłane jako jeden ciągPrzypadek other i kategorie liczby mnogiej dla języka docelowego
Wbudowany HTML"accept the <b>Terms</b>."Przetłumaczone; liczba tagów jest sprawdzana po tłumaczeniuNazwy i liczba tagów (sprawdza to walidator)
Adresy URL zapisane jako tekst"https://example.com/help"Wysyłane do tłumaczeniaŻe wróciło bez zmian
Wartości bez liter"2.4.1", "—", ""Pozostawione bez zmianNic
Liczby, true/false, null20, truePozostawione bez zmianBardzo duże identyfikatory (patrz poniżej)
Tablice ciągów znaków["Open", "Save", "Export as EPUB"]Tłumaczone w pliku przetłumaczonym; pozostawione bez tłumaczenia w pliku dwujęzycznymPlik dwujęzyczny, jeśli go używasz
Komentarze (JSONC)// Shown on the home screenUsunięte. Jeśli wartość zawiera również //, plik może się nie wczytaćUsuń komentarze przed przesłaniem

W dalszej części tego przewodnika pokazujemy, skąd bierze się każdy wiersz i co z nim zrobić.

Krok 1: Sprawdź plik źródłowy przed przesłaniem

Uruchom walidator na pliku, który planujesz przetłumaczyć. Potrzebuje tylko Pythona 3:

python3 check_i18n_json.py en.json

Na naszym pliku testowym wyświetlił:

WARN  limits.projectId: 9007199254740993 is above 2^53 and can change in a JavaScript round trip; store IDs as strings
WARN  error.network: a flat key with dots and a nested key resolve to the same dot path; tools that address strings by dot path can mix them up
INFO  (root): 3 keys look like integers; JavaScript sorts those first and in numeric order when it rewrites the object
INFO  source: 17 strings, 2 numbers

Dwa ostrzeżenia i uwaga o kluczach przypominających liczby całkowite odpowiadają zmianom, które zaobserwowaliśmy, gdy nasz plik testowy przeszedł przez tłumacz. Jeśli Twój plik nie generuje żadnego z nich, możesz przejść do kroku 3.

Walidator odrzuca również plik, który nie jest ścisłym JSON-em, na przykład taki z komentarzami lub końcowymi przecinkami, ponieważ to jest kolejny problem.

Co wróciło z naszego pliku testowego

Test został przeprowadzony 2 listopada 2026 r. na bieżącej obsłudze JSON w backendzie BookTranslator. Zamiast wywoływać model, test zastąpił każdą wartość, która zostałaby wysłana do tłumaczenia, ciągiem [ZH] plus oryginalny tekst. Dzięki temu wynik jest przewidywalny: wszystko z [ZH] na początku zostało wysłane, a wszystko bez niego nie. Oba wyniki są opublikowane: plik przetłumaczony i plik dwujęzyczny.

Przetłumaczony plik

Każdy klucz wrócił bez zmian, a plik nadal się parsował. Wysłano wartości zawierające litery, w tym {name}, %s, komunikat ICU, znacznik <b> i adres URL pomocy. Ciąg wersji "2.4.1", myślnik i pusty ciąg nie zostały wysłane, ponieważ nie zawierają liter. Liczby i wartości logiczne pozostały nietknięte.

Zmieniły się cztery inne rzeczy:

  1. Jeden ciąg trafił do niewłaściwego klucza. W naszym teście plik zawierał płaski klucz "error.network" z tekstem "Check your connection." oraz zagnieżdżony "error": { "network": "Network error" }. Tłumacz identyfikuje każdy ciąg po jego ścieżce z kropkami między częściami, więc oba ciągi miały ten sam adres, error.network. W wyniku oba klucze zawierały tłumaczenie "Network error". "Check your connection." zniknęło. Wymaga to obu stylów w tym samym pliku, co jest rzadkie, ale kończy się cichym błędem. Narzędzie sprawdzające ostrzega o tym przed przesłaniem.
  2. Klucze przypominające liczby całkowite zostały uporządkowane inaczej. "steps": { "10": ..., "2": ..., "1": ... } wróciło jako "1", "2", "10". JavaScript zawsze najpierw wymienia klucze przypominające liczby całkowite, w kolejności numerycznej, gdy odtwarza obiekt. Klucze i wartości nadal pasują, więc Twoja aplikacja działa poprawnie, ale porównanie z plikiem źródłowym będzie pełne szumu.
  3. Duży identyfikator utracił precyzję. 9007199254740993 wróciło jako 9007199254740992. JavaScript przechowuje liczby jako 64-bitowe liczby zmiennoprzecinkowe, a liczby całkowite powyżej 2^53 nie mogą być wszystkie dokładnie reprezentowane (MDN o Number.MAX_SAFE_INTEGER). Przechowuj długie identyfikatory jako ciągi znaków lub trzymaj je poza plikiem językowym.
  4. Formatowanie zostało przepisane. Wynik używa wcięcia dwóch spacji i kończy się znakiem nowej linii. Jeśli źródło używało tabulatorów lub czterech spacji, spodziewaj się zmian białych znaków w porównaniu.

Nasz plik testowy nie miał zduplikowanych kluczy, ale warto o nich wiedzieć: parser JSON w JavaScripcie zachowuje tylko ostatnią wartość dla powtórzonego klucza, więc wcześniejszy tekst nigdy nie jest tłumaczony. Twoja aplikacja załadowałaby tę samą ostatnią wartość, więc nic widocznego się nie psuje, ale narzędzie sprawdzające wymienia zduplikowane klucze, abyś mógł je uporządkować.

Plik dwujęzyczny

Plik dwujęzyczny zachowuje każdą oryginalną wartość i dodaje sąsiedni klucz z _translated na końcu:

"greeting": "Welcome back, {name}!",
"greeting_translated": "[ZH] Welcome back, {name}!",

To dobrze sprawdza się przy przeglądzie, ponieważ źródło i tłumaczenie są obok siebie. To nie jest plik, który Twoja aplikacja może załadować, ponieważ każdy klucz ma teraz bliźniaka. Używaj przetłumaczonego pliku w swojej aplikacji.

Jedna luka: ciągi wewnątrz tablic nie otrzymują sąsiedniego _translated, ponieważ elementy tablicy nie mają klucza, do którego można by go dodać. W naszym teście "menu": ["Open", "Save", "Export as EPUB"] wróciło po angielsku w pliku dwujęzycznym, ale przetłumaczone w pliku przetłumaczonym. Jeśli przeglądasz z pliku dwujęzycznego, sprawdź tablice w pliku przetłumaczonym.

Pliki z komentarzami

Wiele projektów trzyma komentarze w plikach językowych (JSONC), czego ścisły JSON nie pozwala. Tłumacz najpierw próbuje ścisłego JSON. Jeśli to się nie uda, usuwa komentarze i końcowe przecinki i próbuje ponownie. Usuwanie komentarzy oznacza usunięcie wszystkiego po // w wierszu, a to obejmuje // w https://.

Nasz plik z komentarzem i adresem URL w ogóle się nie załadował; parsowanie nie powiodło się przy pierwszym komentarzu. Plik z komentarzami, ale bez // w żadnej wartości, załadował się poprawnie, a komentarze zostały usunięte z wyniku.

Tak więc: jeśli Twój plik ma komentarze, usuń je przed przesłaniem lub zachowaj kopię z komentarzami i zaakceptuj, że tłumaczenie ich nie będzie miało.

Krok 2: Przygotuj plik

Większość tych problemów możesz naprawić przed tłumaczeniem:

  • Uczyń go ścisłym JSON. Usuń komentarze i końcowe przecinki. Jeśli Twoje narzędzie budowania czyta JSONC, zachowaj plik z komentarzami jako źródło i wyeksportuj czystą kopię do tłumaczenia.
  • Używaj jednego stylu kluczy. Albo zagnieżdżaj klucze, albo spłaszczaj je kropkami, nie oba w jednym pliku. Jeśli używasz i18next, jego opcja keySeparator kontroluje sposób odczytywania kropek w kluczach, więc sprawdź to ustawienie, zanim cokolwiek spłaszczysz.
  • Przenieś dane nietekstowe na zewnątrz. Identyfikatory, adresy URL, flagi funkcji i limity należą do konfiguracji, a nie do pliku językowego. Jeśli adres URL musi pozostać, zachowaj go, ale spodziewaj się sprawdzenia go po tłumaczeniu. Nasz przewodnik po tym, czego nie tłumaczyć omawia, jak zdecydować, co jeszcze powinno pozostać w oryginalnym języku.
  • Zapisuj placeholdery wszędzie w ten sam sposób. Jeden styl, taki jak {name}, jest łatwiejszy do sprawdzenia niż mieszanka {name}, {{name}} i %s.

Krok 3: Przetłumacz cały plik

Prześlij oczyszczony plik do tłumacza dokumentów BookTranslator, który wymienia .json wśród innych akceptowanych formatów lokalizacyjnych i tekstów strukturalnych. Cena jest wyświetlana przed płatnością. Po zakończeniu zadania strona historii oferuje przetłumaczony plik i plik dwujęzyczny jako osobne pliki do pobrania.

Na wynik wpływa to, jak tłumaczony jest tekst:

  • Model nie widzi Twoich kluczy. Każda wartość jest wysyłana jako numerowany segment, bez nazwy klucza. Jeśli "button.open" i "status.open" mają wartość „Open”, model nie ma sposobu, by wiedzieć, że jedno to czasownik, a drugie przymiotnik. Krótkie, niejednoznaczne ciągi znaków to te, które warto sprawdzić ręcznie.
  • Każda wartość jest tłumaczona osobno. Nic nie łączy dwóch kluczy, więc to samo słowo w dwóch miejscach może wrócić w dwóch różnych wersjach, a słowo wymagające dwóch różnych tłumaczeń może wrócić takie samo. Opcjonalny automatyczny glosariusz BookTranslatora zbiera nazwy, miejsca i terminy specjalistyczne i utrzymuje ich spójność, ale nie można mu podać własnych stałych tłumaczeń. W przypadku terminów, które muszą być przetłumaczone w jeden konkretny sposób, prowadź listę terminów i sprawdź te ciągi znaków po tłumaczeniu; nasz przewodnik po glosariuszach wyjaśnia, jak go zaplanować.

Wbudowane znaczniki HTML są liczone po tłumaczeniu, a segment, którego znaczniki się nie zgadzają, jest ponawiany. Placeholdery w nawiasach klamrowych, placeholdery w stylu %s i składnia ICU są wysyłane jako część tekstu, ale nie są sprawdzane w ten sam sposób. Wykonaj krok 4 dla każdego tłumaczenia.

Jeśli wolisz tłumaczyć za pomocą modelu czatu lub modelu lokalnego, wysyłaj tylko wartości, nigdy surowego pliku, i przywróć je według kluczy za pomocą skryptu. Poniższe kontrole mają zastosowanie w obu przypadkach.

Krok 4: Sprawdź przetłumaczony plik

Porównaj tłumaczenie ze źródłem:

python3 check_i18n_json.py en.json fr.json

Aby pokazać, co wyłapuje, przygotowaliśmy francuski plik z celowo wprowadzonymi błędami. Jest wykonany ręcznie, nie jest wynikiem modelu. Po tych samych ostrzeżeniach źródłowych co w kroku 1 narzędzie sprawdzające wyświetliło:

ERROR app.greeting: placeholders differ; lost ['name'] extra ['nom']
ERROR app.help_url: urls differ; lost ['https://example.com/help'] extra ['https://example.com/aide']
ERROR app.items: ICU message has no 'other' case
ERROR app.legacy: placeholders differ; lost ['%d'] extra []
ERROR limits.maxFiles: type changed from int to str
ERROR limits.projectId: non-text value changed: 9007199254740993 -> 9007199254740992
INFO  menu[1]: identical to source: 'Save'
INFO  compared 20 source values, 1 unchanged strings

Każdy z nich objawiłby się jako błąd w aplikacji: pusta nazwa, zepsuty link, awaria lub surowy komunikat przy renderowaniu liczby mnogiej, brakująca liczba rozdziałów, limit porównywany jako tekst. Wiersz INFO wymienia ciągi znaków, które wróciły niezmienione. Niektóre są w porządku, jak nazwy marek; „Save” w francuskim menu już nie.

Narzędzie sprawdzające porównuje klucze, typy wartości, wartości inne niż tekstowe, placeholdery ({name}, {{name}}, %s, %1$s, %@), nazwy argumentów ICU, znaczniki HTML i adresy URL. Nie ocenia samego tłumaczenia.

Przypadki liczby mnogiej mają się zmieniać

Liczba mnoga ICU zawiera jedną wiadomość na kategorię liczby mnogiej. Angielski ma dwie kategorie, one i other, ale wiele języków ma inny zestaw. Dobre rosyjskie tłumaczenie naszej wiadomości items zwykle używałoby one, few, many i other, a japońskie tylko other. Niezgodność w tym miejscu nie zawsze jest więc błędem.

Kategorie pochodzą z reguł liczby mnogiej Unicode CLDR, a other to ta, którą ma każdy język. Oto kategorie główne zgłaszane przez Intl.PluralRules w Node.js z CLDR 48:

JęzykKategorie liczby mnogiej
angielski, niemieckione, other
francuski, hiszpański, portugalskione, many, other
rosyjski, polskione, few, many, other
arabskizero, one, two, few, many, other
japoński, chiński, koreańskiinne

Francuski, hiszpański i portugalski używają kategorii many tylko dla bardzo dużych okrągłych liczb, takich jak milion, więc komunikat dla małych wartości zwykle może się bez niej obejść. Walidator zgłasza brak kategorii other jako błąd, a każdą inną zmianę kategorii jako ostrzeżenie do porównania z tą tabelą. Możesz samodzielnie wyświetlić listę dla dowolnego języka:

node -e 'console.log(new Intl.PluralRules("ru").resolvedOptions().pluralCategories)'

Tłumacz otrzymuje angielskie przypadki, więc sprawdź, co zwrócił. Jeśli komunikat rosyjski lub arabski nadal ma tylko one i other, dodaj brakujące przypadki ręcznie lub zleć to tłumaczowi.

Następnie wczytaj go w aplikacji

Walidator dowodzi, że plik można bezpiecznie wczytać, a nie że dobrze się czyta. Przełącz aplikację na nowy język i obejrzyj ekrany: długie słowa w niemieckim lub fińskim, które wystają z przycisku, tekst od prawej do lewej w arabskim lub hebrajskim oraz daty i liczby, które kod formatuje osobno od pliku językowego. Nasz przewodnik po tłumaczeniu a lokalizacji opisuje, co jeszcze zmienia się między rynkami poza samymi słowami.

Częste problemy

Przetłumaczony plik nie chce się wczytać w mojej aplikacji. Uruchom na nim walidator. Jeśli walidator nie potrafi go przeanalizować, plik został zmieniony po tłumaczeniu albo od początku był niepoprawny. Jeśli analiza się powiedzie, szukaj wierszy ERROR dotyczących kluczy lub typów.

Ciąg znaków wyświetla się jako surowy komunikat ICU, np. {count, plural, ...}. Składnia ICU uległa zniszczeniu w tłumaczeniu, zwykle z powodu brakującego nawiasu klamrowego lub brakującego przypadku other. Skopiuj strukturę ze źródła i przetłumacz tylko słowa wewnątrz każdego przypadku.

Mój plik to YAML, plik .po lub Markdown. BookTranslator obsługuje również te formaty, ale każdy z nich ma własne zasady. W przypadku dokumentacji zobacz nasz przewodnik po tłumaczeniu Markdown; w przypadku stron internetowych — przewodnik po plikach HTML.

Czy mogę przetłumaczyć tylko nowe klucze? Nie w ramach jednego przesłania; tłumaczony jest cały plik. Umieść nowe klucze w osobnym pliku, przetłumacz go i scal wynik według kluczy. Walidator potwierdzi, że scalony plik zawiera wszystkie klucze.

Czy powinienem tłumaczyć klucze? Nie. Klucze to identyfikatory, których szuka twój kod. Tylko wartości są tekstem do czytania przez ludzi.

Powiązane wpisy