BookTranslator
BookTranslator

Hoe je een JSON-taalbestand vertaalt zonder sleutels of placeholders te breken

Ontdek welke waarden in een en.json-bestand worden vertaald, wat een bestandsvertaler herschrijft (opmerkingen, sleutelvolgorde, grote ID's) en hoe je placeholders, ICU-meervouden en sleutels controleert met een gratis script en een testbestand.

BookTranslator

BookTranslator Team

13 min lezen

Om een JSON-taalbestand zoals en.json te vertalen, geef je het hele bestand aan een vertaler die JSON begrijpt. De ruwe tekst in een chatvenster of een webvertaler plakken is de riskante manier. Een JSON-bewuste vertaler leest het bestand, vertaalt alleen de tekstwaarden en schrijft dezelfde sleutels terug, zodat je app het bestand nog steeds kan laden. Kopiëren en plakken kan een sleutel vertalen, een aanhalingsteken wijzigen of een accolade laten vallen. Elk van die fouten breekt het bestand.

De sleutels behouden is het makkelijke deel. De meeste problemen komen daarna: een {name}-placeholder die terugkomt als {nom}, een ICU-meervoud dat zijn other-geval verliest, een URL die als gewone tekst wordt meegestuurd voor vertaling, en een paar wijzigingen die de vertaler in het bestand zelf aanbrengt. Dat is wat deze gids behandelt.

We hebben een testbestand gemaakt met al die gevallen erin en een gratis checker geschreven die een bronbestand met zijn vertaling vergelijkt. We hebben het testbestand ook door een lokale kopie van de JSON-parseer- en schrijfcode van BookTranslator gehaald, met een markering in plaats van een model, zodat je precies kunt zien welke waarden worden verzonden en hoe het bestand terugkomt.

Snel antwoord: wat gebeurt er met elk deel van het bestand

Deel van het bestandVoorbeeld uit onze testWat BookTranslator doetWat je moet controleren
Sleutels"greeting", "error"Blijven zoals ze zijnNiets
Gewone tekstwaarden"Reading list"VertaaldBetekenis, lengte op het scherm
Waarden met placeholders"Welcome back, {name}!", "Saved %s of %d chapters"Vertaald, placeholders opgenomen in de verzonden tekstPlaceholdernamen en aantallen (de checker doet dit)
ICU-meervoudsberichten"{count, plural, one {# book} other {# books}}"Als één string verzondenHet other-geval, en meervoudscategorieën voor de doeltaal
Inline HTML"accept the <b>Terms</b>."Vertaald; het aantal tags wordt na vertaling gecontroleerdTagnamen en aantallen (de checker doet dit)
URL's opgeslagen als tekst"https://example.com/help"Verzonden voor vertalingDat het ongewijzigd terugkwam
Waarden zonder letters"2.4.1", "—", ""Onveranderd gelatenNiets
Getallen, true/false, null20, trueOnveranderd gelatenZeer grote ID's (zie hieronder)
Arrays van strings["Open", "Save", "Export as EPUB"]Vertaald in het vertaalde bestand; onvertaald gelaten in het tweetalige bestandHet tweetalige bestand, als je dat gebruikt
Opmerkingen (JSONC)// Shown on the home screenVerwijderd. Als een waarde ook // bevat, kan het bestand niet ladenVerwijder opmerkingen vóór het uploaden

De rest van deze gids laat zien waar elke rij vandaan komt en wat je eraan kunt doen.

Stap 1: Controleer het bronbestand voordat je uploadt

Voer de checker uit op het bestand dat je wilt vertalen. Je hebt alleen Python 3 nodig:

python3 check_i18n_json.py en.json

Op ons testbestand gaf het dit weer:

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

De twee waarschuwingen en de opmerking over op gehele getallen lijkende sleutels komen elk overeen met een wijziging die we zagen toen ons testbestand door de vertaler ging. Als jouw bestand geen van deze meldingen krijgt, kun je doorgaan naar Stap 3.

De checker weigert ook een bestand dat geen strikte JSON is, zoals een bestand met opmerkingen of komma's aan het einde, omdat dat het volgende probleem is.

Wat er terugkwam uit ons testbestand

De test werd uitgevoerd op 2 november 2026, tegen de huidige JSON-verwerking in de backend van BookTranslator. In plaats van een model aan te roepen, verving de test elke waarde die voor vertaling verzonden zou worden door [ZH] plus de originele tekst. Dat houdt het resultaat voorspelbaar: alles met [ZH] ervoor werd verzonden, alles zonder niet. Beide uitvoerbestanden zijn gepubliceerd: het vertaalde bestand en het tweetalige bestand.

Het vertaalde bestand

Elke sleutel kwam ongewijzigd terug en het bestand kon nog steeds worden geparseerd. Waarden met letters werden verzonden, waaronder {name}, %s, het ICU-bericht, de tag <b> en de help-URL. De versiestring "2.4.1", het streepje en de lege string werden niet verzonden, omdat ze geen letters bevatten. Getallen en booleans bleven ongewijzigd.

Vier andere dingen veranderden:

  1. Eén string belandde in de verkeerde sleutel. In onze test had ons bestand een platte sleutel "error.network" met de tekst "Check your connection." en ook een geneste "error": { "network": "Network error" }. De vertaler identificeert elke string aan de hand van het pad met punten tussen de delen, dus beide strings hadden hetzelfde adres, error.network. In de uitvoer bevatten beide sleutels de vertaling van "Network error". "Check your connection." was verdwenen. Dit vereist beide stijlen in hetzelfde bestand, wat zeldzaam is, maar het faalt stil. De checker waarschuwt hiervoor voordat je uploadt.
  2. Sleutels die op integers lijken werden herordend. "steps": { "10": ..., "2": ..., "1": ... } kwam terug als "1", "2", "10". JavaScript vermeldt sleutels die op integers lijken altijd eerst, in numerieke volgorde, wanneer het een object opnieuw opbouwt. De sleutels en waarden komen nog steeds overeen, dus je app werkt prima, maar een diff ten opzichte van het bronbestand zal rommelig zijn.
  3. Een grote ID verloor precisie. 9007199254740993 kwam terug als 9007199254740992. JavaScript slaat getallen op als 64-bits floating point, en integers boven 2^53 kunnen niet allemaal exact worden weergegeven (MDN over Number.MAX_SAFE_INTEGER). Sla lange ID's op als strings, of houd ze buiten het taalbestand.
  4. De opmaak werd herschreven. De uitvoer gebruikt inspringing met twee spaties en eindigt met een nieuwe regel. Als je bron tabs of vier spaties gebruikte, verwacht dan witruimtewijzigingen in de diff.

Ons testbestand had geen dubbele sleutels, maar het is goed om ervan op de hoogte te zijn: de JSON-parser van JavaScript behoudt alleen de laatste waarde voor een herhaalde sleutel, dus de eerdere tekst wordt nooit vertaald. Je app zou dezelfde laatste waarde laden, dus er breekt niets zichtbaars, maar de checker vermeldt dubbele sleutels zodat je ze kunt opschonen.

Het tweetalige bestand

Het tweetalige bestand behoudt elke oorspronkelijke waarde en voegt een zustersleutel toe met _translated aan het einde:

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

Dat werkt goed voor beoordeling, omdat de bron en de vertaling naast elkaar staan. Het is geen bestand dat je app kan laden, omdat elke sleutel nu een tweeling heeft. Gebruik het vertaalde bestand in je app.

Eén hiaat: strings in arrays krijgen geen _translated-zuster, omdat array-items geen sleutel hebben om er een aan toe te voegen. In onze test kwam "menu": ["Open", "Save", "Export as EPUB"] in het Engels terug in het tweetalige bestand, maar vertaald in het vertaalde bestand. Als je beoordeelt op basis van het tweetalige bestand, controleer dan arrays in het vertaalde bestand.

Bestanden met opmerkingen

Veel projecten bewaren opmerkingen in hun taalbestanden (JSONC), wat strikte JSON niet toestaat. De vertaler probeert eerst strikte JSON. Als dat mislukt, verwijdert hij opmerkingen en trailing commas en probeert het opnieuw. Opmerkingen verwijderen betekent alles na // op een regel verwijderen, en dat omvat de // in https://.

Ons bestand met een opmerking en een URL laadde helemaal niet; het parsen mislukte bij de eerste opmerking. Een bestand met opmerkingen maar zonder // in een waarde laadde prima, en de opmerkingen werden uit de uitvoer verwijderd.

Dus: als je bestand opmerkingen bevat, verwijder ze dan voordat je uploadt, of bewaar een kopie met de opmerkingen en accepteer dat de vertaling ze niet zal bevatten.

Stap 2: Het bestand voorbereiden

Je kunt de meeste van deze problemen oplossen voordat je vertaalt:

  • Maak er strikte JSON van. Verwijder opmerkingen en trailing commas. Als je build-tool JSONC leest, houd dan het bestand met opmerkingen als je bron en exporteer een schone kopie voor vertaling.
  • Gebruik één sleutelstijl. Nest sleutels of plat ze met punten, niet beide in één bestand. Als je i18next gebruikt, bepaalt de keySeparator-optie hoe punten in sleutels worden gelezen, dus controleer die instelling voordat je iets platmaakt.
  • Verplaats niet-tekstuele gegevens naar buiten. ID's, URL's, feature flags en limieten horen in config, niet in het taalbestand. Als een URL moet blijven, behoud deze dan, maar verwacht dat je hem na de vertaling controleert. Onze gids voor niet-vertalen behandelt hoe je besluit wat er nog meer in de oorspronkelijke taal moet blijven.
  • Schrijf placeholders overal op dezelfde manier. Eén stijl, zoals {name}, is gemakkelijker te controleren dan een mix van {name}, {{name}} en %s.

Stap 3: Het hele bestand vertalen

Upload het opgeschoonde bestand naar BookTranslator's documentvertaler, die .json vermeldt bij de andere localisatie- en gestructureerde tekstformaten die het accepteert. De prijs wordt getoond vóór het afrekenen. Wanneer de taak is voltooid, biedt je geschiedenispagina het vertaalde bestand en het tweetalige bestand als afzonderlijke downloads aan.

Twee dingen in de manier waarop de tekst wordt vertaald, beïnvloeden het resultaat:

  • Het model ziet je sleutels niet. Elke waarde wordt als een genummerd segment verzonden, zonder de sleutelnaam. Als "button.open" en "status.open" beide "Open" zeggen, heeft het model geen manier om te weten dat de ene een werkwoord is en de andere een bijvoeglijk naamwoord. Korte, dubbelzinnige strings zijn degene die je handmatig moet controleren.
  • Elke waarde wordt afzonderlijk vertaald. Niets verbindt twee sleutels met elkaar, dus hetzelfde woord op twee plaatsen kan op twee verschillende manieren terugkomen, en een woord dat twee verschillende vertalingen nodig heeft, kan hetzelfde terugkomen. De optionele automatische woordenlijst van BookTranslator verzamelt namen, plaatsen en gespecialiseerde termen en houdt ze consistent, maar je kunt er geen vaste vertalingen van je eigen aan geven. Voor termen die op één exacte manier vertaald moeten worden, houd je een termenlijst bij en controleer je die strings na de vertaling; onze woordenlijstgids legt uit hoe je er een plant.

Inline HTML-tags worden na de vertaling geteld, en een segment waarvan de tags niet overeenkomen, wordt opnieuw geprobeerd. Placeholders met accolades, placeholders in %s-stijl en ICU-syntaxis worden als onderdeel van de tekst verzonden, maar worden niet op dezelfde manier gecontroleerd. Voer stap 4 uit op elke vertaling.

Als je liever vertaalt met een chatmodel of een lokaal model, stuur dan alleen de waarden, nooit het ruwe bestand, en zet ze met een script terug op sleutel. De onderstaande controles gelden in beide gevallen.

Stap 4: Controleer het vertaalde bestand

Vergelijk de vertaling met de bron:

python3 check_i18n_json.py en.json fr.json

Om te laten zien wat het vangt, hebben we een Frans bestand met ingebouwde fouten gemaakt. Het is handmatig gemaakt, geen modeluitvoer. Na dezelfde bronwaarschuwingen als in stap 1, drukte de checker af:

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

Elk van die zou als een bug in de app verschijnen: een lege naam, een kapotte link, een crash of een onbewerkte melding wanneer het meervoud wordt weergegeven, een ontbrekend aantal hoofdstukken, een limiet die als tekst wordt vergeleken. De INFO-regel somt strings op die ongewijzigd terugkwamen. Sommige zijn prima, zoals merknamen; "Save" in een Frans menu is dat niet.

De checker vergelijkt sleutels, waardetypen, niet-tekstwaarden, placeholders ({name}, {{name}}, %s, %1$s, %@), ICU-argumentnamen, HTML-tags en URL's. Hij beoordeelt de vertaling zelf niet.

Meervoudsvormen horen te veranderen

Een ICU-meervoud bevat één bericht per meervoudscategorie. Engels heeft twee categorieën, one en other, maar veel talen hebben een andere set. Een goede Russische vertaling van ons items-bericht zou normaal gesproken one, few, many en other gebruiken, en een Japanse alleen other. Een mismatch hier is dus niet altijd een fout.

De categorieën komen uit de Unicode CLDR meervoudsregels, en other is degene die elke taal heeft. Dit zijn de kardinale categorieën die door Intl.PluralRules in Node.js met CLDR 48 worden gerapporteerd:

TaalMeervoudscategorieën
Engels, Duitsone, other
Frans, Spaans, Portugeesone, many, other
Russisch, Poolsone, few, many, other
Arabischzero, one, two, few, many, other
Japans, Chinees, Koreaansoverig

Frans, Spaans en Portugees gebruiken many alleen voor zeer grote ronde getallen, zoals een miljoen, dus een bericht voor kleine aantallen kan het meestal missen. De checker meldt een ontbrekende other als fout, en elke andere wijziging in categorieën als waarschuwing om te vergelijken met deze tabel. Je kunt de lijst voor elke taal zelf afdrukken:

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

De vertaler krijgt de Engelse gevallen te zien, dus controleer wat er terugkwam. Als een Russisch of Arabisch bericht nog steeds alleen one en other heeft, voeg de ontbrekende gevallen dan handmatig toe, of laat een vertaler dat doen.

Laad het daarna in de app

De checker bewijst dat het bestand veilig te laden is, niet dat het lekker leest. Schakel de app naar de nieuwe taal en bekijk de schermen: lange woorden in het Duits of Fins die uit een knop lopen, rechts-naar-links tekst in het Arabisch of Hebreeuws, en datums of getallen die je code apart van het taalbestand formatteert. Onze gids over vertalen versus lokaliseren behandelt wat er naast de woorden nog meer verandert tussen markten.

Veelvoorkomende problemen

Het vertaalde bestand laadt niet in mijn app. Voer de checker erop uit. Als de checker het niet kan parsen, is het bestand na de vertaling bewerkt of was het vanaf het begin ongeldig. Als het wel parset, zoek dan naar ERROR-regels over sleutels of typen.

Een string verschijnt als een onbewerkt ICU-bericht, zoals {count, plural, ...}. De ICU-syntaxis is tijdens de vertaling stukgegaan, meestal door een ontbrekende accolade of een ontbrekende other-case. Kopieer de structuur uit de bron en vertaal alleen de woorden binnen elke case.

Mijn bestand is YAML, een .po-bestand of Markdown. BookTranslator accepteert die ook, maar elk formaat heeft zijn eigen regels. Voor documentatie, zie onze Markdown-vertaalgids; voor webpagina's, de HTML-bestandsgids.

Kan ik alleen de nieuwe sleutels vertalen? Niet binnen één upload; het hele bestand wordt vertaald. Zet de nieuwe sleutels in een apart bestand, vertaal dat, en voeg het resultaat samen op sleutel. De checker bevestigt dat het samengevoegde bestand elke sleutel bevat.

Moet ik de sleutels vertalen? Nee. Sleutels zijn identificaties die je code opzoekt. Alleen de waarden zijn tekst voor mensen om te lezen.

Gerelateerde berichten