iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
AI 自動化

用 AI Agent 打造你的產品使用手冊產線系列 第 23 篇

[Day 23] 多語言 2:翻譯同步

  • 分享至 

  • xImage
  •  

昨天讓同一份 manifest 產出了中英文兩份手冊,但那只是「出一份英文版」。手冊是會一直改的,真正麻煩的是之後的維護:中文正文改了一段,英文版怎麼知道要跟著改?

今天就來處理翻譯同步。

整份重翻不行嗎?

相信大家第一直覺肯定是就就是每次中文改版,就把整份英文重翻一次。

但是,這樣其實很浪費 token,而且更大的問題是,已經審過的譯文會被丟掉。英文版可能已經請母語人士看過、修過用詞,整份重翻就要全部重審一次。

請 AI Agent 只改有變的地方,不就好了?

用過 AI Agent 的人,應該馬上會想到另一個做法:把中文的 git diff 跟英文正文一起交給 agent,請它「只翻有改的地方,其他不要動」。

大部分時候,這樣確實會成功。而且在「找出英文對應的是哪一段」這件事上,agent 比今天要做的工具還強。它看得懂意思,就算英文把一段拆成兩段,也對得起來。

但只靠 agent,還少了三樣東西:

  1. 基準:「中文改了什麼」要跟某個版本比。上次英文同步時,中文是哪一版?英文檔案最後一次被修改,不代表當時已經跟中文同步了,說不定只是改了一個錯字。沒有紀錄,agent 也只能猜。
  2. 驗證:「其他不要動」是一個要求,不是保證。agent 偶爾會順手潤飾一段它覺得不通順的英文,而那段可能正好是母語人士審過的。這也是這個系列從 Day 01 就定下的判準:AI 的輸出,要能被審查,或被決定性的機制驗證。「請 agent 小心一點」兩者都不是。
  3. 提醒:agent 只在被叫的時候才會動。中文改了一段,得有人記得英文也要跟上。

所以今天要做的,不是取代 agent,而是替它補上這三樣東西:翻譯交給 agent (或人),工具負責記住基準、列出要翻的段落,並在翻完之後驗證其他段落沒有被動到。

每段算一個 hash

做法是把正文用空行切成段落,每段算一個 hash:

const text = buf.join('\n')
const hash = crypto.createHash('sha256').update(text).digest('hex').slice(0, 8)

算 hash 之前,會先統一換行符號、去掉行尾空白,避免只是排版不同,就被當成改了內容。

英文正文旁邊放一份對照紀錄 docs/en/50-camera-add.sync.json,記下「中文這段的 hash ↔ 英文那段的 hash」:

{
  "source": "docs/50-camera-add.md",
  "blocks": [
    { "source": "0b98ba91", "target": "78b6b4e5" },
    { "source": "72c25f6e", "target": "357f26f9" },
    ...
  ]
}

檢查時,拿現在每段中文的 hash 去紀錄裡找:

  • 找得到:這段翻過了,沿用。
  • 找不到:新增或改過的段落,需要翻譯。
  • 紀錄裡的英文 hash,在現在的英文正文裡找不到:有人直接改了譯文,需要人工確認。

重點是用 hash 比對,不用位置。如果用「第幾段」來對,中文在中間插入一段,後面所有段落都會錯位,全部變成「需要翻譯」。用 hash 比對的話,沒改的段落不管移到哪裡都認得出來。

另外,只有 {{screenshot:*}} 的段落不需要翻譯,中英文寫法本來就一樣,所以不會出現在「需要翻譯」的清單裡。

範例專案 auto-manual-gen 的 chore/day23 分支,新增了一個 sync 指令:

npm run sync -- --locale en                       # 列出每一章的同步狀態
npm run sync -- --locale en --accept camera-add   # 記下這一章目前的對照

--accept 負責寫入對照紀錄:第一次使用時,先確認現有的英文是對的,執行一次建立基準;之後每次翻好、審完,再執行一次更新紀錄。

另外,只要有任何一章沒有同步,sync 就會以非 0 的狀態結束,所以可以直接放進 CI,不用靠人記得去檢查。

實際跑一次

