Como Automatizar a Tradução de .po no seu Pipeline de CI/CD

Sua equipe faz merges para main quatorze vezes ao dia. Cada um desses merges pode adicionar uma nova string visível para o usuário – um rótulo de botão, uma mensagem de erro, uma dica de ferramenta. E cada uma dessas strings começa sua vida apenas em inglês. Em algum ponto do seu processo de lançamento, um humano precisa notar as novas strings, exportá-las, obter suas traduções, colá-las de volta e recompilar. Esse humano é um gargalo e, em uma equipe de entrega rápida, esse gargalo significa que seus usuários alemães veem placeholders em inglês por dois sprints.
A solução é automatizar a tradução no seu pipeline de CI/CD para que as novas strings sejam traduzidas no mesmo merge que as introduziu – sem humanos no caminho crítico para o caso rotineiro. Isso é totalmente alcançável hoje, mas apenas se você evitar a armadilha em que a maioria das equipes cai: chamadas LLM brutas feitas manualmente que silenciosamente corrompem seus placeholders %s e a estrutura de .po. Este guia detalha um padrão de pipeline de CI realista, um esqueleto funcional de GitHub Actions e as decisões de design – idempotência, tradução apenas de novos itens, portões de revisão – que separam um pipeline que ajuda de um que entrega builds quebrados.
Por Que a Tradução Manual é um Gargalo no Lançamento
A resposta para "por que não traduzir antes de cada lançamento" é que os prazos nunca se alinham. Strings são adicionadas continuamente por desenvolvedores, mas a tradução acontece em lotes por uma pessoa diferente em um cronograma diferente. A lacuna entre essas duas cadências é onde sua dívida de localização se acumula.
O Problema das Duas Cadências
Imagine o fluxo manual. Um desenvolvedor adiciona __( 'Export to CSV', 'mytextdomain' ) e faz o merge. Ninguém regenera o .pot. Duas semanas depois, alguém executa wp i18n make-pot, nota quarenta novas strings não traduzidas (algumas de desenvolvedores que já saíram de férias), tenta adivinhar a intenção de metade delas e envia um .po para um tradutor. A tradução volta, é colada de volta, e talvez os placeholders tenham sobrevivido, talvez não. Enquanto isso, três lançamentos foram enviados com essas strings em inglês.
Cada etapa é manual, em lote e propensa a erros. O objetivo da automação de CI/CD é transformar isso em algo que seja executado automaticamente no merge, traduzindo apenas o que realmente mudou e falhando ruidosamente quando algo parece errado – transformando a tradução de uma tarefa periódica em uma parte invisível do pipeline.
O Padrão do Pipeline: Extrair, Diferenciar, Traduzir, Fazer Commit
Em um alto nível, um trabalho de tradução automatizado executa quatro etapas no merge para main: regenerar o template, detectar o que é novo, traduzir apenas essas strings e fazer commit dos resultados. O processo todo deve ser idempotente – executá-lo em um merge sem alterações de strings deve produzir zero diff e zero ruído.
Extrair e Diferenciar
A primeira etapa é a extração. Regenere o .pot a partir do código-fonte atual para que ele reflita cada string na base de código:
wp i18n make-pot . languages/myplugin.pot
A segunda etapa é a diferenciação (diff). Esta é a decisão de design mais importante em todo o pipeline. Você não quer retraduzir cada string em cada merge – isso desperdiça chamadas de API, corre o risco de retraduzir strings que um humano já corrigiu e produz diffs de revisão enormes. Em vez disso, você mescla o .pot recém-gerado em cada .po existente e traduz apenas as entradas que agora estão vazias (as strings genuinamente novas):
# Merge new template into each locale, preserving existing translations.
# Newly-added strings appear with empty msgstr; --backup=none keeps the tree clean.
for po in languages/*.po; do
msgmerge --update --backup=none "$po" languages/myplugin.pot
done
Após o msgmerge, apenas strings novas terão um msgstr vazio. Tudo o que foi traduzido anteriormente permanece intocado. Essa propriedade é o que torna o pipeline idempotente e o que mantém seus diffs de revisão pequenos e revisáveis.
Traduzir e Fazer Commit
A terceira etapa envia apenas essas entradas vazias para uma etapa de tradução. A quarta etapa faz commit do .po atualizado, compila .mo e regenera qualquer JSON. Vamos integrar tudo isso ao GitHub Actions em seguida.
Um Esqueleto de GitHub Actions
Aqui está a resposta para "como isso realmente se parece em um arquivo de fluxo de trabalho": um job acionado em push para main que executa o ciclo de extrair-diferenciar-traduzir-fazer commit e abre um pull request com os resultados em vez de fazer commit diretamente para main.
O Arquivo de Workflow
name: Translate on merge
on:
push:
branches: [ main ]
jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install WP-CLI and gettext
run: |
sudo apt-get update && sudo apt-get install -y gettext
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp && sudo chmod +x /usr/local/bin/wp
- name: Regenerate template and merge into locales
run: |
wp i18n make-pot . languages/myplugin.pot
for po in languages/*.po; do
msgmerge --update --backup=none "$po" languages/myplugin.pot
done
- name: Translate only new (empty) strings
env:
TRANSLATE_API_KEY: ${{ secrets.TRANSLATE_API_KEY }}
run: ./scripts/translate-new-strings.sh languages/
- name: Compile .mo and JSON
run: |
wp i18n make-mo languages/
wp i18n make-json languages/ --no-purge
- name: Open pull request with translations
uses: peter-evans/create-pull-request@v6
with:
branch: chore/auto-translations
title: "chore: auto-translate new strings"
commit-message: "chore: auto-translate new strings"
Onde o Trabalho Real se Esconde
A etapa translate-new-strings.sh é onde a tradução real acontece. A versão ingênua desse script lê cada entrada vazia, a envia para uma API LLM uma string por vez e cola a resposta de volta. Essa versão ingênua é exatamente a armadilha, e vale a pena ser explícito sobre o porquê.
Por Que Chamadas LLM Feitas Manualmente Quebram Seus Arquivos
A resposta curta: chamadas LLM brutas tratam suas strings .po como prosa, e suas strings .po não são prosa – são dados estruturados com placeholders, formas plurais e contexto que uma chamada de chat-completion alegremente destrói.
A Corrupção de Placeholder Que Você Não Verá nos Testes
Envie Deleted %d of %s files para um endpoint de chat geral e você pode receber Supprimé %d des %s fichiers (ok) ou Supprimé %d de % s fichiers (um espaço invadiu o placeholder, e agora sprintf() falha em tempo de execução). Envie uma entrada plural e o modelo pode colapsar duas formas em uma, quebrando idiomas com mais de duas categorias plurais. Envie uma string com <a href="%s"> e o modelo pode traduzir a URL ou remover a tag. Nenhuma dessas falhas aparece nos seus testes, a menos que você teste especificamente a saída renderizada em cada localidade – elas aparecem como erros de tempo de execução em produção para usuários dos quais você não pode ler relatórios de bugs.
A Armadilha da Manutenção
Você pode tentar se defender disso com engenharia de prompt e pós-processamento de regex, e muitas equipes o fazem. O problema é que agora você está mantendo um motor de tradução frágil como um projeto paralelo, redescobrindo cada caso de borda de placeholder que o ecossistema Gettext já resolveu. Nós catalogamos as formas específicas como as variáveis são corrompidas – e como preveni-las – em tradução de arquivos PO sem quebrar variáveis de código. A lição ali se aplica diretamente: a qualidade do modelo raramente é o problema; o tratamento estrutural ao redor dele é.
Encaixando um Tradutor em Nuvem Orientado por API no CI
É aqui que um serviço de tradução orientado por API substitui sua suposição de translate-new-strings.sh. Em vez de criar chamadas LLM e regex de pós-processamento manualmente, sua etapa de CI carrega o .po alterado para um serviço que já entende a estrutura Gettext e retorna uma saída limpa. A forma do pipeline permanece idêntica – extrair, diferenciar, traduzir, fazer commit – mas a etapa intermediária frágil se torna uma única chamada de API.
A Chamada de API Que Substitui Seu Script
SimplePoTranslate é construído exatamente para isso. Ele expõe uma API de nuvem adequada para automação e CI, então a etapa de tradução do seu workflow se torna uma requisição que entrega o .po e recebe um traduzido de volta, sem loop por string. Seu Syntax Locking mantém %s, %1$s, {count}, HTML e tokens de código no lugar automaticamente – toda a classe de bugs da seção anterior é tratada pelo motor em vez de por regex que você mantém. Com suporte completo a Gettext plural e msgctxt, formas plurais e contexto sobrevivem à ida e volta, o que uma chamada de chat-completion não pode garantir.
Idempotência e o Portão de Revisão Permanecem Seus
Duas decisões de design ainda pertencem a você, independentemente do motor que você usar. Primeiro, a idempotência: continue traduzindo apenas as entradas vazias após o msgmerge, para que merges sem operação não produzam diff. Segundo, um portão de revisão: faça com que o job abra um pull request, como o esqueleto acima faz, em vez de fazer commit diretamente para main. A tradução automática é excelente para colocar strings no ar rapidamente, mas um olhar humano antes do merge detecta a rara falha de contexto – e um PR oferece esse olhar sem bloquear o caso comum. Equipes que lidam com muitas localidades ou muitos sites de clientes reconhecerão este padrão de o fluxo de trabalho de localização ideal para agências, onde o mesmo padrão de automatizar-e-depois-revisar se estende por dezenas de projetos.
Conclusão
Para automatizar a tradução em CI/CD sem enviar builds quebrados, o padrão é consistente: regenere o .pot no merge, faça msgmerge em cada localidade para mostrar apenas as novas strings, traduza apenas essas strings e faça commit do resultado atrás de um portão de revisão de pull request. A idempotência mantém seus diffs limpos; o portão de revisão mantém a rara tradução ruim fora de produção.
A parte a ser feita corretamente é a própria etapa de tradução. Chamadas LLM feitas manualmente corromperão placeholders e formas plurais de maneiras que seus testes não detectarão, e você gastará mais tempo mantendo a 'cola' de tradução do que o código de recurso que ela serve. Um motor orientado por API com Syntax Locking remove todo esse modo de falha, de modo que seu pipeline traduz novas strings no mesmo merge que as introduziu – e seus usuários não-ingleses param de ver placeholders em inglês.
Pronto para automatizar a tradução no seu pipeline sem quebrar os placeholders? Experimente o SimplePoTranslate gratuitamente — não é necessário cartão de crédito. Comece no plano gratuito, valide a API com seus próprios arquivos
.poe integre-a ao CI quando estiver pronto.