BookTranslator
BookTranslator

Como traduzir um ficheiro de idioma JSON sem quebrar chaves ou placeholders

Veja quais valores num ficheiro en.json são traduzidos, o que um tradutor de ficheiros reescreve (comentários, ordem das chaves, IDs grandes) e como verificar placeholders, plurais ICU e chaves com um script gratuito e um ficheiro de teste.

BookTranslator

BookTranslator Team

14 min de leitura

Para traduzir um ficheiro de idioma JSON como en.json, entregue o ficheiro completo a um tradutor que compreenda JSON. Colar o texto bruto numa janela de chat ou num tradutor web é a forma arriscada de o fazer. Um tradutor que compreende JSON lê o ficheiro, traduz apenas os valores de texto e volta a escrever as mesmas chaves, para que a sua aplicação continue a conseguir carregar o ficheiro. Copiar e colar pode traduzir uma chave, alterar uma aspa ou eliminar uma chaveta. Qualquer uma destas situações quebra o ficheiro.

Manter as chaves é a parte fácil. A maior parte dos problemas surge depois: um placeholder {name} que volta como {nom}, um plural ICU que perde o caso other, um URL enviado para tradução como qualquer outro texto e algumas alterações que o tradutor faz ao próprio ficheiro. É isso que este guia aborda.

Criámos um ficheiro de teste com todos esses casos e escrevemos um verificador gratuito que compara um ficheiro de origem com a sua tradução. Também executámos o ficheiro de teste através de uma cópia local do código de análise e escrita de JSON que o BookTranslator utiliza, com um marcador no lugar de um modelo, para que possa ver exatamente quais valores são enviados e como o ficheiro regressa.

Resposta rápida: o que acontece a cada parte do ficheiro

Parte do ficheiroExemplo do nosso testeO que o BookTranslator fazO que verificar
Chaves"greeting", "error"Mantidas como estãoNada
Valores de texto simples"Reading list"TraduzidosSignificado, comprimento no ecrã
Valores com placeholders"Welcome back, {name}!", "Saved %s of %d chapters"Traduzidos, placeholders incluídos no texto enviadoNomes e contagens de placeholders (o verificador faz isto)
Mensagens de plural ICU"{count, plural, one {# book} other {# books}}"Enviadas como uma única stringO caso other e as categorias de plural para o idioma de destino
HTML inline"accept the <b>Terms</b>."Traduzido; a contagem de etiquetas é verificada após a traduçãoNomes e contagens de etiquetas (o verificador faz isto)
URLs armazenados como texto"https://example.com/help"Enviados para traduçãoQue voltaram inalterados
Valores sem letras"2.4.1", "—", ""Deixados como estavamNada
Números, true/false, null20, trueDeixados como estavamIDs muito grandes (ver abaixo)
Arrays de strings["Open", "Save", "Export as EPUB"]Traduzidos no ficheiro traduzido; não traduzidos no ficheiro bilingueO ficheiro bilingue, se o utilizar
Comentários (JSONC)// Shown on the home screenRemovidos. Se um valor também contiver //, o ficheiro pode não carregarRemova os comentários antes de carregar

O resto deste guia mostra de onde vem cada linha e o que fazer em relação a ela.

Passo 1: Verifique o ficheiro de origem antes de carregar

Execute o verificador no ficheiro que pretende traduzir. Só precisa de Python 3 e de mais nada:

python3 check_i18n_json.py en.json

No nosso ficheiro de teste, imprimiu:

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

Os dois avisos e a nota sobre chaves semelhantes a inteiros correspondem cada um a uma alteração que observámos quando o nosso ficheiro de teste passou pelo tradutor. Se o seu ficheiro não apresentar nenhum deles, pode avançar para o Passo 3.

O verificador também rejeita um ficheiro que não seja JSON estrito, como um com comentários ou vírgulas finais, porque esse é o problema seguinte.

O que veio do nosso ficheiro de teste

O teste foi executado a 2 de novembro de 2026, com o tratamento atual de JSON no backend do BookTranslator. Em vez de chamar um modelo, o teste substituiu cada valor que seria enviado para tradução por [ZH] mais o texto original. Isso mantém o resultado previsível: tudo o que tem [ZH] à frente foi enviado, tudo o que não tem não foi. Ambos os resultados estão publicados: o ficheiro traduzido e o ficheiro bilingue.

O ficheiro traduzido

Todas as chaves foram devolvidas sem alterações e o ficheiro continuou a ser analisado corretamente. Foram enviados os valores com letras, incluindo {name}, %s, a mensagem ICU, a etiqueta <b> e o URL de ajuda. A string de versão "2.4.1", o travessão e a string vazia não foram enviados, porque não contêm letras. Os números e os booleanos ficaram intactos.

