BookTranslator
BookTranslator

Cómo traducir un archivo HTML sin romper etiquetas, atributos ni enlaces

Qué traducir en un archivo HTML, qué dejar intacto y cómo comprobarlo. Con una página de ejemplo, un verificador gratuito y una prueba de 9 ejecuciones con un LLM local.

BookTranslator

BookTranslator Team

16 min de lectura

Para traducir un archivo HTML, traduce el texto que lee la gente y los atributos que contienen palabras dirigidas al lector, como alt, title, placeholder, aria-label y la meta descripción de la página. Deja el marcado intacto: nombres de etiquetas, href, src, id, class, scripts, estilos y cualquier cosa marcada con translate="no". Luego cambia un valor que es fácil de olvidar, el atributo lang del elemento <html>, para que los navegadores, los lectores de pantalla y los motores de búsqueda sepan en qué idioma está ahora la página.

Mantener las etiquetas intactas resultó ser la parte fácil. Tradujimos una página de ayuda HTML de 59 líneas al chino simplificado nueve veces con un modelo local, usando tres métodos, y comprobamos cada salida con un script. Todas las ejecuciones conservaron todas las etiquetas, enlaces e ID. Lo que falló fue más sutil: el modelo tradujo una línea dentro de un bloque de código <pre> en las seis ejecuciones que enviaron el archivo completo, dejó el aria-label de la navegación en inglés en las seis, y el método que por diseño mantiene el marcado más seguro, traducir las piezas de texto una por una, produjo una frase que le decía al lector que hiciera lo incorrecto en las tres ejecuciones.

Respuesta rápida: qué traducir y qué dejar intacto

Parte del archivoEjemplo de nuestra muestra¿Traducirlo?
Texto visibleencabezados, párrafos, celdas de tabla, texto de enlaces, <label>Sí
<title> y meta descripción<title>Export Your Library | Shelfmark Help</title>Sí. Ambos aparecen en los resultados de búsqueda y en las pestañas del navegador.
og:title y otras metaetiquetas sociales<meta property="og:title" content="Export Your Library">Sí, solo el valor de content
alt, title, placeholder, aria-labelalt="The Export dialog with EPUB selected", aria-label="Breadcrumb"Sí
lang en <html><html lang="en">Cámbialo al idioma de destino, por ejemplo zh-Hans
Nombres de etiquetas y atributos<a>, <img>, width, el propio aria-labelNo
href, src, id, class, for, name, type, data-*href="#choose-format", id="q"No
<code>, <pre>, <script>, <style>shelfmark export --format epubNo. Decide por separado sobre los comentarios de código y la salida de ejemplo.
Texto establecido por CSS.note::before { content: "Note: "; }Sí, pero reside en la hoja de estilos, así que tenlo en cuenta
Datos estructurados JSON-LD"name": "Export your Shelfmark library"Traduce los valores de name y description; mantén las claves y el JSON válido
Elementos con translate="no"<span translate="no">Shelfmark</span>No
Comentarios HTML<!-- Generated by the help-center build... -->Opcional. Los lectores nunca los ven.
Referencias de caracteres&lt;library&gt;, &nbsp;, &copy;Mantén escapadas las que ya lo están. &lt; no debe convertirse en un < real.

Esta lista no es invención nuestra. La sección del Estándar HTML sobre el atributo translate define los «atributos traducibles»: title en todos los elementos, alt en las imágenes, placeholder en los campos de entrada, content en los elementos meta cuyo nombre se sabe que es traducible, y lang, que según indica debe cambiarse para coincidir con el nuevo idioma. También menciona aria-label de ARIA como ejemplo de un atributo que otras especificaciones pueden añadir, y afirma que en un elemento con translate="no", el texto y los valores de los atributos «deben dejarse tal cual cuando se localiza la página».

Lo que probamos

Escribimos sample-source.html, una página de centro de ayuda para la misma aplicación ficticia de lectura, Shelfmark, que usamos en nuestra prueba de traducción de Markdown. Contiene un ejemplo de cada fila de la tabla anterior, además de algunas trampas:

  • una frase dividida por etiquetas en línea: «Antes de exportar, haz una copia de seguridad de tus notas. Una exportación copia tus libros, pero no elimina nada de Shelfmark», con el nombre del producto dentro de translate="no";
  • una frase con dos elementos <code> en línea cuyo orden importa: «Escribe la ruta a la carpeta <library> después de --out»;
  • un bloque <pre><code> con un comando y una línea de salida de ejemplo, # Export finished: 128 books;
  • una regla CSS que inserta la palabra «Nota:» antes de un párrafo;
  • un enlace interno a #choose-format, un enlace mailto:, un formulario de búsqueda y un bloque JSON-LD.

