Sistema di traduzione
Questo sito è pubblicato in 17 lingue. Non da un team di traduttori, ma da uno script Python, un modello Claude Opus e una struttura di file progettata affinché tutto si incastri perfettamente. Ecco come funziona l’intero sistema.
L’albero delle lingue
Sezione intitolata “L’albero delle lingue”La documentazione risiede in src/content/docs/. L’inglese è la radice — ogni altra lingua lo rispecchia esattamente:
src/content/docs/├── index.mdx ← English (root)├── getting-started.md├── features/│ ├── play-chess.md│ ├── multiplayer.md│ └── ...├── setup/│ ├── tts-overview.md│ └── ...├── under-the-hood/│ ├── architecture.md│ └── ...│├── fr/ ← French│ ├── index.mdx│ ├── getting-started.md│ ├── features/│ │ ├── play-chess.md│ │ └── ...│ └── ...│├── ja/ ← Japanese│ ├── index.mdx│ ├── getting-started.md│ └── ...│└── ... (16 language directories total)Ogni file tradotto è uno specchio strutturale del suo sorgente inglese. Stesso nome di file, stesso percorso di sottodirectory, stesse chiavi nel frontmatter. L’unica differenza è che la prosa è in un’altra lingua.
Perché la simmetria è importante
Sezione intitolata “Perché la simmetria è importante”Starlight (il framework di documentazione) si basa su questa simmetria. Quando un utente cambia lingua, Starlight sostituisce /docs/getting-started/ con /fr/docs/getting-started/ — stesso percorso, prefisso locale diverso. Se il file francese non esiste esattamente in fr/getting-started.md, il selettore di lingua si interrompe o torna silenziosamente all’inglese.
Le 17 lingue
Sezione intitolata “Le 17 lingue”Le lingue sono ordinate per popolazione mondiale di giocatori di scacchi, basandosi su dati di Lichess, Chess.com e registrazioni FIDE. L’inglese è al primo posto come lingua sorgente; tutto il resto segue le classifiche scacchistiche:
| Posizione | Codice | Lingua | Stile |
|---|---|---|---|
| 1 | en | Inglese | Fonte di riferimento |
| 2 | es | Spagnolo | Formale standard |
| 3 | hi | Hindi | Scrittura Devanagari |
| 4 | ru | Russo | Formale standard |
| 5 | de | Tedesco | Formale standard |
| 6 | fr | Francese | Formale standard |
| 7 | pt | Portoghese | Portoghese europeo |
| 11 | pl | Polacco | Formale standard |
| 12 | it | Italiano | Formale standard |
| 13 | uk | Ucraino | Formale standard |
| 14 | tr | Turco | Formale standard |
| 17 | ko | Coreano | Forma 합니다/습니다 |
| 18 | zh | Cinese (semplificato) | Caratteri semplificati |
| — | zh-tw | Cinese (tradizionale) | Caratteri tradizionali |
| 23 | nb | Norvegese Bokmål | Bokmål standard |
| — | be | Bielorusso | Bielorusso standard |
| 34 | ja | Giapponese | Forma です/ます |
La colonna “stile” è importante. Giapponese e coreano hanno scelte di registro formale che influenzano ogni frase. Il prompt di traduzione include queste istruzioni affinché il modello produca una prosa naturale e curata — non un output meccanico e rigido.
Questo ordinamento controlla anche il menu a discesa delle lingue nell’intestazione del sito. Le lingue scacchistiche più parlate appaiono per prime, così gli utenti hanno maggiori probabilità di trovare la propria senza scorrere.
La pipeline di traduzione
Sezione intitolata “La pipeline di traduzione”Fase 1: Scrivere in inglese
Sezione intitolata “Fase 1: Scrivere in inglese”Tutta la documentazione inizia come markdown in inglese in src/content/docs/. Il frontmatter contiene un title e una description:
---title: "Getting Started"description: "Install En Parlant~ and play your first game."---
Download the latest release...Fase 2: Eseguire lo script
Sezione intitolata “Fase 2: Eseguire lo script”Uno script Python (scripts/translate-docs.py) legge ogni file sorgente inglese, lo invia all’API di Claude e scrive il markdown tradotto:
python3 scripts/translate-docs.py \ --anthropic-key $ANTHROPIC_API_KEY \ --model claude-opus-4-6 \ --workers 5Lo script impiega circa 60–70 minuti per tradurre tutti i 28 file sorgente in tutte le 16 lingue di destinazione (448 file in totale). Esegue 5 chiamate API in parallelo per rimanere entro i limiti di frequenza.
Per una singola nuova lingua, bastano circa 4 minuti.
Fase 3: Build
Sezione intitolata “Fase 3: Build”pnpm buildAstro legge il markdown sorgente, lo renderizza attraverso i template di Starlight e produce HTML statico nella directory dist/. Il build impiega circa 30 secondi per tutte le ~500 pagine.
Fase 4: Deploy
Sezione intitolata “Fase 4: Deploy”pnpm run deployPubblica il sito compilato su Cloudflare Workers.
Come funziona lo script
Sezione intitolata “Come funziona lo script”Lo script di traduzione è volutamente semplice — circa 300 righe di Python. Ecco il flusso:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐│ Read English │────▶│ Claude API │────▶│ Write target ││ source file │ │ (Opus 4.6) │ │ language file ││ │ │ │ │ ││ getting- │ │ "Translate into │ │ fr/getting- ││ started.md │ │ French..." │ │ started.md │└─────────────────┘ └──────────────────┘ └─────────────────┘ × 5 parallel × 16 languages × 28 filesIl prompt dice a Claude esattamente cosa tradurre e cosa lasciare invariato:
- Tradurre: prosa, intestazioni, etichette delle tabelle, title e description del frontmatter
- Mantenere in inglese: blocchi di codice, codice inline, esempi di comandi, percorsi di file, URL, nomi di prodotto, nomi di persone, diagrammi ASCII
Questa separazione è fondamentale. Un comando come pnpm build deve rimanere pnpm build in ogni lingua. Un nome di prodotto come “En Parlant~” o “Stockfish” resta non tradotto. Ma “Getting Started” diventa “はじめに” in giapponese e “Начало работы” in russo.
Perché Opus?
Sezione intitolata “Perché Opus?”Claude Opus 4.6 produce traduzioni notevolmente migliori rispetto ai modelli più veloci. La differenza si nota in:
- Formulazione naturale — Opus scrive come un madrelingua, non come un traduttore. Ristruttura le frasi quando l’ordine delle parole inglese risulterebbe innaturale nella lingua di destinazione.
- Accuratezza tecnica — La terminologia scacchistica, il gergo TTS e i concetti software vengono tradotti utilizzando i termini corretti specifici del dominio.
- Coerenza — Il registro formale rimane coerente ovunque. Il giapponese usa la forma です/ます dappertutto, senza alternare tra registro informale e formale a metà paragrafo.
- Gestione MDX — Opus preserva correttamente i tag dei componenti JSX (
<Card>,<CardGrid>) e le istruzioniimportnei file.mdxsenza alterarli.
La differenza di costo è reale — circa $28 per una traduzione completa del sito con Opus contro $5 con Sonnet — ma per 448 file che gli utenti leggeranno effettivamente, la qualità ne vale la pena.
L’architettura di routing delle lingue
Sezione intitolata “L’architettura di routing delle lingue”Questa è la parte che ha richiesto più tempo per essere risolta correttamente.
Il problema
Sezione intitolata “Il problema”Starlight rileva la lingua di una pagina controllando il primo segmento dello slug del contenuto. Quando vede fr/docs/getting-started, sa che è francese perché fr è il primo segmento. Ma l’implementazione iniziale produceva slug come docs/fr/getting-started — con la lingua sepolta sotto docs/. Starlight vedeva docs come primo segmento, trattava tutto come inglese e generava oltre 7.000 pagine duplicate invece di circa 500.
La soluzione
Sezione intitolata “La soluzione”Una funzione personalizzata generateId in src/content.config.ts controlla come i percorsi dei file diventano slug di contenuto:
generateId({ entry }) { const slug = entry.replace(/\.[^.]+$/, ""); const firstSeg = slug.split("/")[0]; if (firstSeg && localeKeys.includes(firstSeg)) { return `${firstSeg}/docs/${slug.slice(firstSeg.length + 1)}`; } return `docs/${slug}`;}Questo posiziona il prefisso locale prima di docs/:
| Percorso del file | Slug | URL |
|---|---|---|
getting-started.md | docs/getting-started | /docs/getting-started/ |
fr/getting-started.md | fr/docs/getting-started | /fr/docs/getting-started/ |
ja/features/puzzles.md | ja/docs/features/puzzles | /ja/docs/features/puzzles/ |
L’inglese usa defaultLocale: "root", il che significa nessun prefisso — risiede direttamente in /docs/, non in /en/docs/.
L’array localeKeys
Sezione intitolata “L’array localeKeys”Un array localeKeys nello stesso file deve elencare ogni lingua non inglese. Se una lingua esiste nella configurazione di Astro ma non in questo array, il suo contenuto tradotto viene trattato come contenuto inglese — il selettore di lingua si interrompe e il conteggio delle pagine esplode.
Cache del data store
Sezione intitolata “Cache del data store”Astro memorizza nella cache le mappature degli slug in .astro/data-store.json. Dopo qualsiasi modifica alla configurazione delle lingue, questo file deve essere eliminato prima di ricompilare, altrimenti il build riesce con un routing obsoleto (errato).
La struttura del menu
Sezione intitolata “La struttura del menu”La barra laterale è definita in astro.config.mjs. Gli elementi che puntano a una pagina (proprietà slug) utilizzano automaticamente il titolo del frontmatter della pagina tradotta — nessuna traduzione manuale necessaria:
{ slug: "docs/getting-started" }// English: "Getting Started" (from English frontmatter)// French: "Premiers pas" (from French frontmatter)// Japanese: "はじめに" (from Japanese frontmatter)Ma le etichette dei gruppi e i link esterni necessitano di traduzioni esplicite:
{ label: "Features", translations: { fr: "Fonctionnalités", es: "Características", de: "Funktionen", ja: "機能", // ... all 16 languages }, items: [ { slug: "docs/features/play-chess" }, { slug: "docs/features/multiplayer" }, // ... ],}Sette elementi della barra laterale necessitano di traduzioni manuali: Welcome, Features, App Menus, Setup Guides, Under the Hood, Credits e Accessibility. Aggiungere una nuova lingua significa aggiungere una voce a ciascuno di questi sette blocchi.
La connessione con l’app
Sezione intitolata “La connessione con l’app”En Parlant~ collega questa documentazione dal suo menu Aiuto. Il link è sensibile alla lingua — se stai usando l’app in francese, apre la documentazione in francese:
const docsLocalePrefix = useMemo(() => { const lang = i18n.language; // e.g. "fr_FR", "zh_TW" if (!lang || lang.startsWith("en")) return ""; if (lang === "zh_TW") return "/zh-tw"; return `/${lang.slice(0, 2)}`;}, [i18n.language]);Ogni lingua disponibile nell’app ha una traduzione corrispondente sul sito. La mappatura è automatica — fr_FR corrisponde a /fr/docs/, ja_JP corrisponde a /ja/docs/. L’unico caso speciale è il cinese tradizionale: zh_TW corrisponde a /zh-tw/docs/ (con trattino).
Perché ora è semplice
Sezione intitolata “Perché ora è semplice”Le parti difficili sono state risolte:
-
L’architettura di routing è definita. La funzione
generateId, l’arraylocaleKeyse la configurazionedefaultLocale: "root"lavorano insieme affinché Starlight generi la struttura URL corretta. Questo è stato il problema più grande — ha richiesto di analizzare oltre 6 file sorgente in Starlight e Astro per individuare e correggere il problema. -
Lo script di traduzione gestisce tutto. Ritraduzione completa del sito, aggiunta di una singola lingua, aggiornamenti di singoli file — tutto con lo stesso script con flag differenti. Riprova automaticamente in caso di limiti di frequenza, parallelizza tra i worker e segnala gli errori in modo chiaro.
-
Aggiungere una nuova lingua richiede quattro modifiche alla configurazione e un comando. Aggiungere la lingua a
astro.config.mjs,content.config.tsetranslate-docs.py, aggiungere le traduzioni della barra laterale, eseguire lo script. Circa 10 minuti di lavoro più 4 minuti di tempo di traduzione. -
Aggiornare i contenuti è ancora più semplice. Modificare il sorgente inglese, eseguire lo script con
--filese--overwritesolo per i file modificati, ricompilare. Oppure per piccole correzioni alla prosa, modificare direttamente i file tradotti. -
L’intera pipeline è catturata in una skill. Eseguire
/translate_docsguida attraverso l’intero processo — quale modalità usare, quali flag passare, controlli preliminari, verifica post-traduzione. Nessuna conoscenza pregressa necessaria.
La traduzione totale di 448 file in 16 lingue è costata circa $28 e ha richiesto circa 70 minuti. Una singola nuova lingua costa circa $1,70. Un singolo file ritradotto in tutte le lingue costa circa $1. Questi sono i costi correnti per mantenere la documentazione aggiornata in 17 lingue.