Pular para o conteúdo
Development

Ensinando um app a falar em um idioma que ele ainda não conhece

Por Victor Da Luz•
sveltei18nrustdev-loggreenhouse

O Greenhouse tem um problema de i18n parado no backlog há um tempo: eventualmente adicionar espanhol e português, já que consigo revisar os dois idiomas sozinho. Um spike no começo de julho mapeou o escopo, encontrou algo em torno de 170 strings traduzíveis, e recomendou dividir o trabalho em duas partes: primeiro corrigir um problema arquitetural em como os nomes de estágio são armazenados, depois fazer a passada real de tradução mais tarde, quando o app for algo além de um build local que só eu rodo. Essa semana retomei a parte da tradução, e acabou sendo uma boa lição sobre delimitar um trabalho a algo que você de fato consegue terminar em uma única sentada.

A contagem de strings não parava de subir

Antes de tocar em qualquer código, refiz o inventário da superfície traduzível do app, já que o spike original tinha algumas semanas e muita coisa tinha sido lançada desde então. O número foi de aproximadamente 170 strings para aproximadamente 230: quatro telas novas (uma view de colheita, um quadro kanban, uma tela de recuperação de vault danificado, um diálogo de regras) que ainda não existiam quando o spike rodou. Esse tipo de deriva é exatamente o motivo pelo qual não confio em uma estimativa antiga sem checar de novo.

A questão maior de escopo era o que “adicionar espanhol e português” realmente significa como uma unidade de trabalho. Extrair cada string hardcoded para um sistema de tradução é mecânico e totalmente checável por máquina: o build passa, os testes continuam passando, nada muda visualmente. Escrever o espanhol e o português de verdade não é mecânico. Palavras como “Budding” e “Leafing” não têm equivalentes limpos em nenhum dos dois idiomas, e quero revisar essas escolhas de palavra com cuidado, não carimbar um rascunho de máquina.

Então dividi em três partes em vez de fazer tudo em um único issue: extração e ferramental agora, com inglês como único idioma lançado; as mensagens de erro do lado Rust depois, já que são construídas como strings pré-formatadas lá no fundo do backend e precisam de uma mudança de arquitetura de verdade, não só um arquivo de tradução; e o conteúdo real em espanhol e português mais tarde ainda, como seu próprio issue onde posso sentar com as palavras e levá-las a sério.

Paralelizando a parte chata

Uma vez delimitado a “extrair cada string, manter o inglês exatamente igual”, o trabalho era mecânico mas espalhado por dezenove arquivos. Dividi em uma tarefa por arquivo e rodei em paralelo, cada uma com as mesmas instruções: encontrar cada string voltada para o usuário, dar um nome a ela, substituir por uma chamada de função, não tocar no texto renderizado, e nunca usar {@html} mesmo que isso deixasse uma frase mais fácil de montar, já que alguns desses diálogos renderizam nomes de projeto e notas digitadas pelo usuário, e jogar um template literal dentro de HTML bruto é exatamente o tipo de atalho que vira um bug de injeção de verdade depois.

Essa restrição importou mais do que eu esperava. Um diálogo registra um toque com uma frase como “Logs a touch on your project name and sinks it to the bottom of your worklist”, com o nome do projeto em negrito. O atalho tentador é uma string só com o negrito embutido via HTML bruto. Em vez disso, a tag de negrito continua como marcação de verdade no template, e a frase ao redor dela se divide em uma parte “antes” e uma parte “depois”, cada uma sua própria string traduzível. Um pouco mais trabalhoso de escrever, consideravelmente mais seguro de lançar.

Uma falha silenciosa que parecia sucesso

A parte de verdade irritante veio durante a verificação, depois que os dezenove arquivos estavam prontos e mergeados. O build passou. Sem erros. E, ainda assim, toda única string traduzida no app compilado estava indefinida.

Descobri que minha configuração de projeto escrita à mão estava sem uma configuração que o plugin de tradução precisa para saber onde encontrar os arquivos de tradução. Sem ela, o plugin não dá erro, ele simplesmente decide silenciosamente que não há mensagens para compilar. Um build limpo com zero conteúdo traduzido parece exatamente igual a um build limpo com conteúdo totalmente traduzido, até você realmente ler a saída compilada. Só percebi porque fui olhar o arquivo gerado diretamente em vez de confiar no check verde. Uma vez corrigido, um segundo problema apareceu na hora: a sintaxe de plural que eu tinha usado para “5 items” versus “1 item” não era a sintaxe que esse plugin específico realmente espera, ele queria sua própria forma declarativa em vez do atalho padrão. Essa pelo menos teve a decência de falhar alto e claro.

Lição

Um build passar não é a mesma coisa que um build fazer o que você acha que ele está fazendo. “Zero erros” só diz que nada quebrou, não que algo funcionou, e as duas afirmações estão mais distantes uma da outra do que parecem no momento. Também: quando um trabalho contém naturalmente uma parte que você pode verificar por máquina e uma parte que só pode verificar por julgamento, isso geralmente é sinal de que na verdade são dois issues usando um único rótulo.

Leitura relacionada

Você também pode achar útil

AdGuard

AdGuard para iOS

Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.

Como afiliado da AdGuard, ganho com compras qualificadas.

Saiba mais
NordPass

NordPass

Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.

Como afiliado da NordPass, ganho com compras qualificadas.

Saiba mais
Airalo

eSIM Airalo

eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.

Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.

Saiba mais