Luego pedimos a un modelo local que lo tradujera al chino simplificado de tres maneras:

  1. Archivo completo. Pegar el archivo entero y pedir que devuelva el archivo traducido.
  2. Con instrucciones. Lo mismo, más ocho reglas: mantener cada etiqueta y nombre de atributo, no cambiar href, src, id, class y valores similares, traducir los atributos de texto y metaetiquetas indicados, dejar <code>, <pre>, <script> y <style> intactos excepto el name y la description del JSON-LD, respetar translate="no", mantener las referencias de caracteres y los comentarios, y establecer lang en zh-Hans.
  3. Segmentos. El enfoque de las herramientas que extraen texto del HTML: un script divide el archivo en etiquetas y texto, omite los elementos <code>, <pre>, <script>, <style> y translate="no", envía los fragmentos de texto restantes y los atributos de texto al modelo como una lista JSON numerada, y escribe las traducciones de vuelta en el marcado intacto. También establece lang="zh-Hans" por sí mismo.

El modelo fue Qwen 3.6 27B (Q4_K_M) en LM Studio, con el razonamiento desactivado y temperatura 0.3, en un Apple M4 Max con 64 GB de memoria. Ejecutamos cada método tres veces, nueve ejecuciones en total, el 27 de octubre de 2026. El script es run_llm_html_test.py, y las nueve salidas se publican sin editar; el README enumera todos los archivos. Si nunca has configurado un modelo local, nuestra guía de traducción con LLM local cubre los ajustes.

Esta fue una prueba pequeña de un modelo, un par de idiomas y una página. Muestra qué problemas ocurren y cómo detectarlos. No clasifica modelos ni herramientas.

Resultado: El marcado sobrevivió; tres decisiones no

Revisamos cada salida con check_html_translation.py, un verificador que utiliza únicamente la biblioteca estándar de Python. La salida completa está en check-results.txt, y results.csv tiene una fila por ejecución.

VerificaciónArchivo completo (3 ejecuciones)Con instrucciones (3 ejecuciones)Segmentos (3 ejecuciones)
Las mismas 58 etiquetas en orden; cada href, src, id, class sin cambios3 de 33 de 33 de 3
Texto con translate="no" conservado, el enlace interno sigue funcionando, &lt;library&gt; sigue escapado3 de 33 de 33 de 3
Línea de salida de muestra dentro de <pre> traducida3 de 33 de 30 de 3 (omitido por diseño)
CSS content: "Note: " traducido3 de 30 de 30 de 3 (omitido por diseño)
JSON-LD name y description traducidos3 de 33 de 30 de 3 (omitido por diseño)
aria-label="Breadcrumb" traducido0 de 30 de 33 de 3
La oración con dos elementos <code> en línea dice lo correcto3 de 33 de 30 de 3
Espacios entre texto chino a través de una etiqueta en líneaninguno0, 5 y 46 en cada ejecución
lang en <html>zh-CN (no solicitado)zh-Hanszh-Hans (establecido por el script)

Cada una de las nueve ejecuciones mantuvo las mismas 58 etiquetas en el mismo orden, cada href, src, id y class, el destino del enlace en la página, el comentario, el &lt;library&gt; escapado y el nombre del producto dentro de translate="no". Si te preocupaba que un modelo estropeara el marcado de una página sencilla, no lo hizo. Los problemas estaban todos en qué traducir.

1. La salida de ejemplo dentro de <pre> se tradujo

El bloque de código de nuestro ejemplo termina con una línea de salida del programa:

<pre><code>shelfmark export --format epub --covers --out ~/Books/export
# Export finished: 128 books</code></pre>

En las seis ejecuciones completas y con instrucciones, el modelo devolvió # 导出完成:128 本书 en esa línea, incluidas las tres ejecuciones en las que el prompt indicaba no cambiar nada dentro de <pre>. El comando en sí nunca se tocó. El modelo trató la línea como un comentario, y traducir comentarios es una elección razonable en un tutorial. Pero esta línea es lo que imprime el programa, y un lector que la compare con su terminal verá inglés. Decide antes de traducir si se traducen la salida de ejemplo y los comentarios del código, y díselo a la herramienta, o protege los bloques <pre> por completo.