先對每一章執行 --accept 建立基準,接著在中文版「新增攝影機」做了三個改動:

  1. 在「完成後」插入一段新內容。
  2. 改寫最後一段「注意」。
  3. 另外直接潤飾了一段英文譯文,模擬 agent 順手改了不該改的段落。
$ npm run sync -- --locale en

✔ overview  9 段都已同步
✔ live-monitor  11 段都已同步
✔ layout-preset  12 段都已同步
✔ settings  11 段都已同步
✖ camera-add
    需要翻譯  第 27 行:新增的攝影機會出現在「攝影機清單」的最下方。
    需要翻譯  第 33 行:> 注意:「{{legend.name}}」或「{{legend.source}…
    需要確認  原文第 31 行對應的譯文被改過:「{{legend.zone}}」預設為「大門」,「{{legend.enabl…
    舊譯文    docs/en/50-camera-add.md:29:**{{legend.zone}}** defaults to **Main G…
    舊譯文    docs/en/50-camera-add.md:31:> Note: While **{{legend.name}}** is emp…

幾個觀察:

  • 中間插入了一段,但後面沒改的段落都沒有被誤判。這就是用 hash 而不用位置的好處。
  • 「舊譯文」是中文改掉之後,英文版裡已經沒有對應原文的段落。第二筆剛好就是那段「注意」原本的英文,翻譯的人可以拿來參考,只改差異的部分就好。
  • 被潤飾過的譯文抓出來了:原文沒變、譯文卻變了,就會列成「需要確認」。agent 如果越界,改到不該改的段落,就是這樣被發現的。
  • 不過它被報了兩次:一次是「需要確認」,一次是第一筆「舊譯文」。工具只看得到 hash,分不出「有人潤飾了譯文」跟「原文改掉後留下的舊譯文」,只能兩種都列出來,交給人判斷。

範例專案的 tools/samples/ 放了這三個改動的檔案,README 有重現步驟,執行紀錄在 tools/logs/day23-sync.txt。

這份清單就是交給 AI Agent 的範圍:只把「需要翻譯」的段落,連同對應的舊譯文、App 的 en.json 一起給它,翻出來的東西範圍小、好審。翻完之後再跑一次 sync,如果 agent 動到了其他段落,會出現在「需要確認」。

英文版補好這兩段、確認沒有動到其他段落,再執行 --accept,狀態就回到全部同步。

最大的限制:段落要一對一

hash 只能告訴我們「中文哪一段變了」,但沒辦法告訴我們「英文哪一段是它的翻譯」。這個對應關係,只能在 --accept 那一刻,靠「第 1 段對第 1 段、第 2 段對第 2 段」建立。

所以譯文的段落結構必須跟原文一模一樣,段落數對不上時,--accept 會拒絕記錄。這代表翻譯時不能把一段拆成兩段,也不能把兩段併成一段,而英文的句構偏偏常常需要這樣做。

這個限制對手冊來說還算可以接受:步驟本來就是一步一段,結構很固定。但段落的粒度也因此比較粗,像一整串編號步驟中間沒有空行,就算一段,改其中一個步驟,整串都要重翻。

另一種做法,是在英文正文的每一段前面加上 <!-- src:0b98ba91 --> 這樣的註解,直接標明它是哪一段中文的翻譯。對應關係跟內容綁在一起,就不需要一對一,也不會有「被報兩次」的問題。代價是正文變得很雜亂,翻譯的人 (或 AI) 也必須記得保留這些註解。我最後選擇讓正文保持乾淨。

小結

今天處理了多語言手冊的長期維護:

  • 整份重翻會丟掉審過的譯文;請 AI Agent 只改有變的地方可以,但還需要基準、驗證與提醒。
  • 翻譯交給 agent,工具負責這三件事:每段算一個 hash,用 hash 而不是位置比對,列出需要翻譯的段落,並抓出不該被改的譯文。
  • 第一次先用 --accept 建立基準,之後每次翻好、審完再更新一次,對照紀錄跟正文一起進版控。
  • 代價是譯文的段落結構要跟原文一對一,不能拆段或併段。

多語言的部分就到這裡告一段落,明天開始會開始新的主題~


上一篇
[Day 22] 多語言 1:同一份設定檔,產出中英文手冊
系列文
用 AI Agent 打造你的產品使用手冊產線 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言