Ir para o conteúdo

Blogue ·

Dezasseis mil chaves, nove idiomas

Traduzir um painel de controlo não é um problema de texto. As falhas que interessam são silenciosas: um argumento perdido, um plural achatado, uma guarda vazia.

Por Provibr

O nosso catálogo de mensagens em inglês tem neste momento pouco mais de dezasseis mil cadeias de texto. Há nove catálogos, um por idioma, e são obrigados a ter exatamente as mesmas chaves. Isso dá algures acima de cento e quarenta mil pedaços de texto que têm todos de continuar fiéis uns aos outros enquanto uma dúzia de pessoas muda o produto por baixo deles.

O que é interessante nesse problema não é o volume. É que quase nenhuma das falhas se anuncia. Uma tradução com uma gralha é comunicada por um cliente dentro de uma semana. Uma tradução que deixou cair um argumento em silêncio produz uma frase com um buraco, para sempre, e ninguém abre um pedido de suporte por causa de uma frase que se lê bem.

Este é o conjunto de guardas a que chegámos, e o raciocínio por trás de cada uma. Não são específicas de nenhuma tecnologia em particular; os modos de falha são uma propriedade de haver mais do que um idioma.

A classe de defeito que interessa

Comecemos por aquele que nos fez levar isto a sério.

Uma cadeia em inglês diz Welcome back, {name}. Alguém a traduz, e a tradução é Welcome back. É gramatical, é uma frase razoável, e é composta sem erro em qualquer sistema de modelos que exista. O parâmetro simplesmente não aparece. Não há exceção, não há linha de registo, não há aviso de chave em falta.

Esse defeito é invisível para todos os testes que verificam se as chaves existem, que é o teste que quase toda a gente escreve primeiro. A única coisa que o encontra é uma comparação: extrair o conjunto de nomes de marcadores da cadeia de referência, extraí-los da tradução, e exigir que coincidam.

Depois de construída essa comparação, três afinações revelam-se necessárias, e cada uma delas veio de uma cadeia real que escapou.

Compare os tipos dos argumentos, não apenas os nomes. Um {count} nu não é formatado. Escreve o número em bruto, sem separador de milhares, em qualquer idioma. Escreva-se {count, number} e passa a ser formatado. Se o tipo estiver presente em inglês e ausente na tradução, o nome coincide, a estrutura dos ramos coincide, e um idioma compõe discretamente um inteiro em bruto onde os outros compõem um número formatado. O mesmo vale para as datas: um argumento de data que perdeu o tipo atira uma data ISO à cara de quem lê.

Não compare os estilos dos argumentos. Estilo de moeda contra estilo de número simples é uma escolha de apresentação que legitimamente difere por idioma, e uma guarda que a assinala ensina as pessoas a ignorar a guarda. O tipo é estrutural, o estilo é gosto, e só um dos dois pertence a um teste.

Compare os nomes dos ramos, não o texto dos ramos. Um plural que o tradutor achatou mantém o seu argumento, pelo que uma comparação de marcadores o deixa passar alegremente, enquanto o ecrã acaba a mostrar um número nu sem substantivo a seguir. Tínhamos exatamente isso no nosso catálogo neerlandês, e foi assim que foi encontrado.

Os plurais não são um problema resolvido que se possa copiar

O instinto é exigir que uma tradução tenha os mesmos ramos de plural que o inglês. O inglês tem dois: one e other. Esse requisito está certo para os idiomas que também têm dois, e está precisamente errado para os que não têm, porque aceita de bom grado um catálogo a que faltam categorias de que a sua própria língua precisa.

Algumas medições, todas elas a partir das regras padrão que acompanham qualquer motor moderno:

IdiomaO que surpreende as pessoas
FrancêsO zero fica no ramo singular. «0 machine», não «0 machines».
UcranianoQuatro categorias. O zero cai em many, juntamente com 5 a 20; 21 é one e 22 é few.
Romeno2 a 19 é few, 20 é other, e 101 volta a ser few.
ÁrabeSeis categorias, com cortes em 0, 1, 2, 3 a 10, e 11 a 99.

A linha do romeno tem uma consequência que aparece como volume puro. O romeno põe uma preposição antes do substantivo acima de dezanove, pelo que uma frase que em inglês não precisa de plural nenhum precisa de um em romeno. Contando as mensagens dos nossos catálogos que contêm um plural: o inglês tem 319, o romeno tem 498. 179 mensagens precisam em romeno de uma construção de plural de que o inglês não precisa de todo. O ucraniano acrescenta 13 das suas, o francês 4.

Eis uma delas, e é inglês completamente comum:

