翻譯系統
本網站以 17 種語言發布。不是靠一群翻譯人員,而是靠一個 Python 腳本、一個 Claude Opus 模型,以及一套精心設計的檔案結構,讓所有東西都能各就其位。以下是整個系統的運作方式。
文件存放在 src/content/docs/ 中。英文是根目錄——其他每種語言都完全鏡像它的結構:
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)每個翻譯檔案都是其英文來源的結構鏡像。相同的檔名、相同的子目錄路徑、相同的 frontmatter 鍵值。唯一的差異是正文內容是另一種語言。
為什麼鏡像結構很重要
Section titled “為什麼鏡像結構很重要”Starlight(文件框架)依賴這種對稱性。當使用者切換語言時,Starlight 會將 /docs/getting-started/ 替換為 /fr/docs/getting-started/——相同的路徑,不同的語系前綴。如果法文檔案不是精確地位於 fr/getting-started.md,語言切換器就會壞掉或默默回退到英文。
17 種語言
Section titled “17 種語言”語言按全球西洋棋玩家人口排序,資料來自 Lichess、Chess.com 和 FIDE 註冊數據。英文作為來源語言排在首位;其餘依照西洋棋排名排列:
| 排名 | 代碼 | 語言 | 風格 |
|---|---|---|---|
| 1 | en | 英文 | 真實來源 |
| 2 | es | 西班牙文 | 標準正式 |
| 3 | hi | 印地文 | 天城文字體 |
| 4 | ru | 俄文 | 標準正式 |
| 5 | de | 德文 | 標準正式 |
| 6 | fr | 法文 | 標準正式 |
| 7 | pt | 葡萄牙文 | 歐洲葡萄牙文 |
| 11 | pl | 波蘭文 | 標準正式 |
| 12 | it | 義大利文 | 標準正式 |
| 13 | uk | 烏克蘭文 | 標準正式 |
| 14 | tr | 土耳其文 | 標準正式 |
| 17 | ko | 韓文 | 합니다/습니다 形式 |
| 18 | zh | 中文(簡體) | 簡體字 |
| — | zh-tw | 中文(繁體) | 繁體字 |
| 23 | nb | 挪威語(書面語) | 標準 Bokmål |
| — | be | 白俄羅斯文 | 標準白俄羅斯文 |
| 34 | ja | 日文 | です/ます 形式 |
「風格」欄位很重要。日文和韓文有語體層級的選擇,會影響每一句話。翻譯提示詞包含這些指示,讓模型產出自然、精緻的文字——而不是僵硬的機器翻譯。
這個排序也控制了網站標題列的語言下拉選單。使用人數最多的西洋棋語言排在最前面,使用者更可能不需要捲動就找到自己的語言。
步驟一:撰寫英文
Section titled “步驟一:撰寫英文”所有文件都從 src/content/docs/ 中的英文 markdown 開始。Frontmatter 包含 title 和 description:
---title: "Getting Started"description: "Install En Parlant~ and play your first game."---
Download the latest release...步驟二:執行腳本
Section titled “步驟二:執行腳本”一個 Python 腳本(scripts/translate-docs.py)讀取每個英文來源檔案,將其傳送到 Claude API,然後寫出翻譯後的 markdown:
python3 scripts/translate-docs.py \ --anthropic-key $ANTHROPIC_API_KEY \ --model claude-opus-4-6 \ --workers 5腳本將全部 28 個來源檔案翻譯成所有 16 種目標語言(共 448 個檔案),大約需要 60–70 分鐘。它同時執行 5 個平行 API 呼叫,以控制在速率限制之內。
若只翻譯一種新語言,大約 4 分鐘。
步驟三:建構
Section titled “步驟三:建構”pnpm buildAstro 讀取來源 markdown,透過 Starlight 的模板渲染,並將靜態 HTML 輸出到 dist/。所有約 500 個頁面的建構大約需要 30 秒。
步驟四:部署
Section titled “步驟四:部署”pnpm run deploy將建構完成的網站推送到 Cloudflare Workers。
腳本的運作方式
Section titled “腳本的運作方式”翻譯腳本刻意保持簡單——大約 300 行 Python。以下是流程:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐│ 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 files提示詞明確告訴 Claude 什麼要翻譯、什麼要保持原樣:
- 翻譯: 正文、標題、表格標籤、frontmatter 的 title 和 description
- 保持英文: 程式碼區塊、行內程式碼、命令範例、檔案路徑、URL、產品名稱、人名、ASCII 圖表
這種區分至關重要。像 pnpm build 這樣的命令在每種語言中都必須保持 pnpm build。像「En Parlant~」或「Stockfish」這樣的產品名稱不翻譯。但「Getting Started」在日文中變成「はじめに」,在俄文中變成「Начало работы」。
為什麼選擇 Opus?
Section titled “為什麼選擇 Opus?”Claude Opus 4.6 產出的翻譯品質明顯優於較快的模型。差異體現在:
- 自然的措辭——Opus 寫起來像母語者,而非翻譯者。當英文的語序在目標語言中聽起來不自然時,它會重新組織句子結構。
- 技術準確度——西洋棋術語、TTS 專業用語和軟體概念都使用正確的領域特定詞彙來翻譯。
- 一致性——正式語體自始至終保持一致。日文全程使用 です/ます 形式,不會在段落中間切換正式與隨意語體。
- MDX 處理——Opus 能正確保留
.mdx檔案中的 JSX 元件標籤(<Card>、<CardGrid>)和import陳述式,不會破壞它們。
成本差異是真實的——使用 Opus 進行全站翻譯約 $28,而 Sonnet 約 $5——但對於使用者實際會閱讀的 448 個檔案而言,品質是值得的。
語系路由架構
Section titled “語系路由架構”這是花最長時間才搞定的部分。
Starlight 透過檢查內容 slug 的第一個區段來偵測頁面語言。當它看到 fr/docs/getting-started 時,因為 fr 是第一個區段,所以知道這是法文。但最初的實作產出的 slug 是 docs/fr/getting-started——語系被埋在 docs/ 底下。Starlight 將 docs 視為第一個區段,把所有東西都當成英文處理,結果產生了 7,000 多個重複頁面,而不是約 500 個。
src/content.config.ts 中的自訂 generateId 函式控制檔案路徑如何轉換為內容 slug:
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}`;}這會將語系前綴放在 docs/ 之前:
| 檔案路徑 | 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/ |
英文使用 defaultLocale: "root",意味著沒有前綴——它直接位於 /docs/,而不是 /en/docs/。
localeKeys 陣列
Section titled “localeKeys 陣列”同一檔案中的 localeKeys 陣列必須列出每個非英文語系。如果某個語系存在於 Astro 的設定中但不在這個陣列中,其翻譯內容會被當作英文內容處理——語言切換器會壞掉,頁面數量會暴增。
資料儲存快取
Section titled “資料儲存快取”Astro 會在 .astro/data-store.json 中快取 slug 對應關係。在任何語系設定變更後,必須先刪除此檔案再重新建構,否則建構會以過時(錯誤)的路由成功完成。
側邊欄定義在 astro.config.mjs 中。指向頁面的項目(slug 屬性)會自動使用翻譯頁面的 frontmatter 標題——不需要手動翻譯:
{ slug: "docs/getting-started" }// English: "Getting Started" (from English frontmatter)// French: "Premiers pas" (from French frontmatter)// Japanese: "はじめに" (from Japanese frontmatter)但群組標籤和外部連結需要明確的翻譯:
{ 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" }, // ... ],}有七個側邊欄元素需要手動翻譯:Welcome、Features、App Menus、Setup Guides、Under the Hood、Credits 和 Accessibility。新增一種語言意味著在這七個區塊中各新增一個條目。
與應用程式的連結
Section titled “與應用程式的連結”En Parlant~ 從其說明選單連結到這份文件。連結具有語系感知功能——如果你使用法文版的應用程式,它會開啟法文文件:
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]);應用程式中可用的每種語言在網站上都有對應的翻譯。對應是自動的——fr_FR 對應到 /fr/docs/,ja_JP 對應到 /ja/docs/。唯一的特殊情況是繁體中文:zh_TW 對應到 /zh-tw/docs/(使用連字號)。
為什麼現在很簡單
Section titled “為什麼現在很簡單”困難的部分已經完成:
-
路由架構已解決。
generateId函式、localeKeys陣列和defaultLocale: "root"設定協同運作,使 Starlight 產生正確的 URL 結構。這是最大的痛點——需要追蹤 Starlight 和 Astro 中 6 個以上的原始檔才找到並修復。 -
翻譯腳本處理一切。 全站重新翻譯、新增單一語言、更新個別檔案——全部使用同一個腳本配合不同的旗標。它會在遇到速率限制時自動重試、跨工作執行緒平行處理,並清楚地報告錯誤。
-
新增一種語言只需四處設定修改和一個命令。 將語系新增到
astro.config.mjs、content.config.ts和translate-docs.py,新增側邊欄翻譯,執行腳本。大約 10 分鐘的工作加上 4 分鐘的翻譯時間。 -
更新內容更加簡單。 編輯英文來源,使用
--files和--overwrite旗標只針對變更的檔案執行腳本,重新建構。或者對於小幅的文字修正,直接編輯翻譯檔案。 -
整個流程已被記錄為技能。 執行
/translate_docs會引導你走完整個過程——使用哪種模式、傳遞什麼旗標、飛行前檢查、翻譯後驗證。不需要任何隱性知識。
448 個檔案跨 16 種語言的完整翻譯花費約 $28,耗時約 70 分鐘。新增一種語言約花費 $1.70。將一個檔案重新翻譯成所有語言約花費 $1。這些就是將文件維持在 17 種語言版本的持續成本。