Système de traduction
Ce site est publié en 17 langues. Non pas par une équipe de traducteurs, mais grâce à un script Python, un modèle Claude Opus et une structure de fichiers conçue pour que tout s’emboîte parfaitement. Voici comment l’ensemble fonctionne.
L’arborescence des langues
Section intitulée « L’arborescence des langues »La documentation réside dans src/content/docs/. L’anglais est la racine — chaque autre langue en est le miroir exact :
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)Chaque fichier traduit est un miroir structurel de sa source anglaise. Même nom de fichier, même chemin de sous-répertoire, mêmes clés de frontmatter. La seule différence est que le texte est dans une autre langue.
Pourquoi les miroirs sont importants
Section intitulée « Pourquoi les miroirs sont importants »Starlight (le framework de documentation) repose sur cette symétrie. Lorsqu’un utilisateur change de langue, Starlight remplace /docs/getting-started/ par /fr/docs/getting-started/ — même chemin, préfixe de locale différent. Si le fichier français n’existe pas exactement à fr/getting-started.md, le sélecteur de langue se casse ou revient silencieusement à l’anglais.
Les 17 langues
Section intitulée « Les 17 langues »Les langues sont classées par population mondiale de joueurs d’échecs, sur la base de données provenant de Lichess, Chess.com et des inscriptions FIDE. L’anglais vient en premier en tant que langue source ; tout le reste suit le classement échiquéen :
| Rang | Code | Langue | Style |
|---|---|---|---|
| 1 | en | Anglais | Source de référence |
| 2 | es | Espagnol | Standard formel |
| 3 | hi | Hindi | Écriture devanagari |
| 4 | ru | Russe | Standard formel |
| 5 | de | Allemand | Standard formel |
| 6 | fr | Français | Standard formel |
| 7 | pt | Portugais | Portugais européen |
| 11 | pl | Polonais | Standard formel |
| 12 | it | Italien | Standard formel |
| 13 | uk | Ukrainien | Standard formel |
| 14 | tr | Turc | Standard formel |
| 17 | ko | Coréen | Forme 합니다/습니다 |
| 18 | zh | Chinois (simplifié) | Caractères simplifiés |
| — | zh-tw | Chinois (traditionnel) | Caractères traditionnels |
| 23 | nb | Norvégien bokmål | Bokmål standard |
| — | be | Biélorusse | Biélorusse standard |
| 34 | ja | Japonais | Forme です/ます |
La colonne « style » est importante. Le japonais et le coréen comportent des choix de registre formel qui affectent chaque phrase. Le prompt de traduction inclut ces instructions afin que le modèle produise un texte naturel et soigné — et non une traduction mécanique rigide.
Cet ordre contrôle également le menu déroulant des langues dans l’en-tête du site. Les langues les plus parlées dans le monde des échecs apparaissent en premier, pour que les utilisateurs trouvent plus facilement la leur sans avoir à défiler.
Le pipeline de traduction
Section intitulée « Le pipeline de traduction »Étape 1 : Rédiger en anglais
Section intitulée « Étape 1 : Rédiger en anglais »Toute la documentation commence sous forme de markdown en anglais dans src/content/docs/. Le frontmatter contient un title et une description :
---title: "Getting Started"description: "Install En Parlant~ and play your first game."---
Download the latest release...Étape 2 : Exécuter le script
Section intitulée « Étape 2 : Exécuter le script »Un script Python (scripts/translate-docs.py) lit chaque fichier source en anglais, l’envoie à l’API Claude et écrit le markdown traduit :
python3 scripts/translate-docs.py \ --anthropic-key $ANTHROPIC_API_KEY \ --model claude-opus-4-6 \ --workers 5Le script prend environ 60 à 70 minutes pour traduire les 28 fichiers source dans les 16 langues cibles (448 fichiers au total). Il exécute 5 appels API en parallèle pour rester dans les limites de débit.
Pour une seule nouvelle langue, cela prend environ 4 minutes.
Étape 3 : Compiler
Section intitulée « Étape 3 : Compiler »pnpm buildAstro lit le markdown source, le rend via les templates de Starlight et produit du HTML statique dans dist/. La compilation prend environ 30 secondes pour l’ensemble des ~500 pages.
Étape 4 : Déployer
Section intitulée « Étape 4 : Déployer »pnpm run deployPousse le site compilé vers Cloudflare Workers.
Fonctionnement du script
Section intitulée « Fonctionnement du script »Le script de traduction est volontairement simple — environ 300 lignes de Python. Voici le flux :
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐│ 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 filesLe prompt indique à Claude exactement ce qu’il faut traduire et ce qu’il faut laisser tel quel :
- Traduire : le texte, les titres, les libellés de tableaux, le title et la description du frontmatter
- Conserver en anglais : les blocs de code, le code en ligne, les exemples de commandes, les chemins de fichiers, les URL, les noms de produits, les noms de personnes, les diagrammes ASCII
Cette séparation est cruciale. Une commande comme pnpm build doit rester pnpm build dans toutes les langues. Un nom de produit comme « En Parlant~ » ou « Stockfish » reste non traduit. Mais « Getting Started » devient « はじめに » en japonais et « Начало работы » en russe.
Pourquoi Opus ?
Section intitulée « Pourquoi Opus ? »Claude Opus 4.6 produit des traductions sensiblement meilleures que les modèles plus rapides. La différence se manifeste dans :
- La fluidité des formulations — Opus écrit comme un locuteur natif, pas comme un traducteur. Il restructure les phrases lorsque l’ordre des mots anglais sonnerait maladroit dans la langue cible.
- La précision technique — La terminologie échiquéenne, le jargon TTS et les concepts logiciels sont traduits avec les termes spécifiques au domaine.
- La cohérence — Le registre formel reste constant tout au long du texte. Le japonais utilise la forme です/ます partout, sans alterner entre registre familier et poli en plein paragraphe.
- La gestion du MDX — Opus préserve correctement les balises de composants JSX (
<Card>,<CardGrid>) et les instructionsimportdans les fichiers.mdxsans les altérer.
La différence de coût est réelle — environ 28 $ pour une traduction complète du site avec Opus contre 5 $ avec Sonnet — mais pour 448 fichiers que les utilisateurs vont réellement lire, la qualité en vaut la peine.
L’architecture de routage des locales
Section intitulée « L’architecture de routage des locales »C’est la partie qui a pris le plus de temps à faire fonctionner correctement.
Le problème
Section intitulée « Le problème »Starlight détecte la langue d’une page en vérifiant le premier segment de son slug de contenu. Quand il voit fr/docs/getting-started, il sait que c’est du français parce que fr est le premier segment. Mais l’implémentation initiale produisait des slugs comme docs/fr/getting-started — la locale enfouie sous docs/. Starlight voyait docs comme premier segment, traitait tout comme de l’anglais et générait plus de 7 000 pages en double au lieu de ~500.
La solution
Section intitulée « La solution »Une fonction generateId personnalisée dans src/content.config.ts contrôle la façon dont les chemins de fichiers deviennent des slugs de contenu :
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}`;}Cela place le préfixe de locale avant docs/ :
| Chemin du fichier | 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’anglais utilise defaultLocale: "root", ce qui signifie aucun préfixe — il réside directement à /docs/, et non à /en/docs/.
Le tableau localeKeys
Section intitulée « Le tableau localeKeys »Un tableau localeKeys dans le même fichier doit lister chaque locale non anglaise. Si une locale existe dans la configuration d’Astro mais pas dans ce tableau, son contenu traduit est traité comme du contenu anglais — le sélecteur de langue se casse et le nombre de pages explose.
Cache du data store
Section intitulée « Cache du data store »Astro met en cache les correspondances de slugs dans .astro/data-store.json. Après toute modification de configuration des locales, ce fichier doit être supprimé avant de recompiler, sinon la compilation réussit avec un routage obsolète (incorrect).
La structure du menu
Section intitulée « La structure du menu »La barre latérale est définie dans astro.config.mjs. Les éléments qui pointent vers une page (propriété slug) utilisent automatiquement le titre du frontmatter de la page traduite — aucune traduction manuelle nécessaire :
{ slug: "docs/getting-started" }// English: "Getting Started" (from English frontmatter)// French: "Premiers pas" (from French frontmatter)// Japanese: "はじめに" (from Japanese frontmatter)Mais les libellés de groupes et les liens externes nécessitent des traductions explicites :
{ 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" }, // ... ],}Sept éléments de la barre latérale nécessitent des traductions manuelles : Welcome, Features, App Menus, Setup Guides, Under the Hood, Credits et Accessibility. Ajouter une nouvelle langue signifie ajouter une entrée dans chacun de ces sept blocs.
La connexion avec l’application
Section intitulée « La connexion avec l’application »En Parlant~ renvoie vers cette documentation depuis son menu Aide. Le lien est sensible à la locale — si vous utilisez l’application en français, il ouvre la documentation en français :
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]);Chaque langue disponible dans l’application a une traduction correspondante sur le site. La correspondance est automatique — fr_FR pointe vers /fr/docs/, ja_JP pointe vers /ja/docs/. Le seul cas particulier est le chinois traditionnel : zh_TW pointe vers /zh-tw/docs/ (avec trait d’union).
Pourquoi c’est facile maintenant
Section intitulée « Pourquoi c’est facile maintenant »Les parties difficiles sont résolues :
-
L’architecture de routage est en place. La fonction
generateId, le tableaulocaleKeyset la configurationdefaultLocale: "root"fonctionnent ensemble pour que Starlight génère la structure d’URL correcte. C’était le point le plus douloureux — il a fallu remonter à travers plus de 6 fichiers source dans Starlight et Astro pour trouver et corriger le problème. -
Le script de traduction gère tout. Retraduction complète du site, ajout d’une seule langue, mise à jour de fichiers individuels — tout passe par le même script avec des options différentes. Il relance automatiquement en cas de limite de débit, parallélise les tâches entre les workers et signale clairement les erreurs.
-
Ajouter une nouvelle langue, c’est quatre modifications de configuration et une commande. Ajouter la locale dans
astro.config.mjs,content.config.tsettranslate-docs.py, ajouter les traductions de la barre latérale, lancer le script. Environ 10 minutes de travail plus 4 minutes de traduction. -
Mettre à jour le contenu est encore plus simple. Modifier la source en anglais, lancer le script avec
--fileset--overwriteuniquement pour les fichiers modifiés, recompiler. Ou pour des corrections mineures de texte, modifier directement les fichiers traduits. -
L’ensemble du pipeline est capturé dans un skill. Lancer
/translate_docsguide à travers tout le processus — quel mode utiliser, quelles options passer, vérifications préalables, vérification post-traduction. Aucune connaissance institutionnelle requise.
La traduction totale de 448 fichiers dans 16 langues a coûté environ 28 $ et a pris environ 70 minutes. Une seule nouvelle langue coûte environ 1,70 $. Un seul fichier retraduit dans toutes les langues coûte environ 1 $. Ce sont les coûts récurrents pour maintenir la documentation à jour en 17 langues.