Outras quatro coisas mudaram:

  1. Uma string foi parar à chave errada. No nosso teste, o ficheiro tinha uma chave plana "error.network" com o texto "Check your connection." e também uma chave aninhada "error": { "network": "Network error" }. O tradutor identifica cada string pelo seu caminho com pontos entre as partes, pelo que ambas as strings tinham o mesmo endereço, error.network. No resultado, ambas as chaves continham a tradução de "Network error". "Check your connection." desapareceu. Isto exige os dois estilos no mesmo ficheiro, o que é raro, mas falha silenciosamente. O verificador avisa sobre isto antes de carregar o ficheiro.
  2. As chaves semelhantes a inteiros foram reordenadas. "steps": { "10": ..., "2": ..., "1": ... } voltou como "1", "2", "10". O JavaScript lista sempre primeiro as chaves semelhantes a inteiros, por ordem numérica, quando reconstrói um objeto. As chaves e os valores continuam a corresponder, pelo que a sua aplicação está bem, mas um diff em relação ao ficheiro de origem ficará ruidoso.
  3. Um ID grande perdeu precisão. 9007199254740993 voltou como 9007199254740992. O JavaScript armazena números como vírgula flutuante de 64 bits, e os inteiros acima de 2^53 não podem todos ser representados exatamente (MDN sobre Number.MAX_SAFE_INTEGER). Armazene IDs longos como strings ou mantenha-os fora do ficheiro de idioma.
  4. A formatação foi reescrita. O resultado usa indentação de dois espaços e termina com uma nova linha. Se a origem usava tabulações ou quatro espaços, espere alterações de espaços em branco no diff.

O nosso ficheiro de teste não tinha chaves duplicadas, mas vale a pena conhecê-las: o analisador JSON do JavaScript mantém apenas o último valor de uma chave repetida, pelo que o texto anterior nunca é traduzido. A sua aplicação carregaria o mesmo último valor, pelo que nada visível se quebra, mas o verificador lista as chaves duplicadas para que as possa limpar.

O ficheiro bilingue

O ficheiro bilingue mantém cada valor original e acrescenta uma chave irmã com _translated no final:

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

Isso funciona bem para revisão, porque a origem e a tradução ficam lado a lado. Não é um ficheiro que a sua aplicação possa carregar, porque agora cada chave tem um gémeo. Use o ficheiro traduzido na sua aplicação.

Uma lacuna: as strings dentro de arrays não recebem uma chave irmã _translated, porque os itens de array não têm chave à qual acrescentar uma. No nosso teste, "menu": ["Open", "Save", "Export as EPUB"] voltou em inglês no ficheiro bilingue, mas traduzido no ficheiro traduzido. Se fizer a revisão a partir do ficheiro bilingue, verifique os arrays no ficheiro traduzido.

Ficheiros com comentários

Muitos projetos mantêm comentários nos seus ficheiros de idioma (JSONC), o que o JSON estrito não permite. O tradutor tenta primeiro JSON estrito. Se falhar, remove os comentários e as vírgulas finais e tenta novamente. Remover comentários significa apagar tudo o que vem depois de // numa linha, e isso inclui o // em https://.

O nosso ficheiro com um comentário e um URL não carregou de todo; a análise falhou no primeiro comentário. Um ficheiro com comentários mas sem // dentro de qualquer valor carregou bem, e os comentários foram removidos do resultado.

Portanto: se o seu ficheiro tiver comentários, remova-os antes de carregar, ou guarde uma cópia com os comentários e aceite que a tradução não os terá.

Passo 2: Preparar o ficheiro

Pode corrigir a maioria destes problemas antes de traduzir:

  • Torne-o JSON estrito. Remova comentários e vírgulas finais. Se a sua ferramenta de compilação lê JSONC, mantenha o ficheiro comentado como origem e exporte uma cópia limpa para tradução.
  • Use um único estilo de chaves. Aninhe as chaves ou aplane-as com pontos, não ambas no mesmo ficheiro. Se usar i18next, a sua opção keySeparator controla como os pontos nas chaves são lidos, por isso verifique essa definição antes de aplanar o que quer que seja.
  • Mova os dados que não são texto para fora. IDs, URLs, sinalizadores de funcionalidades e limites pertencem à configuração, não ao ficheiro de idioma. Se um URL tiver de ficar, mantenha-o, mas conte com a sua verificação após a tradução. O nosso guia do que não traduzir explica como decidir o que mais deve ficar no idioma original.
  • Escreva os marcadores de posição da mesma forma em todo o lado. Um único estilo, como {name}, é mais fácil de verificar do que uma mistura de {name}, {{name}} e %s.

Passo 3: Traduzir o ficheiro completo

Carregue o ficheiro limpo no tradutor de documentos do BookTranslator, que lista .json juntamente com os outros formatos de localização e texto estruturado que aceita. O preço é mostrado antes do pagamento. Quando a tarefa terminar, a sua página de histórico oferece o ficheiro traduzido e o ficheiro bilingue como transferências separadas.

