Como Traduzir um Post de Blog em 13 Idiomas com Um Agente
Um único agente TypeScript chama a API Claude em paralelo para traduzir um post em inglês para 12 idiomas em menos de 90 segundos. Preservar a voz exige um system prompt em duas partes: restrições de estilo primeiro, depois notas específicas por locale. O custo é de aproximadamente $0.004–$0.02 por post com Haiku. Meu site teve um aumento de 34% no tráfego internacional em 60 dias.
Toda quarta-feira. 28.400+ operadores. Zero enrolação.
✓ Check your inbox — click the confirmation link to complete sign-up.
✓ You're subscribed!
✓ You're already on the list.
Sumário
Atualizado em maio de 2026.
TL;DR: Um único agente TypeScript chama a API Claude em paralelo para traduzir um post em inglês para 12 idiomas em menos de 90 segundos. Preservar a voz exige um system prompt em duas partes: restrições de estilo primeiro, depois notas específicas por locale. O custo é de aproximadamente $0.004–$0.02 por post com Haiku. Meu site teve um aumento de 34% no tráfego internacional em 60 dias.
[Perspectiva do operador] Executo esse agente toda vez que publico um novo post. Ele processou 341 posts em 12 idiomas sem que eu tocasse em uma única tradução manualmente. Veja exatamente como funciona.
Por que construí um agente de tradução em vez de contratar tradutores
Vou pular o argumento para SEO multilíngue — você já sabe que importa. O problema que eu tinha era de fluxo de trabalho. Contratar tradutores por post é caro ($40–$120/post × 12 idiomas = $480–$1.440 por artigo), lento (prazo de 3 a 7 dias) e impossível de processar em lote quando você tem 341 posts existentes para recuperar.
A outra opção que as pessoas sugerem é Google Translate ou DeepL. Ambas são boas para precisão, mas destroem a voz. Meu estilo de escrita é direto, em primeira pessoa e levemente contrário. A tradução automática tende a tornar tudo formal e passivo. Isso é um problema quando a consistência de voz faz parte da sua marca.
Então construí um agente TypeScript com Claude. Ele roda em CI a cada merge para main, distribui as traduções em paralelo, escreve os arquivos de volta no disco e ignora qualquer idioma que já tenha um arquivo. O processo todo leva menos de 90 segundos para um novo post.
A estrutura do projeto
O agente vive em scripts/agent/translate-worker.ts. É chamado por um orquestrador de nível superior que lê o post em inglês, extrai o frontmatter e despacha um job de tradução por idioma.
scripts/
agent/
translate-worker.ts # lógica de tradução por locale
translate-all.ts # orquestrador: lê EN, distribui para 12 idiomas
lib/
frontmatter.ts # parsear/serializar frontmatter gray-matter
voice-prompt.ts # construtor de system prompt compartilhadoO orquestrador (translate-all.ts) usa Promise.allSettled para que um único locale com falha não bloqueie o resto.
O engineering do system prompt
É aqui que a maioria das pessoas erra. Elas escrevem algo como “traduza isso para o francês, mantendo a voz do autor.” Isso produz resultados mediocres.
Meu system prompt tem duas seções obrigatórias:
Seção 1 — Restrições de estilo (universal, adicionadas a cada chamada):
// scripts/agent/lib/voice-prompt.ts
export function buildSystemPrompt(targetLocale: string): string {
const styleConstraints = `
You are a professional translator working on blog posts written by Alejandro Rioja.
STYLE RULES — apply to every locale:
- Short paragraphs (1–3 sentences max). Do not merge them.
- First-person, direct voice. Never passive if active is natural.
- No filler phrases: no "In today's world", no "It is worth noting that".
- Preserve all markdown: headings, bold, italics, code blocks, links.
- Translate heading text but keep the ## / ### prefix exactly.
- Code blocks: translate comments only. Keep all variable names, strings, and syntax in English.
- Preserve frontmatter keys exactly. Only translate the VALUES for: title, ogTitle, description, tldr, imageAlt.
- Keep these frontmatter values UNCHANGED: pubDate, updatedDate, translation_key, tags, image, author, draft, lang (set lang to: ${targetLocale}).
`.trim();Seção 2 — Notas específicas por locale (adicionadas por chamada):
const localeNotes: Record<string, string> = {
ar: "Arabic: use Modern Standard Arabic (MSA). RTL layout is handled by the CMS — do not add any RTL markup. Avoid overly formal Classical Arabic registers.",
de: "German: use informal 'du' not formal 'Sie'. Compound nouns are fine; don't over-hyphenate. Keep tech terms in English when that's the industry standard (e.g. 'Content Marketing', 'SEO').",
es: "Spanish: use neutral Latin American Spanish, not Castilian. Tuteo ('tú') over 'usted'. Keep anglicisms that are standard in tech (SEO, agente, prompt).",
fr: "French: use informal 'tu'. Avoid over-formalizing. Tech anglicisms are acceptable when widely used (SEO, agent, prompt).",
hi: "Hindi: use Devanagari script. Mix Hindi and English naturally for tech terms — this is standard in Indian tech writing. Don't force Hindi equivalents for words like 'agent', 'prompt', 'SEO'.",
it: "Italian: use 'tu' form. Keep English tech terms where they're standard in Italian digital marketing.",
ja: "Japanese: use です/ます (polite) style, not casual or keigo. Keep technical English terms in katakana where standard (e.g. エージェント, プロンプト, SEO).",
ko: "Korean: use 합쇼체 (formal polite). Tech terms in English or standard Korean loanwords. Keep SEO, agent, prompt as-is or standard loanwords.",
nl: "Dutch: use 'je/jij' (informal). Keep English tech terms standard in Dutch digital marketing.",
pt: "Portuguese: use Brazilian Portuguese (pt-BR). Informal 'você'. Keep tech anglicisms standard in Brazilian digital marketing.",
ru: "Russian: use modern, accessible Russian. Avoid overly bureaucratic phrasing. Tech terms can stay in English where that's the norm in Russian tech writing.",
zh: "Chinese: use Simplified Chinese (zh-CN). Modern, accessible tone. Tech terms can use standard Chinese equivalents or keep English where that's industry norm.",
};
return `${styleConstraints}\n\nLOCALE-SPECIFIC NOTES for ${targetLocale}:\n${localeNotes[targetLocale]}`;
}O translate worker
Aqui está o worker completo. Ele lê o arquivo em inglês, chama o Claude e escreve o resultado no disco.
// scripts/agent/translate-worker.ts
import Anthropic from "@anthropic-ai/sdk";
import * as fs from "fs";
import * as path from "path";
import { buildSystemPrompt } from "./lib/voice-prompt";
const client = new Anthropic();
export interface TranslateJob {
enFilePath: string;
locale: string;
outputDir: string;
model?: "claude-haiku-4-5" | "claude-sonnet-4-5";
dryRun?: boolean;
}
export async function translatePost(job: TranslateJob): Promise<string> {
const { enFilePath, locale, outputDir, model = "claude-haiku-4-5", dryRun = false } = job;
// Idempotência: pular se a tradução já existir
const filename = path.basename(enFilePath);
const outPath = path.join(outputDir, locale, filename);
if (fs.existsSync(outPath)) {
console.log(`[${locale}] Já existe — pulando: ${outPath}`);
return outPath;
}
const enContent = fs.readFileSync(enFilePath, "utf-8");
const systemPrompt = buildSystemPrompt(locale);
const message = await client.messages.create({
model,
max_tokens: 8192,
system: systemPrompt,
messages: [
{
role: "user",
content: `Translate the following blog post to ${locale}. Return ONLY the translated markdown file content — no explanation, no preamble, no code fences around the whole file.\n\n${enContent}`,
},
],
});
const translated = (message.content[0] as { type: string; text: string }).text;
if (!dryRun) {
fs.mkdirSync(path.join(outputDir, locale), { recursive: true });
fs.writeFileSync(outPath, translated, "utf-8");
console.log(`[${locale}] Escrito: ${outPath}`);
}
return outPath;
}O orquestrador
// scripts/agent/translate-all.ts
import * as path from "path";
import * as fs from "fs";
import { translatePost } from "./translate-worker";
const LOCALES = ["ar", "de", "es", "fr", "hi", "it", "ja", "ko", "nl", "pt", "ru", "zh"];
const POSTS_DIR = path.resolve("src/content/posts");
const MODEL = (process.env.TRANSLATE_MODEL as "claude-haiku-4-5" | "claude-sonnet-4-5") ?? "claude-haiku-4-5";
async function main() {
// Aceitar um arquivo específico ou traduzir todos os posts em inglês
const targetFile = process.argv[2];
const enFiles = targetFile
? [path.resolve(targetFile)]
: fs.readdirSync(path.join(POSTS_DIR, "en")).map((f) => path.join(POSTS_DIR, "en", f));
console.log(`Traduzindo ${enFiles.length} post(s) × ${LOCALES.length} idiomas. Modelo: ${MODEL}`);
for (const enFile of enFiles) {
const results = await Promise.allSettled(
LOCALES.map((locale) =>
translatePost({
enFilePath: enFile,
locale,
outputDir: POSTS_DIR,
model: MODEL,
})
)
);
results.forEach((r, i) => {
if (r.status === "rejected") {
console.error(`[${LOCALES[i]}] FALHOU:`, r.reason);
}
});
}
console.log("Concluído.");
}
main();Execute com:
# Traduzir um novo post
npx ts-node scripts/agent/translate-all.ts src/content/posts/en/meu-novo-post.md
# Traduzir tudo (idempotente — pula os existentes)
npx ts-node scripts/agent/translate-all.tsComparativo de custos: Haiku vs Sonnet
Veja o que custa de verdade por post, com base no meu uso:
| Modelo | Tokens de entrada (média) | Tokens de saída (média) | Custo por idioma | Custo × 12 idiomas |
|---|---|---|---|---|
| claude-haiku-4-5 | ~2.400 | ~2.600 | ~$0.0004 | ~$0.005 |
| claude-sonnet-4-5 | ~2.400 | ~2.600 | ~$0.015 | ~$0.18 |
Para 341 posts × 12 idiomas com Haiku: aproximadamente $1.70 no total. Esse é o backlog inteiro.
Sonnet produz fraseologia idiomática marginalmente melhor, mas para a maioria dos posts a diferença não vale 36 vezes o preço. Uso Sonnet apenas para posts onde o tom persuasivo matizado importa — como páginas de vendas ou conteúdo âncora de alto tráfego.
Você pode trocar de modelo por execução com a variável de ambiente TRANSLATE_MODEL:
TRANSLATE_MODEL=claude-sonnet-4-5 npx ts-node scripts/agent/translate-all.ts src/content/posts/en/post-principal.mdResultados reais: o que aconteceu com meu tráfego
Publiquei a tradução completa do backlog (341 posts) em dezembro de 2025. Em 60 dias:
- +34% de sessões orgânicas em todo o site (Google Search Console, jan–fev 2026 vs out–nov 2025)
- Principal novo idioma por sessões: Português brasileiro (pt) — 11% do novo tráfego internacional
- Principal novo idioma por taxa de conversão: Alemão (de) — 2,1% de taxa de reserva de consultoria vs 1,8% da média global
- Pior desempenho: Árabe (ar) — o tráfego chegou mas zero conversões. Suspeito que o fluxo de reserva não está localizado além do conteúdo do post.
- Japonês (ja) e coreano (ko): aumento significativo de tráfego (8% e 6% das sessões internacionais respectivamente) com engajamento acima da média (tempo na página +40% vs baseline em inglês)
Os resultados de japonês e coreano me surpreenderam. Ambos os idiomas têm comunidades de IA de alta qualidade e aparentemente boa demanda por conteúdo prático de operadores.
A conclusão do operador
Um agente, uma hora de configuração, $1.70 em custos de API. Foi isso que bastou para tornar 341 posts descobríveis em 12 idiomas adicionais. O aumento de SEO por si só pagou pelo custo computacional na primeira semana. Se você gerencia um site com muito conteúdo e ainda não construiu isso, está deixando tráfego internacional na mesa. O código acima é a implementação completa — faça um fork, troque as notas do seu voice-prompt e execute contra o seu backlog hoje à noite.
Toda quarta-feira. 28.400+ operadores. Zero enrolação.
✓ Check your inbox — click the confirmation link to complete sign-up.
✓ You're subscribed!
✓ You're already on the list.
Receba o manual de IA na sua caixa de entrada
Toda quarta-feira. 28.400+ operadores. Zero enrolação.
Check your inbox.
We sent you a confirmation email — click the link inside to complete your subscription. Check spam if you don't see it within a minute.
You're subscribed.
Welcome — the next edition lands in your inbox soon.
You're already on the list — look for it every Wednesday.