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.

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 ficheiro | Exemplo do nosso teste | O que o BookTranslator faz | O que verificar |
|---|---|---|---|
| Chaves | "greeting", "error" | Mantidas como estão | Nada |
| Valores de texto simples | "Reading list" | Traduzidos | Significado, comprimento no ecrã |
| Valores com placeholders | "Welcome back, {name}!", "Saved %s of %d chapters" | Traduzidos, placeholders incluídos no texto enviado | Nomes e contagens de placeholders (o verificador faz isto) |
| Mensagens de plural ICU | "{count, plural, one {# book} other {# books}}" | Enviadas como uma única string | O 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ção | Nomes e contagens de etiquetas (o verificador faz isto) |
| URLs armazenados como texto | "https://example.com/help" | Enviados para tradução | Que voltaram inalterados |
| Valores sem letras | "2.4.1", "—", "" | Deixados como estavam | Nada |
Números, true/false, null | 20, true | Deixados como estavam | IDs muito grandes (ver abaixo) |
| Arrays de strings | ["Open", "Save", "Export as EPUB"] | Traduzidos no ficheiro traduzido; não traduzidos no ficheiro bilingue | O ficheiro bilingue, se o utilizar |
| Comentários (JSONC) | // Shown on the home screen | Removidos. Se um valor também contiver //, o ficheiro pode não carregar | Remova 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:
- 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. - 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. - Um ID grande perdeu precisão.
9007199254740993voltou como9007199254740992. 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 sobreNumber.MAX_SAFE_INTEGER). Armazene IDs longos como strings ou mantenha-os fora do ficheiro de idioma. - 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
keySeparatorcontrola 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íngua | Categorias de plural |
|---|---|
| Inglês, alemão | one, other |
| Francês, espanhol, português | one, many, other |
| Russo, polaco | one, few, many, other |
| Árabe | zero, one, two, few, many, other |
| Japonês, chinês, coreano | other |
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





