Comment traduire un fichier HTML sans casser les balises, les attributs ou les liens
Ce qu'il faut traduire dans un fichier HTML, ce qu'il faut laisser tel quel, et comment le vérifier. Avec une page d'exemple, un vérificateur gratuit et un test local de LLM en 9 exécutions.

Pour traduire un fichier HTML, traduisez le texte que les gens lisent et les attributs qui contiennent des mots destinés au lecteur, comme alt, title, placeholder, aria-label et la méta-description de la page. Laissez le balisage intact : noms de balises, href, src, id, class, scripts, styles et tout ce qui est marqué translate="no". Ensuite, modifiez une valeur facile à oublier, l'attribut lang sur l'élément <html>, pour que les navigateurs, les lecteurs d'écran et les moteurs de recherche sachent dans quelle langue la page est désormais.
Garder les balises intactes s'est avéré être la partie facile. Nous avons traduit une page d'aide HTML de 59 lignes en chinois simplifié neuf fois avec un modèle local, en utilisant trois méthodes, et avons vérifié chaque sortie avec un script. Chaque exécution a conservé toutes les balises, tous les liens et tous les ID. Ce qui a mal tourné était plus discret : le modèle a traduit une ligne à l'intérieur d'un bloc de code <pre> dans les six exécutions qui envoyaient le fichier entier, il a laissé l'aria-label de la navigation en anglais dans les six, et la méthode qui protège le mieux le balisage par conception, la traduction des morceaux de texte un par un, a produit une phrase qui disait au lecteur de faire la mauvaise chose dans ses trois exécutions.
Réponse rapide : ce qu'il faut traduire et ce qu'il faut laisser tel quel
| Partie du fichier | Exemple de notre échantillon | Faut-il le traduire ? |
|---|---|---|
| Texte visible | titres, paragraphes, cellules de tableau, texte des liens, <label> | Oui |
<title> et méta-description | <title>Export Your Library | Shelfmark Help</title> | Oui. Les deux apparaissent dans les résultats de recherche et les onglets du navigateur. |
og:title et autres balises méta pour les réseaux sociaux | <meta property="og:title" content="Export Your Library"> | Oui, uniquement la valeur de content |
alt, title, placeholder, aria-label | alt="The Export dialog with EPUB selected", aria-label="Breadcrumb" | Oui |
lang sur <html> | <html lang="en"> | Remplacez-la par la langue cible, par exemple zh-Hans |
| Noms de balises et d'attributs | <a>, <img>, width, aria-label lui-même | Non |
href, src, id, class, for, name, type, data-* | href="#choose-format", id="q" | Non |
<code>, <pre>, <script>, <style> | shelfmark export --format epub | Non. Décidez séparément pour les commentaires de code et les exemples de sortie. |
| Texte défini par CSS | .note::before { content: "Note: "; } | Oui, mais cela se trouve dans la feuille de style, alors prévoyez-le |
| Données structurées JSON-LD | "name": "Export your Shelfmark library" | Traduisez les valeurs name et description ; conservez les clés et la validité du JSON |
Éléments avec translate="no" | <span translate="no">Shelfmark</span> | Non |
| Commentaires HTML | <!-- Generated by the help-center build... --> | Facultatif. Les lecteurs ne les voient jamais. |
| Références de caractères | <library>, , © | Gardez les caractères échappés tels quels. < ne doit pas devenir un vrai <. |
Cette liste n'est pas notre invention. La section du standard HTML sur l'attribut translate définit les « attributs traduisibles » : title sur chaque élément, alt sur les images, placeholder sur les champs de saisie, content sur les éléments meta dont le nom est reconnu comme traduisible, et lang, qui doit être modifié pour correspondre à la nouvelle langue. Elle cite également aria-label d'ARIA comme exemple d'attribut que d'autres spécifications peuvent ajouter, et précise que dans un élément translate="no", le texte et les valeurs d'attributs « doivent être laissés tels quels lors de la localisation de la page ».
Ce que nous avons testé
Nous avons rédigé sample-source.html, une page de centre d'aide pour la même application de lecture fictive, Shelfmark, que nous avons utilisée dans notre test de traduction Markdown. Elle contient un exemple de chaque ligne du tableau ci-dessus, ainsi que quelques pièges :
- une phrase coupée par des balises en ligne : « Before you export, back up your notes. An export copies your books, but it does not delete anything from Shelfmark, » avec le nom du produit à l'intérieur de
translate="no"; - une phrase avec deux éléments
<code>en ligne dont l'ordre compte : « Type the path to the<library>folder after--out» ; - un bloc
<pre><code>avec une commande et une ligne de sortie d'exemple,# Export finished: 128 books; - une règle CSS qui insère le mot « Note: » avant un paragraphe ;
- un lien interne vers
#choose-format, un lienmailto:, un formulaire de recherche et un bloc JSON-LD.
Ensuite, nous avons demandé à un modèle local de le traduire en chinois simplifié de trois manières :
- Fichier entier. Collez le fichier entier et demandez le fichier traduit en retour.
- Avec instructions. Idem, plus huit règles : conservez chaque nom de balise et d'attribut, ne modifiez pas
href,src,id,classet valeurs similaires, traduisez les attributs textuels et balises meta listés, laissez<code>,<pre>,<script>et<style>intacts sauf lenameet ladescriptiondu JSON-LD, respecteztranslate="no", conservez les références de caractères et les commentaires, et définissezlangsurzh-Hans. - Segments. L'approche des outils qui extraient le texte du HTML : un script divise le fichier en balises et en texte, ignore les éléments
<code>,<pre>,<script>,<style>ettranslate="no", envoie les fragments de texte restants et les attributs textuels au modèle sous forme de liste JSON numérotée, puis réinsère les traductions dans le balisage intact. Il définit égalementlang="zh-Hans"lui-même.
Le modèle était Qwen 3.6 27B (Q4_K_M) dans LM Studio, avec la réflexion désactivée et une température de 0,3, sur un Apple M4 Max avec 64 Go de mémoire. Nous avons exécuté chaque méthode trois fois, soit neuf exécutions au total, le 27 octobre 2026. Le script est run_llm_html_test.py, et les neuf sorties sont publiées sans modification ; le README liste chaque fichier. Si vous n'avez jamais configuré de modèle local, notre guide de traduction avec un LLM local couvre les réglages.
Il s'agissait d'un petit test avec un seul modèle, une seule paire de langues et une seule page. Il montre quels problèmes surviennent et comment les détecter. Il ne classe pas les modèles ni les outils.
Résultat : le balisage a survécu ; trois décisions, non
Nous avons vérifié chaque sortie avec check_html_translation.py, un vérificateur qui n'utilise que la bibliothèque standard de Python. La sortie complète se trouve dans check-results.txt, et results.csv contient une ligne par exécution.
| Vérification | Fichier entier (3 exécutions) | Avec instructions (3 exécutions) | Segments (3 exécutions) |
|---|---|---|---|
Mêmes 58 balises dans le même ordre ; chaque href, src, id, class inchangé | 3 sur 3 | 3 sur 3 | 3 sur 3 |
Texte translate="no" conservé, lien interne toujours fonctionnel, <library> toujours échappé | 3 sur 3 | 3 sur 3 | 3 sur 3 |
Ligne d'exemple de sortie à l'intérieur de <pre> traduite | 3 sur 3 | 3 sur 3 | 0 sur 3 (ignoré par conception) |
CSS content: "Note: " traduit | 3 sur 3 | 0 sur 3 | 0 sur 3 (ignoré par conception) |
name et description du JSON-LD traduits | 3 sur 3 | 3 sur 3 | 0 sur 3 (ignoré par conception) |
aria-label="Breadcrumb" traduit | 0 sur 3 | 0 sur 3 | 3 sur 3 |
Une phrase avec deux éléments <code> en ligne dit ce qu'il faut | 3 sur 3 | 3 sur 3 | 0 sur 3 |
| Espaces entre le texte chinois à travers une balise en ligne | aucun | 0, 5 et 4 | 6 à chaque exécution |
lang sur <html> | zh-CN (non demandé) | zh-Hans | zh-Hans (défini par le script) |
Chacune des neuf exécutions a conservé les mêmes 58 balises dans le même ordre, chaque href, src, id et class, la cible du lien interne, le commentaire, le <library> échappé et le nom du produit à l'intérieur de translate="no". Si vous craigniez qu'un modèle abîme le balisage d'une page simple, ce ne fut pas le cas. Les problèmes concernaient tous ce qu'il fallait traduire.
1. La sortie d'exemple à l'intérieur de <pre> a été traduite
Le bloc de code de notre exemple se termine par une ligne de sortie du programme :
<pre><code>shelfmark export --format epub --covers --out ~/Books/export
# Export finished: 128 books</code></pre>
Dans les six exécutions complètes et guidées, le modèle a renvoyé # 导出完成:128 本书 sur cette ligne, y compris dans les trois exécutions où l'instruction demandait de ne rien modifier à l'intérieur de <pre>. La commande elle-même n'a jamais été touchée. Le modèle a traité la ligne comme un commentaire, et traduire les commentaires est un choix raisonnable dans un tutoriel. Mais cette ligne est ce que le programme affiche, et un lecteur qui la compare à son terminal verra de l'anglais. Décidez avant de traduire si les sorties d'exemple et les commentaires de code doivent être traduits, et indiquez-le à l'outil, ou protégez complètement les blocs <pre>.
Les exécutions complètes ont aussi traduit le content: "Note: " CSS dans le bloc <style>, tandis que les exécutions guidées, invitées à laisser <style> tranquille, l'ont conservé en anglais. Ni l'un ni l'autre n'est faux en soi. Traduire la feuille de style est correct pour une page où le CSS est en ligne, et incorrect pour un site où une feuille de style partagée sert toutes les langues. Dans les deux cas, c'est une décision que quelqu'un doit prendre, pas un détail que le modèle devrait deviner.
2. Les libellés d'accessibilité ont été ignorés
La navigation fil d'Ariane est <nav aria-label="Breadcrumb">. Un lecteur d'écran annonce ce libellé ; les lecteurs voyants ne le voient jamais. Dans les six exécutions complètes et guidées, il est resté en anglais, même si l'instruction guidée listait aria-label parmi les attributs à traduire. Dans une exécution guidée, le aria-label="Search" du bouton de recherche est également resté en anglais.
La méthode par segments l'a traduit à chaque fois, car son script collectait chaque valeur de aria-label, alt, title et placeholder et demandait explicitement chacune d'elles. C'est l'argument le plus fort pour extraire les attributs textuels intentionnellement plutôt que d'espérer que le modèle les remarque.
3. La traduction par fragments a préservé les balises mais cassé la phrase
La méthode par segments est sûre pour le balisage par construction : le modèle ne voit jamais une balise, il ne peut donc pas l'endommager. Elle a passé toutes les vérifications structurelles lors des trois exécutions. Elle a aussi produit la seule traduction du test qui donnait au lecteur des instructions erronées.
La phrase anglaise est « Type the path to the <library> folder after --out. » Le <code> en ligne la divise en trois morceaux : « Type the path to the », « folder after » et un point. Le chinois place ces mots dans un ordre différent, un traducteur doit donc déplacer les deux éléments de code. La méthode par segments ne peut pas les déplacer. Les trois exécutions ont inversé l'instruction. L'exécution 2 a produit ceci (les exécutions 1 et 3 disent 后 au lieu de 之后, avec le même sens) :
在 <code><library></code> 之后输入 <code>--out</code>文件夹的路径。
Cela se lit « après <library>, tapez le chemin du dossier --out », soit l'inverse de l'instruction. Les six exécutions sur fichier entier et avec instructions ont déplacé les éléments de code et ont eu raison :
在 <code>--out</code> 后面输入 <code><library></code> 文件夹的路径。
La même division a laissé des espaces parasites là où une balise rencontre du texte chinois, comme 前往 <b>设置</b> et <b>书库</b> ,, dans les trois exécutions par segments, six endroits par exécution. Un navigateur les affiche comme des espaces visibles, y compris avant la ponctuation chinoise. Les exécutions sur fichier entier n'en avaient aucun ; deux des trois exécutions avec instructions en avaient quatre ou cinq.
Un vérificateur structurel ne peut pas détecter la phrase inversée, car chaque balise est exactement là où elle était. Notre vérificateur signale quand le code en ligne change d'ordre, ce qui est normal pour de nombreuses langues, mais cet avertissement ne peut pas distinguer le vrai du faux. Dans ce test, il s'est déclenché sur les six traductions correctes et est resté silencieux sur les trois incorrectes, car les incorrectes conservaient l'ordre anglais. Seule une personne lisant la phrase peut le dire. La solution consiste à envoyer des phrases entières, avec leurs balises en ligne, au traducteur, ce que faisaient les exécutions sur fichier entier, et à vérifier la structure ensuite.
Différences mineures
lang: les exécutions sur fichier entier l'ont changé enzh-CNd'elles-mêmes. Les exécutions avec instructions ont utilisézh-Hans, comme demandé. Les deux sont des balises de langue valides ;zh-Hansnomme l'écriture (chinois simplifié),zh-CNnomme une région. Les recommandations du W3C sur les déclarations de langue préconisent de déclarer la langue sur l'élément<html>et non avec une balise metaContent-Language.- JSON-LD : les exécutions sur fichier entier et avec instructions ont traduit les valeurs
nameetdescriptionet ont conservé un JSON valide. Notre script par segments a ignoré le contenu de<script>, ses données structurées sont donc restées en anglais, ce qui est aussi le comportement d'un outil qui ignore les scripts. - Références de caractères : les exécutions sur fichier entier et avec instructions ont conservé
›, ,©et la référence emoji telles qu'écrites. Cinq de ces six exécutions ont réécrit la plage2–5en mots chinois (2 到 5 ou 2 至 5) ; une exécution avec instructions a écrit un tiret simple au lieu de la référence, bien que son prompt demandât de conserver les références. Notre script par segments les a toutes décodées et a réécrit des caractères, ce qui s'affiche de la même façon. Celle qui compte,<library>à l'intérieur de<code>, est restée échappée dans les neuf exécutions.
Avant de traduire : préparer le fichier
- Marquez ce qui ne doit pas changer. Placez
translate="no"sur les noms de produits, les numéros de modèle, les chaînes ressemblant à du code dans la prose et tout ce qui doit rester tel quel. Cela fait partie du HTML, ne coûte rien, et le modèle l'a respecté dans les six exécutions qui lui ont envoyé le fichier entier. Les outils par segments qui le lisent, comme notre script, ignorent entièrement ces éléments. Notre guide des éléments à ne pas traduire explique comment décider ce qui figure sur cette liste. - Décidez la politique de code. Les commentaires de code sont-ils traduits ? Les exemples de sortie ? La feuille de style est-elle partagée entre les langues ? Notez la réponse, avec les noms qui ne doivent pas changer, dans un glossaire de traduction, et donnez-le à qui ou à quoi traduit.
- Conservez les ID dans la langue source. Les liens internes pointent vers des valeurs
id. Si quelqu'un traduit unid, tous les liens vers celui-ci se cassent. Aucune de nos exécutions ne l'a fait, mais un traducteur qui « aide » en localisant les ancres le fera. - Recueillez le texte caché. Listez les attributs qui contiennent des mots,
alt,title,placeholder,aria-label,valuesur les boutons, les balises meta et le JSON-LD, afin qu'ils soient vérifiés et non oubliés. - Ne traduisez pas les chaînes JavaScript sur place. Les messages définis par un script, comme une alerte ou un libellé de bouton créé dans le code, nécessitent une étape de localisation séparée dans le code. Aucune des six exécutions sur fichier entier ou avec instructions n'a modifié notre bloc de script, ce qui est correct, mais cela signifie que ces chaînes restent en anglais.
Après la traduction : vérifier
-
Lancez la vérification structurelle.
python3 check_html_translation.py source.html translated.html --lang zh-Hans --keep ShelfmarkElle compare les balises et leur ordre, chaque attribut qui n'est pas du texte destiné au lecteur, le contenu de
<pre>,<script>et<style>, le code en ligne, les clés JSON-LD, le textetranslate="no", les commentaires et les liens internes. Elle avertit en cas d'attributs textuels encore en anglais, d'une valeurlangdifférente de celle attendue, de texte visible qui semble non traduit, de code en ligne dont l'ordre a changé et d'espaces entre du texte chinois, japonais ou coréen de part et d'autre d'une balise en ligne. Elle ne juge pas la qualité de la traduction. -
Lisez chaque phrase contenant une balise en ligne. C'est là que le sens s'est cassé dans notre test. Ne vous fiez pas à l'avertissement « code en ligne réordonné » du vérificateur pour trouver ces phrases : dans notre test, les traductions erronées étaient celles qui conservaient l'ordre d'origine.
-
Ouvrez la page dans un navigateur. Cliquez sur les liens internes, parcourez le formulaire au clavier avec un lecteur d'écran si possible, et recherchez les espaces autour du texte en gras et des liens.
-
Pour les langues de droite à gauche, définissez aussi
dir="rtl"sur l'élément<html>. Notre guide de traduction de livres RTL couvre les autres changements. -
Pour le XHTML, comme les fichiers de chapitre dans un EPUB, vérifiez aussi que le fichier est toujours un XML bien formé, par exemple avec
xmllint --noout chapter.xhtml. Un navigateur pardonne une balise non fermée ; une liseuse peut ne pas le faire. Notre liste de contrôle QA pour la traduction EPUB couvre le reste d'un ebook.
Où BookTranslator intervient
La page de téléversement de BookTranslator accepte les fichiers .html, .htm et .xhtml en plus des livres et des documents, ce qui vous permet de téléverser une page enregistrée ou un article d'aide statique sous forme de fichier complet. Nous n'avons pas fait passer cet exemple par BookTranslator pour cet article, et nous n'avons pas testé la façon dont il gère l'ordre du code en ligne, le contenu CSS ou le JSON-LD ; vérifiez donc sa sortie comme vous le feriez pour n'importe quel autre outil. Vous voyez le prix avant de payer.
Quoi que vous utilisiez, effectuez les étapes de préparation avant de traduire et les vérifications ensuite. Une page dont toutes les balises sont intactes peut encore dire au lecteur de taper quelque chose d'incorrect. Si votre HTML est en réalité de la documentation écrite en Markdown, notre guide de traduction Markdown traite du format source à la place.
Obtenez les fichiers de test
Tout le contenu de ce test est librement réutilisable :
- sample-source.html : l'échantillon anglais de 59 lignes ;
- check_html_translation.py : le vérificateur structurel (bibliothèque standard Python uniquement) ;
- run_llm_html_test.py : les trois méthodes, pour tout serveur local compatible OpenAI ;
- les neuf sorties dans le dossier outputs, avec check-results.txt et results.csv.
Articles connexes





