BookTranslator
BookTranslator

Cách dịch tệp Markdown mà không làm hỏng mã, liên kết hay front matter

Cần dịch những gì trong tệp Markdown hoặc MDX, bỏ qua những gì và kiểm tra kết quả ra sao. Kèm tệp mẫu, công cụ kiểm tra miễn phí và bài thử nghiệm LLM cục bộ 9 lần chạy.

Để dịch một tệp Markdown, hãy dịch phần văn xuôi và giữ nguyên phần máy móc: khóa front matter, mã, URL, nhãn liên kết, thẻ thành phần và nhãn chú thích cuối trang phải được trả về nguyên vẹn từng byte. Sau đó kiểm tra ba thứ mà ngay cả bản dịch cẩn thận vẫn có thể làm hỏng: liên kết đến tiêu đề, chú thích trong mã và, với tiếng Trung, tiếng Nhật hoặc tiếng Hàn, khoảng cách quanh chữ đậm và dải số.

Chúng tôi đã dịch một trang MDX dài 73 dòng sang tiếng Trung Giản thể chín lần bằng một mô hình cục bộ, sử dụng ba lời nhắc khác nhau và kiểm tra mọi kết quả bằng một tập lệnh. Những phần người ta thường lo lắng đều sống sót trong cả chín lần chạy: mã, mã nội tuyến, URL, bảng, thành phần JSX và danh sách công việc. Ba thứ khác lại bị lỗi. Liên kết đến một tiêu đề bị hỏng trong cả chín lần chạy. Chú thích trong mã bị dịch trong cả sáu lần chạy gửi toàn bộ tệp, bao gồm ba lần mà lời nhắc nói không được chạm vào mã. Và lời nhắc ẩn cú pháp sau các trình giữ chỗ, nghe có vẻ là lựa chọn an toàn nhất, lại làm hỏng front matter trong cả ba lần chạy của nó.

Trả lời nhanh: Dịch gì và bỏ qua gì

