如何翻譯 Markdown 檔案而不破壞程式碼、連結或 Front Matter
Markdown 或 MDX 檔案中哪些內容該翻譯、哪些該保留,以及如何檢查結果。包含範例檔案、免費檢查工具,以及 9 次本機 LLM 測試。

翻譯 Markdown 檔案時,請翻譯散文內容,並保留機制部分不動:front matter 鍵、程式碼、URL、連結標籤、元件標籤和註腳標籤都必須逐位元組原樣返回。然後檢查三件即使仔細翻譯仍可能出錯的事:指向標題的連結、程式碼註解,以及在中文、日文或韓文中,粗體文字和數字範圍周圍的間距。
我們用本機模型將一個 73 行的 MDX 頁面翻譯成簡體中文九次,使用三種不同的提示詞,並用腳本檢查每個結果。人們通常擔心的部分在全部九次執行中都倖存下來:程式碼、行內程式碼、URL、表格、JSX 元件和工作清單。另外三件事出了問題。指向標題的連結在全部九次執行中都斷了。程式碼註解在全部六次傳送整個檔案的執行中都被翻譯了,包括三次提示詞明確表示不要碰程式碼的執行。而將語法藏在佔位符後面的提示詞——聽起來像是最安全的選項——在它的全部三次執行中都破壞了 front matter。
快速回答:什麼該翻譯,什麼該保留
| 檔案部分 | 我們範例中的例子 | 要翻譯嗎? |
|---|---|---|
| Front matter 鍵 | slug:, sidebar_position:, tags: | 否 |
| Front matter 值 | title: "Export Your Library" | 僅限面向讀者的值,例如 title 和 description |
| 匯入與匯出(MDX) | import Callout from '@site/src/components/Callout'; | 否 |
| 程式碼區塊 | shelfmark export --format epub | 否。註解和面向使用者的字串需另行決定。 |
| 行內程式碼 | library.json, --covers | 否 |
| 連結和圖片目標 | (./export-options.md), (https://www.w3.org/TR/epub-33/ "EPUB 3.3") | 否,但引號中的可選標題除外 |
| 連結文字和圖片替代文字 | [glossary section], ![Folder layout after an export] | 是 |
| 參考標籤和定義 | [project glossary][glossary], [glossary]: ./glossary.md#terms | 保留標籤 [glossary] 和 URL;翻譯可見文字 |
| 標題 | ## Keep one glossary for the whole book | 可以,但請先為它們指定固定 ID(見下文) |
| JSX 元件 | <Callout type="warning" title="Back up first"> | 保留標籤與屬性名稱;翻譯面向讀者的屬性值,例如 title |
| 提示標記 | :::tip 與 ::: | 否 |
| 腳註標籤 | [^sync] | 否 |
| MDX 註解 | {/* Translator note: ... */} | 選用。讀者永遠看不到它們。 |
其餘都是普通散文:段落、清單項目、表格儲存格與區塊引用。
我們測試了什麼
我們撰寫了 sample-source.mdx,這是一個虛構閱讀應用程式 Shelfmark 的文件頁面。它採用 Docusaurus 及類似文件網站所使用的 MDX 風格,包含上表中每種結構各一個範例,外加一個兩空格強制換行、一個粗體連結、一個以括號結尾的粗體片語、兩個以連接號書寫的數字範圍,以及一個指向標題「為整本書保留一份詞彙表」的頁內連結。
接著我們請一個本機模型以三種方式將其翻譯成簡體中文:
- 整個檔案。 貼上整個檔案,並要求回傳翻譯後的檔案。
- 加上指示。 同上,外加六條規則:保留 front matter 鍵、不改動程式碼內的任何內容、保留 URL 與參考定義、保留匯入、標籤與註解、保留腳註標籤與提示標記,並翻譯標題、連結文字、替代文字與
title屬性。 - 受保護。 將程式碼、行內程式碼、URL、標籤、註解、腳註標籤、提示標記與 front matter 行替換為編號佔位符,例如
⟦12⟧,翻譯其餘部分,然後將原始內容放回。title與description的值被保留為可翻譯狀態,而結果顯示,front matter 的---行與結尾引號也是如此。
模型為 LM Studio 中的 Qwen 3.6 27B(Q4_K_M),關閉思考功能,溫度設為 0.3,運行於配備 64 GB 記憶體的 Apple M4 Max 上。我們每種條件各執行三次,總共九次,時間為 2026 年 10 月 25 日。腳本為 run_llm_markdown_test.py,九份輸出皆未經編輯發布;README 列出了每個檔案。如果您從未設定過本機模型,我們的本機 LLM 翻譯指南涵蓋了相關設定。
這是一項小型測試,只涉及一個模型、一個語言對和一個頁面。它展示了哪些問題會發生以及如何捕捉它們。它並不對模型或提示進行排名。
結果:語法倖存;連結與決策則否
我們使用 check_markdown_translation.py 檢查每份輸出,並以 remark 和 MDX 編譯器渲染每一份。完整的檢查器輸出在 check-results.txt 中,而 results.csv 每次執行各有一列。
| 檢查 | 整個檔案 | 已指示 | 已保護 |
|---|---|---|---|
| 可編譯為 MDX | 3 之 3 | 3 之 3 | 3 之 3 |
| 程式碼、行內程式碼、URL、表格、標籤、任務清單皆未變更 | 3 之 3 | 3 之 3 | 3 之 3 |
| 粗體與斜體仍可正常呈現 | 3 之 3 | 3 之 3 | 3 之 3 |
| 前置資料完整無損 | 3 之 3 | 3 之 3 | 0 之 3 |
| 頁內連結仍指向其標題 | 0 之 3 | 0 之 3 | 0 之 3 |
| 程式碼註解保持原樣 | 0 之 3 | 0/3 | 3/3 |
提示框的 title 已翻譯 | 3/3 | 3/3 | 0/3 |
前幾行是好消息。當我們貼上整個檔案時,這個模型完整回傳了 Markdown 語法;我們在程式碼、URL 或表格中找不到任何需要修正的地方。最後四行才是問題所在,而且它們在建立網站時都不會顯示為錯誤。
1. 翻譯後的標題會破壞指向它們的連結
範例使用 [glossary section](#keep-one-glossary-for-the-whole-book) 連結到其詞彙表章節。每次執行都翻譯了標題(這是應該的),每次執行也都保留了連結目標(這也是應該的)。結果是連結指向一個已不存在的 ID。
大多數 Markdown 工具會根據標題文字產生標題 ID。Pandoc 的文件將識別碼描述為「基於標題文字」,而 Docusaurus 警告說你可能會想變更或翻譯標題,「而不更新現有 ID 以避免破壞連結」。一旦標題變成「为整本书保留一个术语表」,其產生的 ID 就是中文,而 #keep-one-glossary-for-the-whole-book 不會捲動到任何地方。頁面仍會建置,所以沒有任何東西會告訴你。
你也無法在翻譯後編輯連結來修正這個問題。在九次執行中,模型用四種不同方式寫了同一個標題,因此產生的 ID 會隨著每次重新翻譯而改變。修正應該在翻譯之前進行:為每個被連結的標題指定固定 ID。
- 在 Docusaurus MDX 中,使用
## Keep one glossary for the whole book {/* #keep-one-glossary */}。Docusaurus 指出較舊的{#id}形式「不再是有效的 MDX 語法」,而其write-heading-ids命令會為每個文件加上 ID。 - 在 Pandoc 以及遵循 PHP Markdown Extra 的 Markdown 處理器中,在標題結尾使用
{#keep-one-glossary}。
如此一來,ID 在每種語言中都保持不變,檢查器會將其視為連結目標。
2. 程式碼註解被翻譯,而可見字串卻沒有
範例中有一個 shell 註解和一個 YAML 註解:
library:
name: "My books" # shown in the sidebar
sort: title
所有六次傳送整個檔案的執行都翻譯了這兩個註解,包括三次提示中寫著「不要變更圍欄程式碼區塊內的任何內容」的執行。程式碼本身在全部九次執行中都未被更動。同時,註解說會顯示在側邊欄的 "My books" 在全部九次執行中都保持英文。模型做了與本地化頁面需求相反的事:它翻譯了給開發人員的註記,卻留下了讀者會看到的標籤。
這兩種選擇在任何情況下都不是錯的。對於讀者會將程式碼複製到自己專案的教學課程,翻譯註解有幫助。對於程式碼必須與儲存庫相符的參考文件,它們會讓頁面與來源不一致。為專案做一次決定,並手動檢查設定範例中的字串值,因為沒有工具能分辨顯示標籤和識別碼。基於這個原因,檢查器會將僅註解變更回報為警告。
3. 看似安全的方法破壞了 front matter
受保護的條件達成了它的承諾:全部 32 個佔位符在三次執行中都回來了,所以程式碼、URL 和標籤都完美還原。但我們的腳本把兩個小東西留在佔位符之外:開啟 front matter 的 --- 行,以及翻譯後標題和描述後的結束引號。模型在三次執行中都把它們丟掉了。檔案開頭變成這樣:
title: "导出您的书库
description: "在更换设备之前,请备份您的笔记和书籍。
slug: export-library
沒有開頭的 ---,Markdown 解析器就不再看到 front matter。它會把六行 YAML 加上結尾的 --- 讀成一個標題,所以頁面會以一個包含 YAML 的大標題開頭。它仍然可以編譯為 MDX。把 --- 放回去也不夠:YAML 解析器會拒絕那兩個未閉合的引號。
教訓不是要避免使用佔位符。而是:任何你暴露在外的內容都可能回來時已改變,而且 front matter 根本不應該作為頁面的一部分送給模型。解析它,只將面向讀者的值(title、description)作為獨立字串送出,並將翻譯寫回原始結構。這只是幾行程式碼,卻能消除整類問題。
中文、日文和韓文中的粗體文字與範圍
另外兩個問題從未在我們的九次執行中出現,但它們值得檢查,因為它們會無聲地破壞頁面,而且兩者都曾影響 BookTranslator 自己的部落格。該部落格以 MDX 撰寫並機器翻譯成 27 種語言,在一次排程翻譯執行中,兩篇韓文文章連續三次重試都未通過我們的結構檢查。原因就是下面兩種模式。
以標點結尾的粗體。 CommonMark 會根據 ** 兩側的字元來決定它能否開啟或關閉粗體。緊接在標點之後的結尾 **,其後必須是空格、標點或行尾;這就是右側翼規則。英文在該處總是有空格,中文、日文和韓文則往往沒有。因此 打开**设置 → 导出(测试版)**并选择 會顯示字面上的星號,因為粗體以全形括號結尾,且其後直接接上「并」。同一行若去掉括號就會呈現粗體,在 ** 後加一個空格也一樣。粗體連結也有同樣的問題,因為連結以 ) 結尾:点击**[导出](./export-options.md)**打开 會失敗,韓文的 **[API](./api.md)**는 也一樣。
我們的模型剛好在每個粗體詞組的兩側都放了空格,所以輸出能正確呈現。若編輯日後為了遵循中文排版慣例而移除這些空格,這個錯誤就會再次出現。你可以在 render-cases.md 中試遍全部七種變體。
使用波浪號的數字範圍。 中文和日文經常將範圍寫成 3~5。建構於 remark 之上的 GitHub Flavored Markdown 網站預設會將單一波浪號轉為刪除線:remark-gfm 的 singleTilde 選項除非你更改,否則為 true。同一段落中若有兩個範圍,例如 需要 3~5 分钟,生成 10~20 MB 的文件夹,兩者之間的文字就會被加上刪除線。我們的範例使用連接號,八次執行都保留了它,第九次則寫成 3 到 5。請要求使用連接號或以文字表示範圍,或在 remark 設定中將 singleTilde: false。
結構檢查器會標記這兩種模式。若要確認實際呈現的結果,render_check.mjs 會計算原文與譯文中的粗體、斜體、刪除線及殘留星號,並嘗試進行 MDX 編譯:
npm install @mdx-js/mdx@3 unified@11 remark-parse@11 remark-gfm@4
node render_check.mjs source.mdx translated.mdx
譯文中的粗體節點較少或字面星號較多,就表示有某些強調無法正確閉合。
經得起考驗的工作流程
-
先修正標題 ID。 為每個被連結的標題加上明確的 ID,語法依你的網站產生器支援的格式而定。在原文語言中做一次,並在翻譯任何內容之前提交。
-
將 front matter 移出頁面。 只翻譯
title和description這類讀者可見的值,以獨立字串處理,再寫回原本的 YAML。 -
決定程式碼政策。 註解是否翻譯;設定範例中的顯示字串是否翻譯。把它寫進你的翻譯詞彙表,與不可更動的產品名稱並列,例如我們範例中的 Shelfmark。
-
翻譯內文。 使用能解析 Markdown 的工具,或附上明確規則送出整頁。在我們的測試中,送出整頁能保持語法完整;若你使用佔位符,請保護完整的結構,不要讓任何部分只被保護一半。
-
執行結構檢查。
python3 check_markdown_translation.py source.mdx translated.mdx --keep Shelfmark它會比對 front matter 的鍵與值、import、程式碼區塊、行內程式碼、連結目標、參考定義、標題層級、頁內連結目標、JSX 標籤與屬性、MDX 註解、提示標記、註腳標籤、表格形狀、工作清單方塊、強制換行、無法閉合的強調、波浪號範圍,以及你要求保留的詞彙。它不評判翻譯品質。
-
針對中日韓目標語言檢查呈現結果,使用
render_check.mjs,並在標點之後的結尾**後加上空格。 -
建置網站並點擊連結。 以各語言開啟頁面,點擊每個頁內連結,並閱讀設定範例。建置成功只證明檔案能編譯。
對於準確性至關重要的文件,我們技術翻譯工作流程中的術語與審閱步驟可額外套用於此。
BookTranslator 的適用之處
BookTranslator 的 TXT 翻譯工具接受 .txt、.md 和 .mdx 檔案。根據產品頁面說明,它會在翻譯時保留段落、換行、特殊字元和文字結構,並將大型檔案分割成片段以進行情境感知翻譯,因此你可以上傳整本手冊或以 Markdown 撰寫的書籍,而不必分段貼上。
產品頁面並未說明對 front matter、程式碼區塊或 MDX 元件的特殊處理方式,我們也未為本文將此範例送交 BookTranslator 執行。無論你使用哪種工具,請在翻譯前完成步驟 1 到 3,並在翻譯後完成步驟 5 到 7。沒有固定 ID 的標題仍會失去連結,以波浪號撰寫的範圍仍會變成刪除線。如果你要以多種語言出版書籍,我們的多語言自助出版指南涵蓋了其餘的工作。
取得測試檔案
本次測試的所有內容皆可自由重複使用:
- sample-source.mdx:73 行的英文範例;
- check_markdown_translation.py:結構檢查器(僅使用 Python 標準函式庫);
- render_check.mjs 與 render-cases.md:呈現檢查與七個中日韓測試案例;
- run_llm_markdown_test.py:適用於任何 OpenAI 相容本機伺服器的三種提示條件;
- 在輸出資料夾中的九個輸出,以及 check-results.txt 與 results.csv。





