Come Tradurre un Post del Blog in 13 Lingue con Un Solo Agente
Un singolo agente TypeScript chiama l'API Claude in parallelo per tradurre un post EN in 12 lingue in meno di 90 secondi. Preservare la voce richiede un system prompt in due parti: prima i vincoli di stile, poi le note specifiche per locale. Il costo è di circa $0.004–$0.02 per post con Haiku. Il mio sito ha registrato un aumento del traffico internazionale del 34% in 60 giorni.
Ogni mercoledì. 28.400+ operatori. Zero riempitivo.
✓ Check your inbox — click the confirmation link to complete sign-up.
✓ You're subscribed!
✓ You're already on the list.
Indice
Aggiornato maggio 2026.
TL;DR: Un singolo agente TypeScript chiama l’API Claude in parallelo per tradurre un post EN in 12 lingue in meno di 90 secondi. Preservare la voce richiede un system prompt in due parti: prima i vincoli di stile, poi le note specifiche per locale. Il costo è di circa $0.004–$0.02 per post con Haiku. Il mio sito ha registrato un aumento del traffico internazionale del 34% in 60 giorni.
[Prospettiva dell’operatore] Eseguo questo agente ogni volta che pubblico un nuovo post. Ha elaborato 341 post in 12 lingue senza che io abbia toccato manualmente una singola traduzione. Ecco esattamente come funziona.
Perché ho costruito un agente di traduzione invece di assumere traduttori
Salto l’argomento a favore della SEO multilingue — sai già che conta. Il problema che avevo era il flusso di lavoro. Assumere traduttori per post è costoso ($40–$120/post × 12 lingue = $480–$1.440 per articolo), lento (tempi di consegna di 3–7 giorni) e impossibile da gestire in batch quando hai 341 post esistenti da recuperare.
L’altra opzione che le persone suggeriscono è Google Translate o DeepL. Entrambi sono accurati ma distruggono la voce. Il mio stile di scrittura è diretto, in prima persona e leggermente controcorrente. La traduzione automatica tende a rendere tutto formale e passivo. Questo è un problema quando la coerenza della voce fa parte del tuo brand.
Quindi ho costruito un agente TypeScript basato su Claude. Gira in CI ad ogni merge su main, distribuisce le traduzioni in parallelo, riscrive i file su disco e salta qualsiasi lingua che abbia già un file. Il tutto richiede meno di 90 secondi per un nuovo post.
La struttura del progetto
L’agente vive in scripts/agent/translate-worker.ts. È chiamato da un orchestratore di alto livello che legge il post EN, estrae il frontmatter e invia un job di traduzione per ogni lingua.
scripts/
agent/
translate-worker.ts # logica di traduzione per locale
translate-all.ts # orchestratore: legge EN, distribuisce a 12 lingue
lib/
frontmatter.ts # parse/serialize frontmatter gray-matter
voice-prompt.ts # costruttore di system prompt condivisoL’orchestratore (translate-all.ts) usa Promise.allSettled in modo che un singolo locale fallito non blocchi il resto.
L’engineering del system prompt
Qui è dove la maggior parte delle persone sbaglia. Scrivono qualcosa come “traduci questo in francese, mantieni la voce dell’autore.” Questo produce risultati mediocri.
Il mio system prompt ha due sezioni obbligatorie:
Sezione 1 — Vincoli di stile (universali, aggiunti a ogni chiamata):
// 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();Sezione 2 — Note specifiche per locale (aggiunte per ogni chiamata):
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]}`;
}Il translate worker
Ecco il worker completo. Legge il file EN, chiama Claude e scrive l’output su 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;
// Idempotenza: salta se la traduzione esiste già
const filename = path.basename(enFilePath);
const outPath = path.join(outputDir, locale, filename);
if (fs.existsSync(outPath)) {
console.log(`[${locale}] Già esistente — salto: ${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}] Scritto: ${outPath}`);
}
return outPath;
}L’orchestratore
// 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() {
// Accetta un file specifico o traduce tutti i post EN
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(`Traduzione di ${enFiles.length} post × ${LOCALES.length} lingue. Modello: ${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]}] FALLITO:`, r.reason);
}
});
}
console.log("Fatto.");
}
main();Esegui con:
# Tradurre un nuovo post
npx ts-node scripts/agent/translate-all.ts src/content/posts/en/mio-nuovo-post.md
# Tradurre tutto (idempotente — salta gli esistenti)
npx ts-node scripts/agent/translate-all.tsConfronto dei costi: Haiku vs Sonnet
Ecco cosa costa davvero per post, basandomi sul mio utilizzo:
| Modello | Token in ingresso (media) | Token in uscita (media) | Costo per lingua | Costo × 12 lingue |
|---|---|---|---|---|
| claude-haiku-4-5 | ~2.400 | ~2.600 | ~$0.0004 | ~$0.005 |
| claude-sonnet-4-5 | ~2.400 | ~2.600 | ~$0.015 | ~$0.18 |
Per 341 post × 12 lingue con Haiku: circa $1.70 in totale. Questo è l’intero backlog.
Sonnet produce una fraseologia idiomatica marginalmente migliore, ma per la maggior parte dei post la differenza non vale 36 volte il prezzo. Uso Sonnet solo per i post dove il tono persuasivo sfumato conta — come le pagine di vendita o i contenuti pillar ad alto traffico.
Puoi cambiare modello per ogni esecuzione con la variabile di ambiente TRANSLATE_MODEL:
TRANSLATE_MODEL=claude-sonnet-4-5 npx ts-node scripts/agent/translate-all.ts src/content/posts/en/post-principale.mdRisultati reali: cosa è successo al mio traffico
Ho pubblicato la traduzione completa del backlog (341 post) a dicembre 2025. In 60 giorni:
- +34% sessioni organiche su tutto il sito (Google Search Console, gen–feb 2026 vs ott–nov 2025)
- Prima nuova lingua per sessioni: Portoghese brasiliano (pt) — 11% del nuovo traffico internazionale
- Prima nuova lingua per tasso di conversione: Tedesco (de) — 2,1% di tasso di prenotazione consulenza vs 1,8% di media globale
- Performance peggiore: Arabo (ar) — il traffico è arrivato ma zero conversioni. Sospetto che il flusso di prenotazione non sia localizzato oltre il contenuto del post.
- Giapponese (ja) e coreano (ko): aumento significativo del traffico (rispettivamente 8% e 6% delle sessioni internazionali) con engagement superiore alla media (tempo sulla pagina +40% vs baseline EN)
I risultati giapponesi e coreani mi hanno sorpreso. Entrambe le lingue hanno comunità AI di alta qualità e apparentemente buona domanda per contenuti pratici da operatore.
La conclusione dell’operatore
Un agente, un’ora di configurazione, $1.70 in costi API. È tutto quello che ci è voluto per rendere 341 post scopribili in 12 lingue aggiuntive. Il solo aumento SEO ha ripagato i costi di calcolo nella prima settimana. Se gestisci un sito ricco di contenuti e non hai ancora costruito questo, stai lasciando traffico internazionale sul tavolo. Il codice sopra è l’implementazione completa — forkalo, sostituisci le note del tuo voice-prompt e lancialo sul tuo backlog stasera.
Ogni mercoledì. 28.400+ operatori. Zero riempitivo.
✓ Check your inbox — click the confirmation link to complete sign-up.
✓ You're subscribed!
✓ You're already on the list.
Ricevi il manuale dell'IA nella tua casella di posta
Ogni mercoledì. 28.400+ operatori. Zero riempitivo.
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.