一篇文章 19 種語言:檔案結構、翻譯 prompt、5 個真實翻車點、如何一次 POST。
Anthropic 和 OpenAI 的官方文件只有英文。這是全球 AI 開發者內容生態很大的空白。how2claude 正在做的事就是填這個空白——每篇文章同時發 19 種語言:zh / en / zh-TW / ja / ko / es / pt-BR / id / vi / tr / ar / fr / de / it / ru / uk / pl / he / th。
讓人做這事不現實(一篇文章 20 個人工譯本,每篇成本幾千美金)。讓 Claude 做,邊際成本接近零——但直接甩一句「翻成 ja」過去會得到機翻味十足、作者聲音全無的稿子。本文是這事做對的完整流程:檔案結構、翻譯 prompt 怎麼寫、哪些地方 Claude 一定會翻車、怎麼把 19 種語言 + 每種語言的推文一次 POST 到 API。
這兩個詞差別很大。
翻譯是把 A 語言的意思搬到 B 語言——Google Translate 幹的事。結果讀起來「對」,但是「平」。
在地化是保持原文的語氣、節奏、專業度,用目標語言讀者的「技術部落格語氣」重寫。結果讀起來像母語寫的。
讓 Claude 做翻譯是 5 分鐘的事;讓 Claude 做在地化需要 prompt 裡明確:
how2claude 的 /write-article 命令裡給每種語言都寫了風格要求:
| 語言檔案 | 風格要求 |
|---|---|
ja.md |
技術部落格語氣,自然日語 |
ko.md |
技術部落格語氣,自然韓語 |
zh-TW.md |
台灣用語習慣,繁體字 |
ar.md |
標準阿拉伯語(現代書面語) |
id.md |
標準印尼語 |
看起來只是一句話的差別,但對 Claude 來說是關鍵——沒有這句它會預設生成「教科書譯本」。
每篇草稿一個目錄,20 個檔案:
docs/drafts/let-claude-translate-articles/
├── meta.json # 所有 19 種語言的 title + summary
├── zh.md # 中文(原文,最先寫)
├── en.md # 英文(第一批翻譯)
├── zh-TW.md
├── ja.md
├── ko.md
├── ...(其餘 14 種)
└── th.md
meta.json:
{
"category_slug": "use-cases",
"series_slug": "writing",
"free": true,
"title": {
"zh": "让 Claude 把一篇文章翻译成 19 种语言",
"zh-TW": "讓 Claude 把一篇文章翻譯成 19 種語言",
"en": "Letting Claude Translate One Article Into 19 Languages",
"ja": "Claude に 1 本の記事を 19 言語に翻訳させる",
"...": "..."
},
"summary": {
"zh-TW": "一篇文章 19 種語言:檔案結構、翻譯 prompt、5 個真實翻車點、如何一次 POST。",
"...": "..."
}
}
每個 .md 檔案第一行是 # 標題,後面是本文。title 不從 md 第一行取,從 meta.json 取——這樣每種語言的 title 都是經過專門潤色的短句,而不是文章裡那句可能已經被翻長了的標題。
兩種流程會走錯:
錯的:鏈式翻譯。zh → en → ja → ko → ...
問題是:ja 基於 en、ko 基於 ja,每跳一次都丟一點原意。到了 th(泰語)已經是四手資訊了。
對的:輻射式翻譯。zh 寫完定稿 → 基於 zh 一次性翻出 en / ja / ko / ar / id / ...
每種語言都直接從原文生成,不走中間跳板。
具體 prompt 結構(/write-article 裡封裝好了):
這是一篇關於 X 的技術部落格,中文原文:
[zh.md 全文]
請翻譯成以下語言,每種語言的要求:
- ja.md: 技術部落格語氣,自然日語
- ko.md: 技術部落格語氣,自然韓語
- ar.md: 標準阿拉伯語(現代書面語)
...
要求:
1. 保留作者的直接、專業、略帶自嘲的語氣
2. 程式碼區塊、命令、API 名、URL 不翻
3. 標題、小標題、段落行文允許重組
4. title 另出一條,要短、有鉤子,不是原標題的直譯
並行翻譯 18 種語言,Claude 一次 session 搞定。重要的一點:在同一個 session/同一個模型版本裡做——跨會話或跨模型 voice 會漂移。
X 給 CJK 字元(中、日、韓)更嚴格的計數。我讓 Claude 生成推文時給過這樣的要求:
第一次生成中文推文時,三條超了(160、164、152 字)。因為 Claude 把英文推文直譯過來——英文 260 字元翻成中文正好踩線。
規律:中文推文必須重寫不是翻譯。要求:140 字上限、要有鉤子、可以損失一點細節。
zh → zh-TW 最容易犯懶:丟給轉換器把簡體轉繁體。結果是字對了,詞沒對:
| zh | zh-TW(錯) | zh-TW(對) |
|---|---|---|
| 文件 | 文件 | 檔案 |
| 信息 | 信息 | 資訊 |
| 软件 | 軟件 | 軟體 |
| 视频 | 視頻 | 影片 |
讓 Claude 翻 zh-TW 時必須明確:「台灣用語習慣,繁體字」——兩句都要有。只說「繁體」會得到字轉換。
阿拉伯語、希伯來語從右往左寫。Rails 側要在 <html dir="rtl">,Tailwind 用 rtl: 變體。但翻譯本身也會踩坑:
unicode-bidi: isolate 不然程式碼會被「吸」到 RTL 方向裡، 代替 ,,用 ؟ 代替 ?。Claude 預設會用英文標點,要明確要求2026),但整個段落流向是右到左。這個瀏覽器會處理,但 Claude 寫的時候經常亂原文裡的口語(「一把梭」、「踩坑」、「翻車」)Claude 翻成日語韓語預設會變成書面表達(「一気に実装」「落とし穴」「失敗」)——意思對,但作者的語氣沒了。
修法:prompt 裡明確「保留原文的直接、略自嘲、不拘禮的技術部落格語氣,不要學術化」。即使這樣,某些語言(德語、俄語)還是會比原文「正」一點——語言本身有書面 bias。
程式碼裡的字串,有些是 UI 文字(要翻),有些是佔位符/變數名(不能翻):
t("pricing.page_title") # 不翻(i18n key)
"user_id" # 不翻(變數名)
"Monthly subscription" # 如果在文中作為範例展示給讀者看 → 不翻
和程式碼相關的字串,預設不翻比較安全。真要翻的是 i18n locale 檔案(config/locales/xx.yml),那是另一回事。
/publish-article 命令把草稿 POST 到 how2claude.com/api/articles:
{
"category_slug": "use-cases",
"series_slug": "writing",
"free": true,
"thread": true,
"title": { "zh": "...", "en": "...", "ja": "...", ... },
"summary": { "zh": "...", "en": "...", ... },
"content": { "zh": "<md>", "en": "<md>", ... },
"tweets": { "en": ["...", "..."], "zh": [...], ... }
}
一個請求 300KB 左右(19 種語言 × 約 15KB)。伺服器拆到資料庫:
articles 表,title / summary / content 是 jsonb,按 locale 存x_queue_tweets 表按 locale + account 分發,推文排期進佇列Tweet 只發給有連接 X 帳戶的 locale(目前是 en/zh/ja/ko/ar/id 6 個),沒連接帳戶的語言 tweets 傳 []。查帳戶:
Account.all.each { |a| puts "#{a.locale}: #{a.name}" }
# en: @how2claude
# zh: @howtoclaude
# ja: @how2claude_ja
# ko: @how2claude_ko
# ar: @how2claude_ar
# id: @how2claude_id
讓 Claude 把一篇文章翻譯成 19 種語言的完整清單:
dir="rtl" + Tailwind rtl: 變體。標點混排、程式碼區塊隔離要單獨處理。真正的瓶頸不是翻譯品質——Claude 4 級模型翻成日韓阿拉伯俄文的品質都接近母語。瓶頸是你肯不肯把一篇文章分發到 19 種語言的讀者面前。技術上 5 分鐘能跑完,內容上只要你願意同時服務 200 倍的人。