Duas coisas sobre a forma como o texto é traduzido afetam o resultado:

  • O modelo não vê as suas chaves. Cada valor é enviado como um segmento numerado, sem o nome da chave. Se "button.open" e "status.open" disserem ambos "Open", o modelo não tem como saber que um é um verbo e o outro um adjetivo. As cadeias curtas e ambíguas são as que deve verificar manualmente.
  • Cada valor é traduzido isoladamente. Nada liga duas chaves entre si, pelo que a mesma palavra em dois locais pode voltar de formas diferentes, e uma palavra que precisa de duas traduções diferentes pode voltar igual. O glossário automático opcional do BookTranslator recolhe nomes, locais e termos especializados e mantém-nos consistentes, mas não lhe pode dar traduções fixas suas. Para termos que têm de ser traduzidos de uma forma exata, mantenha uma lista de termos e verifique essas cadeias após a tradução; o nosso guia de glossários explica como planear um.

As etiquetas HTML inline são contadas após a tradução, e um segmento cujas etiquetas não correspondam é repetido. Os marcadores de posição com chavetas, os marcadores de posição no estilo %s e a sintaxe ICU são enviados como parte do texto, mas não são verificados da mesma forma. Execute o Passo 4 em todas as traduções.

Se preferir traduzir com um modelo de chat ou um modelo local, envie apenas os valores, nunca o ficheiro em bruto, e volte a colocá-los por chave com um script. As verificações abaixo aplicam-se de qualquer forma.

Passo 4: Verificar o ficheiro traduzido

Compare a tradução com a origem:

python3 check_i18n_json.py en.json fr.json

Para mostrar o que deteta, criámos um ficheiro francês com erros plantados. É feito à mão, não é resultado de um modelo. Após os mesmos avisos de origem do Passo 1, o verificador imprimiu:

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

Cada um destes apareceria como um erro na aplicação: um nome vazio, uma ligação quebrada, uma falha ou uma mensagem em bruto quando o plural é renderizado, uma contagem de capítulos em falta, um limite comparado como texto. A linha INFO lista cadeias que voltaram inalteradas. Algumas estão bem, como nomes de marcas; "Save" num menu francês não está.

O verificador compara chaves, tipos de valores, valores não textuais, marcadores de posição ({name}, {{name}}, %s, %1$s, %@), nomes de argumentos ICU, etiquetas HTML e URLs. Não avalia a tradução em si.

Os casos de plural devem mudar

Um plural ICU lista uma mensagem por categoria de plural. O inglês tem duas categorias, one e other, mas muitas línguas têm um conjunto diferente. Uma boa tradução russa da nossa mensagem items usaria normalmente one, few, many e other, e uma japonesa apenas other. Portanto, uma discrepância aqui nem sempre é um erro.

As categorias provêm das regras de plural CLDR da Unicode, e other é a que todas as línguas têm. Estas são as categorias cardinais reportadas por Intl.PluralRules no Node.js com CLDR 48:

LínguaCategorias de plural
Inglês, alemãoone, other
Francês, espanhol, portuguêsone, many, other
Russo, polacoone, few, many, other
Árabezero, one, two, few, many, other
Japonês, chinês, coreanoother

O francês, o espanhol e o português usam many apenas para números redondos muito grandes, como um milhão, pelo que uma mensagem para contagens pequenas normalmente pode dispensá-lo. O verificador reporta um other em falta como erro e qualquer outra alteração nas categorias como aviso, para comparar com esta tabela. Pode imprimir a lista para qualquer idioma:

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

O tradutor recebe os casos em inglês, por isso verifique o que foi devolvido. Se uma mensagem em russo ou árabe ainda tiver apenas one e other, acrescente os casos em falta manualmente ou peça a um tradutor que o faça.

Depois, carregue-o na aplicação

O verificador prova que o ficheiro é seguro para carregar, não que se lê bem. Mude a aplicação para o novo idioma e observe os ecrãs: palavras longas em alemão ou finlandês que transbordam de um botão, texto da direita para a esquerda em árabe ou hebraico, e datas ou números que o seu código formata separadamente do ficheiro de idioma. O nosso guia sobre tradução versus localização aborda o que mais muda entre mercados além das palavras.

Problemas Comuns

O ficheiro traduzido não carrega na minha aplicação. Execute o verificador sobre ele. Se o verificador não conseguir analisá-lo, o ficheiro foi editado após a tradução ou não era válido desde o início. Se for analisado, procure linhas ERROR sobre chaves ou tipos.

Uma string aparece como uma mensagem ICU em bruto, como {count, plural, ...}. A sintaxe ICU quebrou na tradução, normalmente uma chaveta em falta ou um caso other em falta. Copie a estrutura do original e traduza apenas as palavras dentro de cada caso.

O meu ficheiro é YAML, um ficheiro .po ou Markdown. O BookTranslator também aceita esses formatos, mas cada formato tem as suas próprias regras. Para documentação, consulte o nosso guia de tradução de Markdown; para páginas web, o guia de ficheiros HTML.

Posso traduzir apenas as chaves novas? Não dentro de um único carregamento; o ficheiro inteiro é traduzido. Coloque as chaves novas num ficheiro separado, traduza-o e combine o resultado por chave. O verificador confirmará que o ficheiro combinado tem todas as chaves.

Devo traduzir as chaves? Não. As chaves são identificadores que o seu código procura. Apenas os valores são texto para as pessoas lerem.

Publicações relacionadas