BookTranslator
BookTranslator

Come tradurre file Markdown senza rompere codice, link o front matter

Cosa tradurre in un file Markdown o MDX, cosa lasciare intatto e come verificare il risultato. Con un file di esempio, un checker gratuito e un test su un LLM locale eseguito 9 volte.

BookTranslator

BookTranslator Team

15 min read

Per tradurre un file Markdown, traduci il testo e lascia stare la parte tecnica: le chiavi del front matter, il codice, gli URL, le etichette dei link, i tag dei componenti e le etichette delle note devono rimanere identici byte per byte. Poi controlla tre cose che anche una traduzione accurata può rompere: i link alle intestazioni, i commenti nel codice e, in cinese, giapponese o coreano, la spaziatura attorno al testo in grassetto e agli intervalli numerici.

Abbiamo tradotto una pagina MDX di 73 righe in cinese semplificato nove volte con un modello locale, usando tre prompt diversi, e abbiamo verificato ogni risultato con uno script. Le parti che di solito preoccupano sono sopravvissute in tutte e nove le esecuzioni: codice, codice inline, URL, la tabella, il componente JSX e la lista di attività. Altre tre cose sono andate storte. Il link a un'intestazione si è rotto in tutte e nove le esecuzioni. I commenti nel codice sono stati tradotti in tutte e sei le esecuzioni che inviavano l'intero file, comprese tre in cui il prompt diceva di non toccare il codice. E il prompt che nascondeva la sintassi dietro segnaposto, che sembra l'opzione più sicura, ha rotto il front matter in tutte e tre le sue esecuzioni.

Risposta rapida: cosa tradurre e cosa lasciare intatto