Las ejecuciones completas también tradujeron el content: "Note: " de CSS en el bloque <style>, mientras que las ejecuciones con instrucciones, a las que se les dijo que dejaran <style> en paz, lo mantuvieron en inglés. Ninguna de las dos es incorrecta por sí sola. Traducir la hoja de estilos es correcto para una página donde el CSS es en línea, e incorrecto para un sitio donde una hoja de estilos compartida sirve a todos los idiomas. En cualquier caso, es una decisión que alguien tiene que tomar, no un detalle que un modelo deba adivinar.

2. Se omitieron las etiquetas de accesibilidad

La navegación de migas de pan es <nav aria-label="Breadcrumb">. Un lector de pantalla anuncia esa etiqueta; los lectores videntes nunca la ven. En las seis ejecuciones completas y con instrucciones, permaneció en inglés, aunque el prompt con instrucciones incluía aria-label entre los atributos a traducir. En una ejecución con instrucciones, el aria-label="Search" del botón de búsqueda también permaneció en inglés.

El método de segmentos lo tradujo todas las veces, porque su script recopilaba cada valor de aria-label, alt, title y placeholder y solicitaba cada uno explícitamente. Ese es el argumento más sólido para extraer los atributos de texto a propósito en lugar de esperar que el modelo los note.

3. Traducir por fragmentos conservó las etiquetas y rompió la frase

El método de segmentos es seguro para el marcado por construcción: el modelo nunca ve una etiqueta, así que no puede dañarla. Superó todas las comprobaciones estructurales en las tres ejecuciones. También produjo la única traducción de la prueba que dio instrucciones erróneas al lector.

La frase en inglés es «Type the path to the <library> folder after --out.» El <code> en línea la divide en tres fragmentos: «Type the path to the», «folder after» y un punto. El chino ordena estas palabras de forma diferente, por lo que un traductor necesita mover los dos elementos de código. El método de segmentos no puede moverlos. Las tres ejecuciones invirtieron la instrucción. La ejecución 2 produjo esto (las ejecuciones 1 y 3 dicen 后 en lugar de 之后, con el mismo significado):

在 <code>&lt;library&gt;</code> 之后输入 <code>--out</code>文件夹的路径。

Eso se lee «después de <library>, escribe la ruta de la carpeta --out», lo contrario de la instrucción. Las seis ejecuciones de archivo completo e instruidas movieron los elementos de código y lo hicieron bien:

在 <code>--out</code> 后面输入 <code>&lt;library&gt;</code> 文件夹的路径。

La misma división dejó espacios sueltos donde una etiqueta se encuentra con texto chino, como 前往 <b>设置</b> y <b>书库</b> ,, en las tres ejecuciones por segmentos, seis lugares por ejecución. Un navegador los muestra como huecos visibles, incluso antes de la puntuación china. Las ejecuciones de archivo completo no tuvieron ninguno; dos de las tres ejecuciones instruidas tuvieron cuatro o cinco.

Un verificador estructural no puede detectar la frase invertida, porque cada etiqueta está exactamente donde estaba. Nuestro verificador avisa cuando el código en línea cambia de orden, lo cual es normal en muchos idiomas, pero esa advertencia no distingue lo correcto de lo incorrecto. En esta prueba se activó en las seis traducciones correctas y permaneció en silencio en las tres incorrectas, porque las incorrectas mantuvieron el orden del inglés. Solo una persona que lea la frase puede saberlo. La solución es enviar frases completas, con sus etiquetas en línea, al traductor, que es lo que hicieron las ejecuciones de archivo completo, y comprobar la estructura después.

Diferencias menores

  • lang: las ejecuciones de archivo completo lo cambiaron a zh-CN por su cuenta. Las ejecuciones instruidas usaron zh-Hans, como se pidió. Ambas son etiquetas de idioma válidas; zh-Hans nombra la escritura (chino simplificado), zh-CN nombra una región. La guía del W3C sobre declaraciones de idioma recomienda declarar el idioma en el elemento <html> y no con una metaetiqueta Content-Language.
  • JSON-LD: las ejecuciones de archivo completo e instruidas tradujeron los valores de name y description y mantuvieron el JSON válido. Nuestro script de segmentos omitió el contenido de <script>, por lo que sus datos estructurados permanecieron en inglés, que es como se comportará también una herramienta que ignore los scripts.
  • Referencias de caracteres: las ejecuciones de archivo completo e instruidas mantuvieron &rsaquo;, &nbsp;, &copy; y la referencia del emoji tal como estaban escritas. Cinco de esas seis ejecuciones reescribieron el rango 2&ndash;5 con palabras chinas (2 到 5 o 2 至 5); una ejecución instruida escribió un guion simple en lugar de la referencia, aunque su prompt decía que mantuviera las referencias. Nuestro script de segmentos decodificó todas y volvió a escribir caracteres, lo que se muestra igual. La que importa, &lt;library&gt; dentro de <code>, permaneció escapada en las nueve ejecuciones.

