Como traduzir um arquivo de idioma JSON sem quebrar chaves ou placeholders
Veja quais valores em um arquivo en.json são traduzidos, o que um tradutor de arquivos reescreve (comentários, ordem das chaves, IDs grandes) e como verificar placeholders, plurais ICU e chaves com um script gratuito e um arquivo de teste.

Para traduzir um arquivo de idioma JSON como en.json, entregue o arquivo inteiro a um tradutor que entenda JSON. Colar o texto bruto em uma janela de chat ou em um tradutor web é a maneira arriscada de fazer isso. Um tradutor que entende JSON lê o arquivo, traduz apenas os valores de texto e grava as mesmas chaves de volta, para que seu aplicativo ainda possa carregar o arquivo. Copiar e colar pode traduzir uma chave, alterar uma aspa ou remover uma chave. Qualquer uma dessas coisas quebra o arquivo.
Manter as chaves é a parte fácil. A maior parte do problema vem depois: um placeholder {name} que volta como {nom}, um plural ICU que perde o caso other, uma URL enviada para tradução como qualquer outro texto e algumas alterações que o tradutor faz no próprio arquivo. É isso que este guia aborda.
Criamos um arquivo de teste com todos esses casos e escrevemos um verificador gratuito que compara um arquivo de origem com sua tradução. Também executamos o arquivo de teste em uma cópia local do código de análise e gravação de JSON que o BookTranslator usa, com um marcador no lugar de um modelo, para que você veja exatamente quais valores são enviados e como o arquivo retorna.
Resposta rápida: o que acontece com cada parte do arquivo
| Parte do arquivo | 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 na tela |
| 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 isso) |
| 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 embutido | "accept the <b>Terms</b>." | Traduzido; a contagem de tags é verificada após a tradução | Nomes e contagens de tags (o verificador faz isso) |
| URLs armazenadas como texto | "https://example.com/help" | Enviadas para tradução | Que voltaram inalteradas |
| Valores sem letras | "2.4.1", "—", "" | Mantidos como estavam | Nada |
Números, true/false, null | 20, true | Mantidos como estavam | IDs muito grandes (veja abaixo) |
| Arrays de strings | ["Open", "Save", "Export as EPUB"] | Traduzidos no arquivo traduzido; não traduzidos no arquivo bilíngue | O arquivo bilíngue, se você o usar |
| Comentários (JSONC) | // Shown on the home screen | Removidos. Se um valor também contiver //, o arquivo pode falhar ao carregar | Remova os comentários antes do upload |
O restante deste guia mostra de onde vem cada linha e o que fazer a respeito.
Etapa 1: Verifique o arquivo de origem antes do upload
Execute o verificador no arquivo que você planeja traduzir. Ele precisa apenas de Python 3 e nada mais:
python3 check_i18n_json.py en.json
No nosso arquivo de teste, ele 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 observação sobre chaves semelhantes a inteiros correspondem cada um a uma alteração que vimos quando nosso arquivo de teste passou pelo tradutor. Se o seu arquivo não apresentar nenhum deles, você pode pular para a Etapa 3.
O verificador também rejeita um arquivo que não seja JSON estrito, como um com comentários ou vírgulas finais, porque esse é o próximo problema.
O que voltou do nosso arquivo de teste
O teste foi executado em 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: qualquer coisa com [ZH] na frente foi enviada, qualquer coisa sem ele não foi. Ambas as saídas estão publicadas: o arquivo traduzido e o arquivo bilíngue.
O arquivo traduzido
Todas as chaves voltaram inalteradas e o arquivo ainda pôde ser analisado. Valores com letras foram enviados, incluindo {name}, %s, a mensagem ICU, a tag <b> e a URL de ajuda. A string de versão "2.4.1", o travessão e a string vazia não foram enviados, pois não contêm letras. Números e booleanos foram deixados como estavam.
Outras quatro coisas mudaram:
- Uma string foi para a chave errada. No nosso teste, nosso arquivo 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, então ambas as strings tinham o mesmo endereço,error.network. Na saída, ambas as chaves continham a tradução de "Network error". "Check your connection." desapareceu. Isso exige os dois estilos no mesmo arquivo, o que é raro, mas falha silenciosamente. O verificador avisa sobre isso antes do upload. - Chaves semelhantes a inteiros foram reordenadas.
"steps": { "10": ..., "2": ..., "1": ... }voltou como"1","2","10". O JavaScript sempre lista chaves semelhantes a inteiros primeiro, em ordem numérica, quando reconstrói um objeto. As chaves e os valores ainda correspondem, então seu app está bem, mas um diff contra o arquivo de origem ficará ruidoso. - Um ID grande perdeu precisão.
9007199254740993voltou como9007199254740992. O JavaScript armazena números como ponto flutuante de 64 bits, e inteiros acima de 2^53 não podem ser todos representados exatamente (MDN sobreNumber.MAX_SAFE_INTEGER). Armazene IDs longos como strings ou mantenha-os fora do arquivo de idioma. - A formatação foi reescrita. A saída usa indentação de dois espaços e termina com uma nova linha. Se sua origem usava tabs ou quatro espaços, espere mudanças de espaço em branco no diff.
Nosso arquivo 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, então o texto anterior nunca é traduzido. Seu app carregaria o mesmo último valor, então nada visível quebra, mas o verificador lista chaves duplicadas para que você possa limpá-las.
O arquivo bilíngue
O arquivo bilíngue mantém cada valor original e adiciona uma chave irmã com _translated no final:
"greeting": "Welcome back, {name}!",
"greeting_translated": "[ZH] Welcome back, {name}!",
Isso funciona bem para revisão, pois a origem e a tradução ficam lado a lado. Não é um arquivo que seu app possa carregar, pois agora toda chave tem uma gêmea. Use o arquivo traduzido no seu app.
Uma lacuna: strings dentro de arrays não recebem uma chave irmã _translated, pois itens de array não têm chave para adicionar uma. No nosso teste, "menu": ["Open", "Save", "Export as EPUB"] voltou em inglês no arquivo bilíngue, mas traduzido no arquivo traduzido. Se você revisar a partir do arquivo bilíngue, verifique os arrays no arquivo traduzido.
Arquivos com comentários
Muitos projetos mantêm comentários em seus arquivos de idioma (JSONC), o que o JSON estrito não permite. O tradutor primeiro tenta JSON estrito. Se isso falhar, ele remove comentários e vírgulas finais e tenta novamente. Remover comentários significa apagar tudo após // em uma linha, e isso inclui o // em https://.
Nosso arquivo com um comentário e uma URL não carregou de jeito nenhum; a análise falhou no primeiro comentário. Um arquivo com comentários mas sem // dentro de qualquer valor carregou bem, e os comentários foram removidos da saída.
Então: se seu arquivo tiver comentários, remova-os antes do upload, ou mantenha uma cópia com os comentários e aceite que a tradução não os terá.
Passo 2: Prepare o arquivo
Você pode corrigir a maioria desses problemas antes de traduzir:
- Torne-o JSON estrito. Remova comentários e vírgulas finais. Se sua ferramenta de build lê JSONC, mantenha o arquivo comentado como sua origem e exporte uma cópia limpa para tradução.
- Use um único estilo de chave. Aninhe chaves ou aplane-as com pontos, não ambos no mesmo arquivo. Se você usa i18next, sua opção
keySeparatorcontrola como os pontos nas chaves são lidos, então verifique essa configuração antes de achatar qualquer coisa. - Mova dados não textuais para fora. IDs, URLs, feature flags e limites pertencem à configuração, não ao arquivo de idioma. Se uma URL precisar ficar, mantenha-a, mas espere verificá-la após a tradução. Nosso guia do que não traduzir cobre como decidir o que mais deve permanecer no idioma original.
- Escreva placeholders da mesma forma em todos os lugares. Um estilo, como
{name}, é mais fácil de verificar do que uma mistura de{name},{{name}}e%s.
Passo 3: Traduza o arquivo inteiro
Faça upload do arquivo limpo para o tradutor de documentos do BookTranslator, que lista .json com os outros formatos de localização e texto estruturado que aceita. O preço é mostrado antes do checkout. Quando a tarefa terminar, sua página de histórico oferece o arquivo traduzido e o arquivo bilíngue como downloads separados.
Duas coisas sobre como o texto é traduzido afetam o resultado:
- O modelo não vê suas chaves. Cada valor é enviado como um segmento numerado, sem o nome da chave. Se
"button.open"e"status.open"ambos dizem "Open", o modelo não tem como saber que um é verbo e o outro é adjetivo. Strings curtas e ambíguas são as que devem ser verificadas manualmente. - Cada valor é traduzido isoladamente. Nada conecta duas chaves, então a mesma palavra em dois lugares pode voltar de duas formas diferentes, e uma palavra que precisa de duas traduções diferentes pode voltar igual. O glossário automático opcional do BookTranslator coleta nomes, lugares e termos especializados e os mantém consistentes, mas você não pode fornecer traduções fixas próprias. Para termos que devem ser traduzidos de uma forma exata, mantenha uma lista de termos e verifique essas strings após a tradução; nosso guia de glossário explica como planejar um.
Tags HTML inline são contadas após a tradução, e um segmento cujas tags não correspondem é tentado novamente. Placeholders com chaves, placeholders no estilo %s e sintaxe ICU são enviados como parte do texto, mas não são verificados da mesma forma. Execute a Etapa 4 em toda tradução.
Se você preferir traduzir com um modelo de chat ou um modelo local, envie apenas os valores, nunca o arquivo bruto, e recoloque-os por chave com um script. As verificações abaixo se aplicam de qualquer forma.
Etapa 4: Verifique o arquivo traduzido
Compare a tradução com o original:
python3 check_i18n_json.py en.json fr.json
Para mostrar o que ele detecta, criamos um arquivo em francês com erros plantados. É feito à mão, não é saída de modelo. Após os mesmos avisos de origem da Etapa 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 desses apareceria como um bug no aplicativo: um nome vazio, um link quebrado, um crash ou uma mensagem bruta quando o plural é renderizado, uma contagem de capítulos ausente, um limite comparado como texto. A linha INFO lista strings que voltaram inalteradas. Algumas estão corretas, como nomes de marcas; "Save" em um menu francês não está.
O verificador compara chaves, tipos de valor, valores não textuais, placeholders ({name}, {{name}}, %s, %1$s, %@), nomes de argumentos ICU, tags HTML e URLs. Ele não julga a tradução em si.
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 para o russo da nossa mensagem items normalmente usaria one, few, many e other, e uma para o japonês apenas other. Portanto, uma incompatibilidade aqui nem sempre é um erro.
As categorias vêm das regras de plural do Unicode CLDR, e other é a que toda língua tem. Estas são as categorias cardinais reportadas por Intl.PluralRules no Node.js com CLDR 48:
| Idioma | Categorias de plural |
|---|---|
| Inglês, alemão | one, other |
| Francês, espanhol, português | one, many, other |
| Russo, polonês | one, few, many, other |
| Árabe | zero, one, two, few, many, other |
| Japonês, chinês, coreano | other |
Francês, espanhol e português usam many apenas para números redondos muito grandes, como um milhão, então uma mensagem para contagens pequenas geralmente pode dispensá-lo. O verificador reporta um other ausente como erro, e qualquer outra mudança nas categorias como um aviso para comparar com esta tabela. Você mesmo 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, então verifique o que voltou. Se uma mensagem em russo ou árabe ainda tiver apenas one e other, adicione os casos ausentes manualmente, ou peça a um tradutor para fazê-lo.
Depois, carregue-o no app
O verificador prova que o arquivo é seguro para carregar, não que ele lê bem. Mude o app para o novo idioma e observe as telas: palavras longas em alemão ou finlandês que transbordam um botão, texto da direita para a esquerda em árabe ou hebraico, e datas ou números que seu código formata separadamente do arquivo de idioma. Nosso guia de tradução versus localização cobre o que mais muda entre mercados além das palavras.
Problemas Comuns
O arquivo traduzido não carrega no meu app. Execute o verificador nele. Se o verificador não conseguir analisá-lo, o arquivo foi editado após a tradução ou não era válido desde o início. Se ele for analisado, procure linhas de ERROR sobre chaves ou tipos.
Uma string aparece como uma mensagem ICU bruta, como {count, plural, ...}. A sintaxe ICU quebrou na tradução, geralmente uma chave ausente ou um caso other ausente. Copie a estrutura do original e traduza apenas as palavras dentro de cada caso.
Meu arquivo é YAML, um arquivo .po ou Markdown. O BookTranslator também aceita esses, mas cada formato tem suas próprias regras. Para documentação, veja nosso guia de tradução de Markdown; para páginas web, o guia de arquivos HTML.
Posso traduzir apenas as novas chaves? Não dentro de um único upload; o arquivo inteiro é traduzido. Coloque as novas chaves em um arquivo separado, traduza-o e mescle o resultado por chave. O verificador confirmará que o arquivo mesclado tem todas as chaves.
Devo traduzir as chaves? Não. As chaves são identificadores que seu código procura. Apenas os valores são texto para as pessoas lerem.
Posts Relacionados