text
en: Start {days}-day trial
uk: Почати пробний період на {days, plural, one {# день} few {# дні} many {# днів} other {# дня}}

Nada na frase inglesa sugere que seja um problema de plural. Uma guarda que deriva as suas expectativas do inglês não tem como ver isto.

Por isso a segunda guarda corre por idioma contra o catálogo desse idioma: para cada plural nele, estão tratadas todas as categorias que esta língua consegue de facto produzir, seja pelo nome, seja por um ramo de valor exato?

Essa guarda precisa de um limite deliberado, ou torna-se inútil. O francês e o espanhol têm uma categoria many que se aplica apenas a múltiplos exatos de um milhão. Exigi-la em todo o lado acrescentaria centenas de ramos que nenhuma pessoa alguma vez verá, num produto que conta servidores, endereços, membros de equipa e dias. Por isso a verificação é guiada por amostras: pergunta que categorias são alcançáveis para as contagens que uma chave realmente vê, e existe uma pequena lista explícita de chaves que contam mais alto, para que a maquinaria esteja pronta no dia em que alguma coisa conte.

Essa lista está neste momento vazia, e isso é uma medição e não um descuido. Os dois candidatos que parecem poder crescer muito contam bytes, e a regra francesa quer um múltiplo exato de um milhão; um ficheiro de exatamente 1 000 000 bytes é uma coincidência, não um caso.

A guarda que não estava a fazer nada, em silêncio

Agora a desconfortável, porque é a falha de que trata este artigo inteiro, a acontecer às próprias ferramentas.

Temos uma verificação que garante que todas as chamadas de tradução no código se referem a uma chave que existe. Interpreta o código-fonte, segue que objeto tradutor pertence a que espaço de nomes, e procura cada chave. Comunicava que cobria vários milhares de chaves. Estava verde.

Estava também a saltar todas as chamadas que vinham de um padrão de desestruturação, porque o código que ligava um tradutor a um espaço de nomes só tratava um identificador simples e ignorava em silêncio tudo o resto. Sem ligação, logo sem chave para verificar, logo sem falha. O padrão que saltava calhava de ser o que é usado em quase toda a nossa aplicação.

Encontrámo-lo porque alguém adivinhou o nome de uma chave, escreveu-o, e o teste continuou verde.

Daí saíram duas regras, e aplicamo-las agora a todas as análises estáticas que escrevemos.

Um analisador que encontra uma forma que não compreende tem de falhar alto, não de saltar em silêncio. Não reconhecido é um achado, não uma operação nula. Todo o valor da ferramenta está em ter olhado para tudo.

Prove a guarda vermelha antes de confiar nela verde. Estrague alguma coisa de propósito e veja o teste falhar. Uma verificação que nunca falhou não foi testada; foi apenas corrida.

Um parente próximo dessa lição: um nome de marca aparecia nos nossos catálogos escrito de quatro maneiras diferentes, e o único sítio de todo o código que sabia a grafia correta sabia-a como uma exceção numa lista de permissões. O único componente que tinha a resposta certa estava configurado para desviar o olhar dela. Uma exceção não é uma guarda, e a correção passa normalmente por inverter a coisa: enuncie a regra, e deixe que as exceções sejam dados pendurados nela.

Interpretar o seu próprio formato de mensagens é mais difícil do que parece

A extração de marcadores com uma expressão regular funciona até deixar de funcionar, e a maneira como falha é instrutiva porque falha nas suas melhores cadeias.

Um ramo de seleção cujo texto é uma única palavra é, para um comparador de padrões, exatamente igual a um argumento. O nosso antigo analisador dava alarmes falsos nas mensagens escritas com mais cuidado em todo o catálogo e, ao mesmo tempo, deixava escapar argumentos verdadeiros dentro dos ramos, porque uma expressão regular não sabe contar chavetas. Estava errado nos dois sentidos ao mesmo tempo.

O substituto percorre as chavetas como deve ser e desce recursivamente aos corpos dos ramos. Tem trinta linhas em vez de uma, e é a diferença entre uma guarda em que as pessoas confiam e uma guarda à volta da qual as pessoas escrevem anotações.

A tipografia é uma guarda por idioma, não uma guarda partilhada

A última categoria não pode ser verificada por comparação nenhuma, porque é exatamente aquilo que difere entre idiomas. O francês quer um espaço inquebrável antes dos dois pontos, do ponto e vírgula, do ponto de exclamação e do ponto de interrogação. O ucraniano quer um carácter de apóstrofo específico, parecido com os outros dois que provavelmente existem no seu teclado. O espanhol abre uma pergunta com um sinal invertido, no ponto em que a pergunta começa e não no início da frase. O romeno usa as suas próprias aspas e diacríticos com vírgula por baixo.

Nada disso é comparável entre idiomas, pelo que cada idioma que tenha regras recebe a sua própria verificação. Isso parece duplicação e não é: a alternativa é uma única guarda genérica que só consegue afirmar a interseção de nove conjuntos de regras, o que na prática é nada.

Um aviso de quem fez isto num repositório grande: um espaço inquebrável é invisível, e várias ferramentas fazem o favor de o converter num espaço normal pelo caminho. Pomo-los lá com um script exatamente por essa razão, e verificamos depois que continuam lá.

O que construiríamos primeiro, por ordem

Para quem parte de um só idioma e conta acrescentar mais, esta é a ordem que mais nos teria poupado.

  1. As mesmas chaves em todos os catálogos, verificado na integração contínua. É barato, e é a fundação sobre a qual o resto assenta.
  2. Comparação de marcadores e de tipos de argumento contra o idioma de referência. É esta que apanha os defeitos silenciosos.
  3. Comparação de nomes de ramos para plurais e seleções, para que um plural achatado seja uma falha.
  4. Cobertura de plurais por idioma, guiada pelas regras da própria língua, antes de chegar o primeiro idioma que precise de mais categorias do que a sua referência.
  5. Tipografia por idioma, uma pequena verificação por cada idioma que tenha convenções.
  6. Uma verificação estática de que todas as chaves referidas no código existem e, mais importante, que seja honesta quanto às chamadas que não conseguiu compreender.

O tema que atravessa as seis é que os defeitos de tradução não rebentam. Compõem-se. Todas as guardas aqui existem porque alguma coisa foi composta na perfeição, num idioma que ninguém na sala lê, e disse algo ligeiramente diferente do que queríamos dizer.

Comece com um host e um agente

Crie uma conta, instale o agente com um único comando e ligue o seu primeiro host Proxmox. O período experimental dura 14 dias e não pede quaisquer dados de pagamento.