Antes de traducir: prepara el archivo

  1. Marca lo que no debe cambiar. Pon translate="no" en nombres de productos, números de modelo, cadenas similares a código en la prosa y cualquier otra cosa que deba permanecer como está. Es parte de HTML, no cuesta nada y el modelo lo respetó en las seis ejecuciones que le enviaron el archivo completo. Las herramientas de segmentos que lo leen, como nuestro script, omiten esos elementos por completo. Nuestra guía de lo que no se debe traducir cubre cómo decidir qué entra en esa lista.
  2. Decide la política de código. ¿Se traducen los comentarios de código? ¿Y la salida de ejemplo? ¿La hoja de estilos se comparte entre idiomas? Anota la respuesta, junto con los nombres que no deben cambiar, en un glosario de traducción y dáselo a quien o a lo que traduzca.
  3. Mantén los ID en el idioma de origen. Los enlaces dentro de la página apuntan a valores de id. Si alguien traduce un id, todos los enlaces a él se rompen. Ninguna de nuestras ejecuciones lo hizo, pero un traductor que localice los anclajes «de forma útil» sí lo hará.
  4. Recopila el texto oculto. Enumera los atributos que contienen palabras, alt, title, placeholder, aria-label, value en botones, metaetiquetas y JSON-LD, para que se revisen y no se olviden.
  5. No traduzcas las cadenas de JavaScript en el lugar. Los mensajes establecidos por un script, como una alerta o la etiqueta de un botón creada en el código, necesitan un paso de localización aparte en el código. Ninguna de las seis ejecuciones de archivo completo o instruidas cambió nuestro bloque de script, lo cual es correcto, pero significa que esas cadenas permanecen en inglés.

Después de traducir: compruébalo

  1. Ejecuta la comprobación estructural.

    python3 check_html_translation.py source.html translated.html --lang zh-Hans --keep Shelfmark
    

    Compara las etiquetas y su orden, cada atributo que no sea texto visible para el lector, el contenido de <pre>, <script> y <style>, el código en línea, las claves de JSON-LD, el texto con translate="no", los comentarios y los enlaces dentro de la página. Avisa sobre atributos de texto que siguen en inglés, un valor de lang que difiere de lo que esperas, texto visible que parece no traducido, código en línea que cambió de orden y espacios entre texto chino, japonés o coreano a través de una etiqueta en línea. No juzga la calidad de la traducción.

  2. Lee cada frase que contenga una etiqueta en línea. Aquí es donde se rompió el significado en nuestra prueba. No te fíes de la advertencia «código en línea reordenado» del verificador para encontrar estas frases: en nuestra prueba, las traducciones incorrectas fueron las que mantuvieron el orden original.

  3. Abre la página en un navegador. Haz clic en los enlaces dentro de la página, recorre el formulario con un lector de pantalla si puedes y busca huecos alrededor del texto en negrita y enlazado.

  4. Para idiomas de derecha a izquierda, establece también dir="rtl" en el elemento <html>. Nuestra guía de traducción de libros RTL cubre qué más cambia.

  5. Para XHTML, como los archivos de capítulo dentro de un EPUB, confirma también que el archivo sigue siendo XML bien formado, por ejemplo con xmllint --noout chapter.xhtml. Un navegador perdona una etiqueta sin cerrar; un lector de libros electrónicos puede que no. Nuestra lista de verificación de control de calidad de traducción de EPUB cubre el resto de un libro electrónico.

Dónde encaja BookTranslator

La página de carga de BookTranslator acepta archivos .html, .htm y .xhtml junto con libros y documentos, por lo que puedes subir una página guardada o un artículo de ayuda estático como un archivo completo. No pasamos esta muestra por BookTranslator para este artículo, y no hemos probado cómo maneja el orden del código en línea, el contenido CSS o JSON-LD, así que revisa su resultado de la misma manera que revisarías el de cualquier otra herramienta. Ves el precio antes de pagar.

Independientemente de lo que uses, haz los pasos de preparación antes de traducir y las comprobaciones después. Una página con todas las etiquetas intactas aún puede indicarle al lector que escriba algo incorrecto. Si tu HTML es en realidad documentación escrita en Markdown, nuestra guía de traducción de Markdown cubre el formato de origen en su lugar.

Obtén los archivos de prueba

Todo lo de esta prueba es libre de reutilizar:

Publicaciones relacionadas