Übersetzungssystem
Diese Website wird in 17 Sprachen veröffentlicht. Nicht von einem Übersetzerteam, sondern durch ein Python-Skript, ein Claude Opus-Modell und eine Dateistruktur, bei der alles nahtlos ineinandergreift. So funktioniert das Ganze.
Der Sprachbaum
Abschnitt betitelt „Der Sprachbaum“Die Dokumentation befindet sich in src/content/docs/. Englisch ist die Wurzel — jede andere Sprache spiegelt sie exakt wider:
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)Jede übersetzte Datei ist eine strukturelle Spiegelung ihrer englischen Quelle. Gleicher Dateiname, gleicher Unterverzeichnispfad, gleiche Frontmatter-Schlüssel. Der einzige Unterschied ist, dass der Fließtext in einer anderen Sprache verfasst ist.
Warum Spiegelung wichtig ist
Abschnitt betitelt „Warum Spiegelung wichtig ist“Starlight (das Dokumentations-Framework) basiert auf dieser Symmetrie. Wenn ein Benutzer die Sprache wechselt, ersetzt Starlight /docs/getting-started/ durch /fr/docs/getting-started/ — gleicher Pfad, anderes Locale-Präfix. Wenn die französische Datei nicht exakt unter fr/getting-started.md existiert, funktioniert der Sprachwechsler nicht oder fällt stillschweigend auf Englisch zurück.
Die 17 Sprachen
Abschnitt betitelt „Die 17 Sprachen“Die Sprachen sind nach der weltweiten schachspielenden Bevölkerung sortiert, basierend auf Daten von Lichess, Chess.com und FIDE-Registrierungen. Englisch steht als Quellsprache an erster Stelle; alles andere folgt der Schach-Rangliste:
| Rang | Code | Sprache | Stil |
|---|---|---|---|
| 1 | en | Englisch | Referenzquelle |
| 2 | es | Spanisch | Standard formal |
| 3 | hi | Hindi | Devanagari-Schrift |
| 4 | ru | Russisch | Standard formal |
| 5 | de | Deutsch | Standard formal |
| 6 | fr | Französisch | Standard formal |
| 7 | pt | Portugiesisch | Europäisches Portugiesisch |
| 11 | pl | Polnisch | Standard formal |
| 12 | it | Italienisch | Standard formal |
| 13 | uk | Ukrainisch | Standard formal |
| 14 | tr | Türkisch | Standard formal |
| 17 | ko | Koreanisch | 합니다/습니다-Form |
| 18 | zh | Chinesisch (Vereinfacht) | Vereinfachte Zeichen |
| — | zh-tw | Chinesisch (Traditionell) | Traditionelle Zeichen |
| 23 | nb | Norwegisch Bokmål | Standard Bokmål |
| — | be | Belarussisch | Standard Belarussisch |
| 34 | ja | Japanisch | です/ます-Form |
Die Spalte „Stil” ist wichtig. Japanisch und Koreanisch haben formelle Registeroptionen, die jeden Satz betreffen. Die Übersetzungsanweisung enthält diese Vorgaben, damit das Modell natürlichen, ausgefeilt formulierten Text produziert — keine steife Maschinenübersetzung.
Diese Reihenfolge bestimmt auch das Sprach-Dropdown in der Kopfzeile der Website. Die meistgesprochenen Schachsprachen erscheinen zuerst, sodass Benutzer ihre Sprache eher finden, ohne scrollen zu müssen.
Die Übersetzungs-Pipeline
Abschnitt betitelt „Die Übersetzungs-Pipeline“Schritt 1: Englisch schreiben
Abschnitt betitelt „Schritt 1: Englisch schreiben“Alle Dokumentation beginnt als englisches Markdown in src/content/docs/. Das Frontmatter enthält einen title und eine description:
---title: "Getting Started"description: "Install En Parlant~ and play your first game."---
Download the latest release...Schritt 2: Das Skript ausführen
Abschnitt betitelt „Schritt 2: Das Skript ausführen“Ein Python-Skript (scripts/translate-docs.py) liest jede englische Quelldatei, sendet sie an die Claude API und schreibt das übersetzte Markdown:
python3 scripts/translate-docs.py \ --anthropic-key $ANTHROPIC_API_KEY \ --model claude-opus-4-6 \ --workers 5Das Skript benötigt etwa 60–70 Minuten, um alle 28 Quelldateien in alle 16 Zielsprachen zu übersetzen (insgesamt 448 Dateien). Es führt 5 parallele API-Aufrufe durch, um innerhalb der Ratenlimits zu bleiben.
Für eine einzelne neue Sprache dauert es etwa 4 Minuten.
Schritt 3: Bauen
Abschnitt betitelt „Schritt 3: Bauen“pnpm buildAstro liest das Quell-Markdown, rendert es durch die Starlight-Templates und gibt statisches HTML in dist/ aus. Der Build dauert etwa 30 Sekunden für alle ~500 Seiten.
Schritt 4: Deployment
Abschnitt betitelt „Schritt 4: Deployment“pnpm run deployÜberträgt die gebaute Website auf Cloudflare Workers.
So funktioniert das Skript
Abschnitt betitelt „So funktioniert das Skript“Das Übersetzungsskript ist bewusst einfach gehalten — etwa 300 Zeilen Python. Hier ist der Ablauf:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐│ 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 filesDie Anweisung teilt Claude genau mit, was übersetzt und was beibehalten werden soll:
- Übersetzen: Fließtext, Überschriften, Tabellenbeschriftungen, Frontmatter-Titel und -Beschreibung
- Auf Englisch belassen: Codeblöcke, Inline-Code, Befehlsbeispiele, Dateipfade, URLs, Produktnamen, Personennamen, ASCII-Diagramme
Diese Trennung ist entscheidend. Ein Befehl wie pnpm build muss in jeder Sprache pnpm build bleiben. Ein Produktname wie „En Parlant~” oder „Stockfish” bleibt unübersetzt. Aber „Getting Started” wird im Japanischen zu „はじめに” und im Russischen zu „Начало работы”.
Warum Opus?
Abschnitt betitelt „Warum Opus?“Claude Opus 4.6 produziert spürbar bessere Übersetzungen als schnellere Modelle. Der Unterschied zeigt sich in:
- Natürliche Formulierung — Opus schreibt wie ein Muttersprachler, nicht wie ein Übersetzer. Es strukturiert Sätze um, wenn die englische Wortstellung in der Zielsprache unnatürlich klingen würde.
- Fachliche Genauigkeit — Schachterminologie, TTS-Fachbegriffe und Software-Konzepte werden mit den korrekten fachspezifischen Begriffen übersetzt.
- Konsistenz — Das formelle Register bleibt durchgehend einheitlich. Japanisch verwendet überall die です/ます-Form, ohne mitten im Absatz zwischen umgangssprachlich und höflich zu wechseln.
- MDX-Handling — Opus bewahrt JSX-Komponenten-Tags (
<Card>,<CardGrid>) undimport-Anweisungen in.mdx-Dateien korrekt, ohne sie zu beschädigen.
Der Kostenunterschied ist real — etwa 28 $ für eine vollständige Website-Übersetzung mit Opus gegenüber 5 $ mit Sonnet — aber für 448 Dateien, die Benutzer tatsächlich lesen werden, ist die Qualität es wert.
Die Locale-Routing-Architektur
Abschnitt betitelt „Die Locale-Routing-Architektur“Dies war der Teil, der am längsten gedauert hat, bis er richtig funktionierte.
Das Problem
Abschnitt betitelt „Das Problem“Starlight erkennt die Sprache einer Seite anhand des ersten Segments ihres Content-Slugs. Wenn es fr/docs/getting-started sieht, weiß es, dass das Französisch ist, weil fr das erste Segment ist. Aber die ursprüngliche Implementierung erzeugte Slugs wie docs/fr/getting-started — das Locale war unter docs/ vergraben. Starlight sah docs als erstes Segment, behandelte alles als Englisch und generierte über 7.000 doppelte Seiten statt ~500.
Die Lösung
Abschnitt betitelt „Die Lösung“Eine benutzerdefinierte generateId-Funktion in src/content.config.ts steuert, wie Dateipfade zu Content-Slugs werden:
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}`;}Dies setzt das Locale-Präfix vor docs/:
| Dateipfad | 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/ |
Englisch verwendet defaultLocale: "root", was bedeutet, dass es kein Präfix gibt — es lebt direkt unter /docs/, nicht unter /en/docs/.
Das localeKeys-Array
Abschnitt betitelt „Das localeKeys-Array“Ein localeKeys-Array in derselben Datei muss jedes nicht-englische Locale auflisten. Wenn ein Locale in Astros Konfiguration existiert, aber nicht in diesem Array, wird dessen übersetzter Inhalt als englischer Inhalt behandelt — der Sprachwechsler funktioniert nicht mehr und die Seitenzahlen explodieren.
Data-Store-Cache
Abschnitt betitelt „Data-Store-Cache“Astro speichert Slug-Zuordnungen in .astro/data-store.json im Cache. Nach jeder Änderung der Locale-Konfiguration muss diese Datei vor dem Neuaufbau gelöscht werden, da der Build sonst mit veralteten (falschen) Routing-Daten erfolgreich durchläuft.
Die Menüstruktur
Abschnitt betitelt „Die Menüstruktur“Die Seitenleiste wird in astro.config.mjs definiert. Einträge, die auf eine Seite verweisen (slug-Eigenschaft), verwenden automatisch den übersetzten Frontmatter-Titel der Seite — keine manuelle Übersetzung nötig:
{ slug: "docs/getting-started" }// English: "Getting Started" (from English frontmatter)// French: "Premiers pas" (from French frontmatter)// Japanese: "はじめに" (from Japanese frontmatter)Aber Gruppenbezeichnungen und externe Links benötigen explizite Übersetzungen:
{ 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" }, // ... ],}Sieben Seitenleistenelemente benötigen manuelle Übersetzungen: Welcome, Features, App Menus, Setup Guides, Under the Hood, Credits und Accessibility. Das Hinzufügen einer neuen Sprache bedeutet, jeweils einen Eintrag zu diesen sieben Blöcken hinzuzufügen.
Die App-Verbindung
Abschnitt betitelt „Die App-Verbindung“En Parlant~ verlinkt aus seinem Hilfe-Menü auf diese Dokumentation. Der Link ist locale-sensitiv — wenn Sie die App auf Französisch verwenden, öffnet sich die französische Dokumentation:
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]);Jede in der App verfügbare Sprache hat eine passende Übersetzung auf der Website. Die Zuordnung erfolgt automatisch — fr_FR wird auf /fr/docs/ abgebildet, ja_JP auf /ja/docs/. Der einzige Sonderfall ist Traditionelles Chinesisch: zh_TW wird auf /zh-tw/docs/ abgebildet (mit Bindestrich).
Warum es jetzt einfach ist
Abschnitt betitelt „Warum es jetzt einfach ist“Die schwierigen Teile sind erledigt:
-
Die Routing-Architektur ist gelöst. Die
generateId-Funktion, daslocaleKeys-Array und diedefaultLocale: "root"-Konfiguration arbeiten zusammen, damit Starlight die korrekte URL-Struktur generiert. Dies war der größte Schmerzpunkt — es erforderte die Analyse von über 6 Quelldateien in Starlight und Astro, um das Problem zu finden und zu beheben. -
Das Übersetzungsskript erledigt alles. Vollständige Neuübersetzung der Website, Hinzufügen einer einzelnen Sprache, Aktualisierung einzelner Dateien — alles mit demselben Skript und unterschiedlichen Flags. Es wiederholt bei Ratenlimits, parallelisiert über Worker und meldet Fehler klar.
-
Eine neue Sprache hinzuzufügen erfordert vier Konfigurationsänderungen und einen Befehl. Das Locale in
astro.config.mjs,content.config.tsundtranslate-docs.pyhinzufügen, Seitenleisten-Übersetzungen ergänzen, das Skript ausführen. Etwa 10 Minuten Arbeit plus 4 Minuten Übersetzungszeit. -
Inhalte aktualisieren ist noch einfacher. Die englische Quelle bearbeiten, das Skript mit
--filesund--overwritenur für die geänderten Dateien ausführen, neu bauen. Oder bei kleinen Textänderungen die übersetzten Dateien direkt bearbeiten. -
Die gesamte Pipeline ist in einem Skill festgehalten. Das Ausführen von
/translate_docsführt durch den gesamten Prozess — welcher Modus verwendet werden soll, welche Flags übergeben werden, Vorab-Prüfungen, Nachübersetzungs-Verifizierung. Kein institutionelles Wissen erforderlich.
Die Gesamtübersetzung von 448 Dateien in 16 Sprachen kostete etwa 28 $ und dauerte ungefähr 70 Minuten. Eine einzelne neue Sprache kostet etwa 1,70 $. Eine einzelne Datei, die in alle Sprachen neu übersetzt wird, kostet etwa 1 $. Das sind die laufenden Kosten, um die Dokumentation in 17 Sprachen aktuell zu halten.