Phần của tệpVí dụ từ mẫu của chúng tôiCó dịch không?
Khóa front matterslug:, sidebar_position:, tags:Không
Giá trị front mattertitle: "Export Your Library"Chỉ những giá trị hướng đến người đọc, như title và description
Lệnh import và export (MDX)import Callout from '@site/src/components/Callout';Không
Khối mãshelfmark export --format epubKhông. Quyết định riêng về chú thích và chuỗi hướng đến người dùng.
Mã nội tuyếnlibrary.json, --coversKhông
Đích liên kết và hình ảnh(./export-options.md), (https://www.w3.org/TR/epub-33/ "EPUB 3.3")Không, trừ tiêu đề tùy chọn trong dấu ngoặc kép
Văn bản liên kết và văn bản thay thế của hình ảnh[glossary section], ![Folder layout after an export]Có
Nhãn và định nghĩa tham chiếu[project glossary][glossary], [glossary]: ./glossary.md#termsGiữ nguyên nhãn [glossary] và URL; dịch phần văn bản hiển thị
Tiêu đề## Keep one glossary for the whole bookCó, nhưng trước tiên hãy gán cho chúng ID cố định (xem bên dưới)
Thành phần JSX<Callout type="warning" title="Back up first">Giữ nguyên tên thẻ và thuộc tính; dịch các giá trị thuộc tính hướng đến người đọc như title
Dấu hiệu admonition:::tip và :::Không
Nhãn chú thích[^sync]Không
Chú thích MDX{/* Translator note: ... */}Tùy chọn. Người đọc không bao giờ thấy chúng.

Mọi thứ còn lại là văn xuôi thông thường: đoạn văn, mục danh sách, ô bảng và trích dẫn khối.

Những gì chúng tôi đã thử nghiệm

Chúng tôi đã viết sample-source.mdx, một trang tài liệu cho một ứng dụng đọc sách hư cấu có tên Shelfmark. Nó được viết theo phong cách MDX được Docusaurus và các trang tài liệu tương tự sử dụng, với một ví dụ cho mỗi cấu trúc trong bảng trên, cộng thêm một ngắt dòng cứng hai dấu cách, một liên kết in đậm, một cụm từ in đậm kết thúc bằng dấu ngoặc đơn, hai khoảng số được viết bằng dấu gạch ngang en, và một liên kết trong trang đến tiêu đề "Keep one glossary for the whole book."

Sau đó, chúng tôi yêu cầu một mô hình cục bộ dịch nó sang tiếng Trung giản thể theo ba cách:

  1. Toàn bộ tệp. Dán toàn bộ tệp và yêu cầu trả lại tệp đã dịch.
  2. Có hướng dẫn. Tương tự, cộng thêm sáu quy tắc: giữ nguyên khóa front matter, không thay đổi bất cứ thứ gì bên trong mã, giữ nguyên URL và định nghĩa tham chiếu, giữ nguyên import, thẻ và chú thích, giữ nguyên nhãn chú thích và dấu hiệu admonition, và dịch tiêu đề, văn bản liên kết, văn bản thay thế và thuộc tính title.
  3. Được bảo vệ. Thay thế mã, mã nội tuyến, URL, thẻ, chú thích, nhãn chú thích, dấu hiệu admonition và các dòng front matter bằng các placeholder được đánh số như ⟦12⟧, dịch phần còn lại, sau đó đặt lại bản gốc. Các giá trị title và description được để lộ để dịch, và hóa ra, các dòng --- của front matter và dấu ngoặc kép đóng cũng vậy.

Mô hình là Qwen 3.6 27B (Q4_K_M) trong LM Studio, với chế độ suy nghĩ tắt và nhiệt độ 0.3, trên Apple M4 Max với 64 GB bộ nhớ. Chúng tôi đã chạy mỗi điều kiện ba lần, tổng cộng chín lần chạy, vào ngày 25 tháng 10 năm 2026. Tập lệnh là run_llm_markdown_test.py, và tất cả chín đầu ra được công bố không chỉnh sửa; README liệt kê mọi tệp. Nếu bạn chưa từng thiết lập một mô hình cục bộ, hướng dẫn dịch bằng LLM cục bộ của chúng tôi sẽ đề cập đến các cài đặt.

Đây là một thử nghiệm nhỏ với một mô hình, một cặp ngôn ngữ và một trang. Nó cho thấy những vấn đề nào xảy ra và cách phát hiện chúng. Nó không xếp hạng các mô hình hay lời nhắc.

Kết quả: Cú pháp sống sót; các liên kết và quyết định thì không

Chúng tôi đã kiểm tra mọi đầu ra bằng check_markdown_translation.py và hiển thị từng đầu ra bằng remark và trình biên dịch MDX. Kết quả đầy đủ của trình kiểm tra có trong check-results.txt, và results.csv có một hàng cho mỗi lần chạy.

Kiểm traToàn bộ tệpĐã hướng dẫnĐược bảo vệ
Biên dịch dưới dạng MDX3 trên 33 trên 33 trên 3
Mã, mã nội tuyến, URL, bảng, thẻ, danh sách tác vụ không thay đổi3 trên 33 trên 33 trên 3
Chữ đậm và chữ nghiêng vẫn hiển thị3 trên 33 trên 33 trên 3
Phần front matter còn nguyên vẹn3 trên 33 trên 30 trên 3
Liên kết trong trang vẫn trỏ đến tiêu đề của nó0 trên 30 trên 30 trên 3
Chú thích trong mã được giữ nguyên như đã viết0 trên 30 trên 33 trên 3
Đã dịch title của Callout3 trên 33 trên 30 trên 3

Những hàng đầu tiên là tin tốt. Mô hình này trả về cú pháp Markdown nguyên vẹn khi chúng tôi dán toàn bộ tệp; chúng tôi không tìm thấy lỗi nào cần sửa trong mã, URL hoặc bảng. Bốn hàng cuối là nơi cần xử lý, và không hàng nào trong số đó hiển thị dưới dạng lỗi khi bạn build trang.

1. Tiêu đề đã dịch làm hỏng liên kết đến chúng

Mẫu liên kết đến phần thuật ngữ của nó bằng [glossary section](#keep-one-glossary-for-the-whole-book). Mọi lần chạy đều dịch tiêu đề, đúng như mong đợi, và mọi lần chạy đều giữ nguyên đích liên kết, cũng đúng như mong đợi. Kết quả là một liên kết đến ID không còn tồn tại.

Hầu hết các công cụ Markdown tạo ID tiêu đề từ văn bản tiêu đề. Tài liệu của Pandoc mô tả định danh là "dựa trên văn bản tiêu đề", và Docusaurus cảnh báo rằng bạn có thể muốn thay đổi hoặc dịch một tiêu đề "mà không cập nhật ID hiện có để tránh làm hỏng liên kết". Khi tiêu đề đọc là 为整本书保留一个术语表, ID được tạo của nó là tiếng Trung, và #keep-one-glossary-for-the-whole-book không cuộn đến đâu cả. Trang vẫn build, nên không có gì báo cho bạn biết.

Bạn cũng không thể sửa điều này bằng cách chỉnh sửa liên kết sau khi dịch. Qua chín lần chạy, mô hình đã viết cùng một tiêu đề theo bốn cách khác nhau, vì vậy ID được tạo sẽ lại thay đổi mỗi lần dịch lại. Cách khắc phục nằm trước khi dịch: gán cho mỗi tiêu đề được liên kết một ID cố định.

  • Trong Docusaurus MDX, hãy dùng ## Keep one glossary for the whole book {/* #keep-one-glossary */}. Docusaurus lưu ý rằng dạng {#id} cũ "không còn là cú pháp MDX hợp lệ nữa", và lệnh write-heading-ids của nó sẽ thêm ID vào mọi tài liệu cho bạn.
  • Trong Pandoc và trong các bộ xử lý Markdown tuân theo PHP Markdown Extra, hãy dùng {#keep-one-glossary} ở cuối tiêu đề.

ID sau đó giữ nguyên trong mọi ngôn ngữ, và trình kiểm tra coi nó là đích liên kết.

2. Chú thích mã bị dịch, còn chuỗi hiển thị thì không

Mẫu có một chú thích shell và một chú thích YAML:

library:
  name: "My books"   # shown in the sidebar
  sort: title

Cả sáu lần chạy gửi toàn bộ tệp đều dịch cả hai chú thích, kể cả ba lần mà lời nhắc nói "Không thay đổi bất cứ thứ gì bên trong khối mã có rào". Bản thân mã không bị động đến trong cả chín lần chạy. Trong khi đó "My books", mà chú thích nói là hiển thị trong thanh bên, vẫn giữ nguyên tiếng Anh trong cả chín lần chạy. Mô hình đã làm ngược lại với điều một trang bản địa hóa cần: nó dịch ghi chú dành cho nhà phát triển và để nguyên nhãn mà người đọc sẽ thấy.

Không lựa chọn nào là sai trong mọi trường hợp. Đối với một hướng dẫn mà người đọc sẽ sao chép mã vào dự án của riêng họ, chú thích được dịch sẽ hữu ích. Đối với tài liệu tham khảo mà mã phải khớp với một kho lưu trữ, chúng khiến trang không nhất quán với nguồn. Hãy đưa ra quyết định một lần cho dự án, và xem xét thủ công các giá trị chuỗi trong ví dụ cấu hình, vì không công cụ nào có thể phân biệt nhãn hiển thị với định danh. Trình kiểm tra báo cáo các thay đổi chỉ trong chú thích dưới dạng cảnh báo vì lý do này.

3. Phương pháp trông có vẻ an toàn lại làm hỏng front matter

Điều kiện được bảo vệ đã làm đúng như nó hứa: cả 32 placeholder đều trở về trong cả ba lần chạy, nên mã, URL và thẻ đã được khôi phục hoàn hảo. Nhưng tập lệnh của chúng tôi đã để hai thứ nhỏ bên ngoài placeholder: dòng --- mở đầu front matter, và dấu ngoặc kép đóng sau tiêu đề và mô tả đã dịch. Mô hình đã bỏ tất cả chúng trong cả ba lần chạy. Tệp bắt đầu như thế này:

title: "导出您的书库
description: "在更换设备之前,请备份您的笔记和书籍。
slug: export-library

Không có --- mở đầu, trình phân tích cú pháp Markdown không còn thấy front matter. Nó đọc sáu dòng YAML cộng với --- đóng như một tiêu đề, vì vậy trang sẽ mở đầu bằng một tiêu đề lớn chứa YAML. Nó vẫn biên dịch được dưới dạng MDX. Đặt lại --- cũng không đủ: trình phân tích cú pháp YAML từ chối hai dấu ngoặc kép chưa đóng.

Bài học không phải là tránh placeholder. Mà là bất cứ thứ gì bạn để lộ ra đều có thể trở về đã thay đổi, và front matter không nên được gửi đến mô hình như một phần của trang. Hãy phân tích cú pháp nó, chỉ gửi các giá trị hướng đến người đọc (title, description) dưới dạng chuỗi riêng biệt, và ghi các bản dịch trở lại cấu trúc gốc. Đó là vài dòng mã và loại bỏ toàn bộ nhóm vấn đề này.

Văn bản in đậm và phạm vi trong tiếng Trung, tiếng Nhật và tiếng Hàn

Hai vấn đề nữa chưa từng xuất hiện trong chín lần chạy của chúng tôi, nhưng đáng để kiểm tra vì chúng làm hỏng trang một cách âm thầm, và cả hai đều đã ảnh hưởng đến blog của chính BookTranslator. Blog được viết bằng MDX và dịch máy sang 27 ngôn ngữ, và trong một lần chạy dịch theo lịch trình, hai bài viết tiếng Hàn đã trượt kiểm tra cấu trúc của chúng tôi ba lần thử liên tiếp. Nguyên nhân là hai mẫu dưới đây.

In đậm kết thúc bằng dấu câu. CommonMark quyết định liệu ** có thể mở hay đóng in đậm bằng cách xem các ký tự ở hai bên. Một ** đóng ngay sau dấu câu phải được theo sau bởi dấu cách, dấu câu hoặc kết thúc dòng; đây là quy tắc right-flanking. Tiếng Anh luôn có dấu cách ở đó. Tiếng Trung, tiếng Nhật và tiếng Hàn thường không có. Vì vậy 打开**设置 → 导出(测试版)**并选择 hiển thị dấu hoa thị theo nghĩa đen, bởi vì phần in đậm kết thúc bằng dấu ngoặc đơn toàn khổ và được theo sau trực tiếp bởi 并. Cùng dòng đó mà không có dấu ngoặc đơn sẽ hiển thị in đậm, và dòng có dấu cách sau ** cũng vậy. Một liên kết in đậm gặp vấn đề tương tự, vì liên kết kết thúc bằng ): 点击**[导出](./export-options.md)**打开 thất bại, và tiếng Hàn **[API](./api.md)**는 cũng vậy.

Mô hình của chúng tôi tình cờ đặt dấu cách ở cả hai bên của mọi cụm từ in đậm, nên đầu ra của nó hiển thị chính xác. Một biên tập viên sau đó xóa những dấu cách đó để tuân theo quy ước sắp chữ tiếng Trung sẽ khiến lỗi quay trở lại. Bạn có thể thử tất cả bảy biến thể trong render-cases.md.

Phạm vi số với dấu ngã. Văn bản tiếng Trung và tiếng Nhật thường viết phạm vi là 3~5. Các trang GitHub Flavored Markdown được xây dựng trên remark biến dấu ngã đơn thành gạch ngang theo mặc định: tùy chọn singleTilde của remark-gfm là true trừ khi bạn thay đổi nó. Hai phạm vi trong một đoạn văn, chẳng hạn như 需要 3~5 分钟,生成 10~20 MB 的文件夹, hiển thị với phần văn bản ở giữa bị gạch ngang. Mẫu của chúng tôi sử dụng dấu gạch ngang en, và tám lần chạy giữ nguyên chúng trong khi lần thứ chín viết 3 到 5. Hãy yêu cầu dấu gạch ngang en hoặc từ ngữ trong phạm vi, hoặc đặt singleTilde: false trong cấu hình remark của bạn.

Trình kiểm tra cấu trúc đánh dấu cả hai mẫu. Để xác nhận những gì thực sự hiển thị, render_check.mjs đếm in đậm, in nghiêng, gạch ngang và dấu hoa thị còn sót lại trong nguồn và bản dịch, đồng thời thử biên dịch MDX:

npm install @mdx-js/mdx@3 unified@11 remark-parse@11 remark-gfm@4
node render_check.mjs source.mdx translated.mdx

Ít nút in đậm hơn hoặc nhiều dấu hoa thị theo nghĩa đen hơn trong bản dịch có nghĩa là một số phần nhấn mạnh đã ngừng đóng.

Một quy trình làm việc vững chắc

  1. Sửa ID tiêu đề trước. Thêm ID rõ ràng cho mọi tiêu đề mà có thứ gì đó liên kết đến, theo cú pháp mà trình tạo trang của bạn hỗ trợ. Làm điều đó bằng ngôn ngữ nguồn, một lần, và commit trước khi bất cứ thứ gì được dịch.

  2. Đưa front matter ra khỏi trang. Chỉ dịch các giá trị hướng đến người đọc như title và description, dưới dạng chuỗi riêng biệt, và ghi chúng trở lại YAML gốc.

  3. Quyết định chính sách mã. Chú thích được dịch hay không; chuỗi hiển thị trong ví dụ cấu hình được dịch hay không. Ghi lại trong bảng thuật ngữ dịch của bạn bên cạnh các tên sản phẩm không được thay đổi, chẳng hạn như Shelfmark trong mẫu của chúng tôi.

  4. Dịch phần thân. Sử dụng công cụ phân tích cú pháp Markdown, hoặc gửi toàn bộ trang với các quy tắc rõ ràng. Trong thử nghiệm của chúng tôi, gửi toàn bộ trang giữ nguyên cú pháp; nếu bạn sử dụng trình giữ chỗ, hãy bảo vệ các cấu trúc hoàn chỉnh và không để lộ bất cứ thứ gì một nửa.

  5. Chạy kiểm tra cấu trúc.

    python3 check_markdown_translation.py source.mdx translated.mdx --keep Shelfmark
    

    Nó so sánh khóa và giá trị front matter, import, khối mã, mã nội tuyến, đích liên kết, định nghĩa tham chiếu, cấp tiêu đề, đích liên kết trong trang, thẻ và thuộc tính JSX, chú thích MDX, dấu hiệu admonition, nhãn chú thích cuối trang, hình dạng bảng, hộp danh sách nhiệm vụ, ngắt dòng cứng, nhấn mạnh không thể đóng, phạm vi dấu ngã và các thuật ngữ bạn yêu cầu giữ nguyên. Nó không đánh giá chất lượng bản dịch.

  6. Kiểm tra hiển thị cho các mục tiêu CJK bằng render_check.mjs, và thêm dấu cách sau bất kỳ ** đóng nào theo sau dấu câu.

  7. Xây dựng trang và nhấp vào các liên kết. Mở trang bằng từng ngôn ngữ, nhấp vào mọi liên kết trong trang và đọc các ví dụ cấu hình. Một bản dựng thành công chỉ chứng minh rằng tệp biên dịch được.

Đối với tài liệu nơi độ chính xác quan trọng, các bước thuật ngữ và đánh giá trong quy trình dịch thuật kỹ thuật của chúng tôi sẽ áp dụng thêm vào đây.

BookTranslator phù hợp ở đâu

Trình dịch TXT của BookTranslator chấp nhận các tệp .txt, .md và .mdx. Theo trang sản phẩm, nó dịch chúng trong khi giữ nguyên đoạn văn, ngắt dòng, ký tự đặc biệt và cấu trúc văn bản, đồng thời chia nhỏ các tệp lớn thành các phân đoạn để dịch nhận biết ngữ cảnh, vì vậy bạn có thể tải lên toàn bộ hướng dẫn hoặc một cuốn sách viết bằng Markdown thay vì dán từng phần.

Trang sản phẩm không mô tả xử lý đặc biệt cho front matter, khối mã hoặc thành phần MDX, và chúng tôi không chạy mẫu này qua BookTranslator cho bài viết này. Dù bạn sử dụng công cụ nào, hãy thực hiện các bước 1 đến 3 trước khi dịch và các bước 5 đến 7 sau đó. Một tiêu đề không có ID cố định vẫn mất liên kết, và một phạm vi viết bằng dấu ngã vẫn biến thành gạch ngang. Nếu bạn đang xuất bản một cuốn sách bằng nhiều ngôn ngữ, hướng dẫn tự xuất bản bằng nhiều ngôn ngữ của chúng tôi bao gồm phần còn lại của công việc đó.

Tải các tệp kiểm tra

Mọi thứ từ thử nghiệm này đều miễn phí để sử dụng lại:

Bài viết liên quan