Parte del fileEsempio dal nostro campioneTradurlo?
Chiavi del front matterslug:, sidebar_position:, tags:No
Valori del front mattertitle: "Export Your Library"Solo quelli destinati al lettore, come title e description
Import ed export (MDX)import Callout from '@site/src/components/Callout';No
Blocchi di codiceshelfmark export --format epubNo. Decidi separatamente per commenti e stringhe visibili all'utente.
Codice inlinelibrary.json, --coversNo
Destinazioni di link e immagini(./export-options.md), (https://www.w3.org/TR/epub-33/ "EPUB 3.3")No, tranne il titolo opzionale tra virgolette
Testo dei link e testo alternativo delle immagini[glossary section], ![Folder layout after an export]Sì
Etichette e definizioni di riferimento[project glossary][glossary], [glossary]: ./glossary.md#termsMantieni l'etichetta [glossary] e l'URL; traduci il testo visibile
Titoli## Keep one glossary for the whole bookSì, ma prima assegna loro ID fissi (vedi sotto)
Componenti JSX<Callout type="warning" title="Back up first">Mantieni i nomi dei tag e degli attributi; traduci i valori degli attributi visibili al lettore come title
Marcatori di avviso:::tip e :::No
Etichette delle note a piè di pagina[^sync]No
Commenti MDX{/* Translator note: ... */}Facoltativo. I lettori non li vedono mai.

Tutto il resto è prosa ordinaria: paragrafi, voci di elenco, celle di tabella e citazioni a blocco.

Cosa abbiamo testato

Abbiamo scritto sample-source.mdx, una pagina di documentazione per un'app di lettura immaginaria chiamata Shelfmark. È scritta nello stile MDX usato da Docusaurus e da siti di documentazione simili, con un esempio di ciascun costrutto nella tabella sopra, più un'interruzione di riga forzata con due spazi, un link in grassetto, una frase in grassetto che termina con una parentesi, due intervalli numerici scritti con trattini en e un link interno alla pagina verso il titolo "Keep one glossary for the whole book."

Poi abbiamo chiesto a un modello locale di tradurlo in cinese semplificato in tre modi:

  1. File intero. Incolla l'intero file e chiedi indietro il file tradotto.
  2. Con istruzioni. Lo stesso, più sei regole: mantieni le chiavi del front matter, non modificare nulla all'interno del codice, mantieni URL e definizioni di riferimento, mantieni import, tag e commenti, mantieni le etichette delle note a piè di pagina e i marcatori di avviso, e traduci titoli, testo dei link, testo alternativo e l'attributo title.
  3. Protetto. Sostituisci codice, codice inline, URL, tag, commenti, etichette delle note a piè di pagina, marcatori di avviso e righe del front matter con segnaposto numerati come ⟦12⟧, traduci il resto, poi reinserisci gli originali. I valori di title e description sono stati lasciati esposti per la traduzione, e così, come si è scoperto, sono state anche le righe --- del front matter e le virgolette di chiusura.

Il modello era Qwen 3.6 27B (Q4_K_M) in LM Studio, con il thinking disattivato e temperatura 0.3, su un Apple M4 Max con 64 GB di memoria. Abbiamo eseguito ogni condizione tre volte, nove esecuzioni in totale, il 25 ottobre 2026. Lo script è run_llm_markdown_test.py, e tutti e nove gli output sono pubblicati senza modifiche; il README elenca ogni file. Se non hai mai configurato un modello locale, la nostra guida alla traduzione con LLM locale copre le impostazioni.

Questo è stato un piccolo test su un modello, una coppia di lingue e una pagina. Mostra quali problemi si verificano e come individuarli. Non classifica modelli o prompt.

Abbiamo controllato ogni output con check_markdown_translation.py e ne abbiamo renderizzato ciascuno con remark e il compilatore MDX. L'output completo del checker è in check-results.txt, e results.csv ha una riga per esecuzione.

ControlloFile interoIstruitoProtetto
Si compila come MDX3 su 33 su 33 su 3
Codice, codice inline, URL, tabella, tag e task list invariati3 su 33 su 33 su 3
Grassetto e corsivo ancora visualizzati3 su 33 su 33 su 3
Front matter intatto3 su 33 su 30 su 3
Il link interno punta ancora alla sua intestazione0 su 30 su 30 su 3
Commenti nel codice lasciati come scritti0 su 30 di 33 di 3
Callout title tradotto3 di 33 di 30 di 3

Le prime righe sono la buona notizia. Questo modello ha restituito la sintassi Markdown intatta quando abbiamo incollato l'intero file; non abbiamo trovato nulla da correggere in codice, URL o tabelle. Le ultime quattro righe sono il punto critico, e nessuna di esse si presenta come errore durante la build del sito.

L'esempio rimanda alla sua sezione glossario con [glossary section](#keep-one-glossary-for-the-whole-book). Ogni esecuzione ha tradotto l'intestazione, come dovrebbe, e ogni esecuzione ha mantenuto il target del link, come dovrebbe. Il risultato è un link a un ID che non esiste più.

La maggior parte degli strumenti Markdown genera gli ID delle intestazioni dal testo dell'intestazione. La documentazione di Pandoc descrive l'identificatore come "basato sul testo dell'intestazione", e Docusaurus avverte che potresti voler cambiare o tradurre un'intestazione "senza aggiornare l'ID esistente per evitare di rompere i link". Una volta che l'intestazione recita 为整本书保留一个术语表, il suo ID generato è in cinese, e #keep-one-glossary-for-the-whole-book non porta da nessuna parte. La pagina viene comunque compilata, quindi nulla te lo segnala.

Non puoi risolvere modificando il link dopo la traduzione. In nove esecuzioni il modello ha scritto la stessa intestazione in quattro modi diversi, quindi l'ID generato cambierebbe di nuovo a ogni ritraduzione. La soluzione va applicata prima della traduzione: assegna a ogni intestazione collegata un ID fisso.

  • In Docusaurus MDX, usa ## Keep one glossary for the whole book {/* #keep-one-glossary */}. Docusaurus nota che la forma più vecchia {#id} "non è più sintassi MDX valida", e il suo comando write-heading-ids aggiunge gli ID a ogni documento per te.
  • In Pandoc e nei processori Markdown che seguono PHP Markdown Extra, usa {#keep-one-glossary} alla fine dell'intestazione.

L'ID rimane quindi lo stesso in ogni lingua, e il checker lo tratta come target del link.

2. I commenti nel codice vengono tradotti, e la stringa visibile no

L'esempio ha un commento shell e un commento YAML:

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

Tutte e sei le esecuzioni che hanno inviato l'intero file hanno tradotto entrambi i commenti, comprese le tre in cui il prompt diceva "Non cambiare nulla all'interno dei blocchi di codice recintati". Il codice stesso è rimasto intatto in tutte e nove le esecuzioni. Nel frattempo "My books", che il commento dice essere mostrato nella barra laterale, è rimasto in inglese in tutte e nove le esecuzioni. Il modello ha fatto l'opposto di ciò che serve a una pagina localizzata: ha tradotto la nota per gli sviluppatori e lasciato l'etichetta che i lettori vedranno.

Nessuna delle due scelte è sbagliata in ogni caso. Per un tutorial i cui lettori copieranno il codice nel proprio progetto, i commenti tradotti aiutano. Per una documentazione di riferimento il cui codice deve corrispondere a un repository, fanno sì che la pagina non sia più coerente con la fonte. Prendi la decisione una volta per il progetto, e controlla a mano i valori stringa negli esempi di configurazione, perché nessuno strumento può distinguere un'etichetta visualizzata da un identificatore. Per questo il checker segnala le modifiche ai soli commenti come avvisi.

3. Il metodo che sembrava sicuro ha rotto il front matter

La condizione protetta ha fatto ciò che prometteva: tutti e 32 i segnaposto sono tornati in tutte e tre le esecuzioni, quindi codice, URL e tag sono stati ripristinati perfettamente. Ma il nostro script ha lasciato due piccole cose fuori dai segnaposto: la riga --- che apre il front matter, e le virgolette di chiusura dopo il titolo e la descrizione tradotti. Il modello le ha eliminate tutte e tre le volte. Il file iniziava così:

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

Senza il --- di apertura, un parser Markdown non vede più il front matter. Legge le sei righe YAML più il --- di chiusura come un'intestazione, quindi la pagina si aprirebbe con un'intestazione grande contenente lo YAML. Compilava comunque come MDX. Rimettere il --- non basta: il parser YAML rifiuta le due virgolette non chiuse.

La lezione non è evitare i segnaposto. È che tutto ciò che lasci esposto può tornare modificato, e che il front matter non dovrebbe mai essere inviato al modello come parte della pagina. Analizzalo, invia solo i valori rivolti al lettore (title, description) come stringhe separate, e riscrivi le traduzioni nella struttura originale. Sono poche righe di codice e rimuovono l'intera classe di problemi.

Testo in grassetto e intervalli in cinese, giapponese e coreano

Altri due problemi non sono mai comparsi nelle nostre nove esecuzioni, ma vale la pena verificarli perché rompono le pagine silenziosamente, ed entrambi hanno colpito il blog di BookTranslator. Il blog è scritto in MDX e tradotto automaticamente in 27 lingue, e in un'esecuzione di traduzione programmata due articoli coreani hanno fallito il nostro controllo della struttura per tre tentativi di fila. Le cause erano i due schemi seguenti.

Grassetto che termina con punteggiatura. CommonMark decide se ** può aprire o chiudere il grassetto osservando i caratteri adiacenti. Un ** di chiusura che segue immediatamente un segno di punteggiatura deve essere seguito da uno spazio, da un altro segno di punteggiatura o dalla fine della riga; questa è la regola del delimitatore destro. In inglese c'è sempre uno spazio in quel punto. In cinese, giapponese e coreano spesso no. Quindi 打开**设置 → 导出(测试版)**并选择 mostra asterischi letterali, perché il grassetto termina con una parentesi a larghezza piena ed è seguito direttamente da 并. La stessa riga senza la parentesi viene resa in grassetto, e così anche la riga con uno spazio dopo **. Un link in grassetto ha lo stesso problema, perché un link termina con ): 点击**[导出](./export-options.md)**打开 fallisce, e così anche il coreano **[API](./api.md)**는.

Il nostro modello ha inserito per caso uno spazio su entrambi i lati di ogni frase in grassetto, quindi il suo output è stato reso correttamente. Un editor che in seguito rimuove quegli spazi per seguire le convenzioni tipografiche cinesi fa riemergere il bug. Puoi provare tutte e sette le varianti in render-cases.md.

Intervalli numerici con la tilde. Il testo cinese e giapponese spesso scrive gli intervalli come 3~5. I siti GitHub Flavored Markdown basati su remark trasformano le singole tildi in barrato per impostazione predefinita: l'opzione singleTilde di remark-gfm è true a meno che tu non la modifichi. Due intervalli in un paragrafo, come 需要 3~5 分钟,生成 10~20 MB 的文件夹, vengono resi con il testo tra di essi barrato. Il nostro esempio usava trattini en, e otto esecuzioni li hanno mantenuti mentre la nona ha scritto 3 到 5. Chiedi trattini en o parole negli intervalli, oppure imposta singleTilde: false nella tua configurazione di remark.

Il controllo strutturale segnala entrambi i pattern. Per confermare cosa viene effettivamente reso, render_check.mjs conta grassetti, corsivi, barrati e asterischi rimasti nel sorgente e nella traduzione, e tenta una compilazione MDX:

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

Meno nodi in grassetto o più asterischi letterali nella traduzione indicano che qualche enfasi ha smesso di chiudersi.

Un flusso di lavoro che regge

  1. Correggi prima gli ID dei titoli. Aggiungi ID espliciti a ogni titolo a cui qualcosa punta, nella sintassi supportata dal tuo generatore di sito. Fallo nella lingua di origine, una volta, e committalo prima che qualsiasi cosa venga tradotta.

  2. Togli il front matter dalla pagina. Traduci solo i valori rivolti al lettore come title e description, come stringhe separate, e riscrivili nel YAML originale.

  3. Decidi la politica per il codice. Commenti tradotti o no; stringhe visualizzate negli esempi di configurazione tradotte o no. Scrivilo nel tuo glossario di traduzione accanto ai nomi di prodotto che non devono cambiare, come Shelfmark nel nostro esempio.

  4. Traduci il corpo. Usa uno strumento che analizzi il Markdown, oppure invia l'intera pagina con regole esplicite. Nel nostro test, inviare l'intera pagina ha mantenuto la sintassi intatta; se usi segnaposto, proteggi costrutti completi e non lasciare nulla a metà.

  5. Esegui il controllo strutturale.

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

    Confronta chiavi e valori del front matter, import, blocchi di codice, codice inline, destinazioni dei link, definizioni di riferimento, livelli dei titoli, destinazioni dei link nella pagina, tag e attributi JSX, commenti MDX, marcatori di avviso, etichette delle note, forma delle tabelle, caselle delle liste di attività, interruzioni di riga forzate, enfasi che non possono chiudersi, intervalli con tilde e termini che chiedi di mantenere. Non giudica la qualità della traduzione.

  6. Controlla la resa per le lingue CJK con render_check.mjs, e aggiungi uno spazio dopo ogni ** di chiusura che segue un segno di punteggiatura.

  7. Compila il sito e clicca i link. Apri la pagina in ogni lingua, clicca ogni link nella pagina e leggi gli esempi di configurazione. Una compilazione che riesce prova solo che il file compila.

Per la documentazione in cui la precisione conta, i passaggi di terminologia e revisione del nostro flusso di lavoro per la traduzione tecnica si applicano in aggiunta a questo.

Dove si inserisce BookTranslator

Il traduttore TXT di BookTranslator accetta file .txt, .md e .mdx. Secondo la pagina del prodotto, li traduce mantenendo paragrafi, interruzioni di riga, caratteri speciali e struttura del testo, e suddivide i file di grandi dimensioni in segmenti per una traduzione consapevole del contesto, così puoi caricare un intero manuale o un libro scritto in Markdown invece di incollarlo a pezzi.

La pagina del prodotto non descrive una gestione speciale per front matter, blocchi di codice o componenti MDX, e non abbiamo eseguito questo esempio attraverso BookTranslator per questo articolo. Qualunque strumento tu usi, esegui i passaggi da 1 a 3 prima di tradurre e i passaggi da 5 a 7 dopo. Un titolo senza un ID fisso perde comunque i suoi link, e un intervallo scritto con le tildi si trasforma comunque in barrato. Se stai pubblicando un libro in più lingue, la nostra guida all'autopubblicazione in più lingue copre il resto di quel lavoro.

Ottieni i file di test

Tutto ciò che proviene da questo test è liberamente riutilizzabile:

Post Correlati