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.

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 archivo | Ejemplo de nuestra muestra | ¿Traducirlo? |
|---|---|---|
| Texto visible | encabezados, 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-label | alt="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-label | No |
href, src, id, class, for, name, type, data-* | href="#choose-format", id="q" | No |
<code>, <pre>, <script>, <style> | shelfmark export --format epub | No. 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 | <library>, , © | Mantén escapadas las que ya lo están. < 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 enlacemailto:, 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:
- Archivo completo. Pegar el archivo entero y pedir que devuelva el archivo traducido.
- Con instrucciones. Lo mismo, más ocho reglas: mantener cada etiqueta y nombre de atributo, no cambiar
href,src,id,classy valores similares, traducir los atributos de texto y metaetiquetas indicados, dejar<code>,<pre>,<script>y<style>intactos excepto elnamey ladescriptiondel JSON-LD, respetartranslate="no", mantener las referencias de caracteres y los comentarios, y establecerlangenzh-Hans. - 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>ytranslate="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 establecelang="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ón | Archivo completo (3 ejecuciones) | Con instrucciones (3 ejecuciones) | Segmentos (3 ejecuciones) |
|---|---|---|---|
Las mismas 58 etiquetas en orden; cada href, src, id, class sin cambios | 3 de 3 | 3 de 3 | 3 de 3 |
Texto con translate="no" conservado, el enlace interno sigue funcionando, <library> sigue escapado | 3 de 3 | 3 de 3 | 3 de 3 |
Línea de salida de muestra dentro de <pre> traducida | 3 de 3 | 3 de 3 | 0 de 3 (omitido por diseño) |
CSS content: "Note: " traducido | 3 de 3 | 0 de 3 | 0 de 3 (omitido por diseño) |
JSON-LD name y description traducidos | 3 de 3 | 3 de 3 | 0 de 3 (omitido por diseño) |
aria-label="Breadcrumb" traducido | 0 de 3 | 0 de 3 | 3 de 3 |
La oración con dos elementos <code> en línea dice lo correcto | 3 de 3 | 3 de 3 | 0 de 3 |
| Espacios entre texto chino a través de una etiqueta en línea | ninguno | 0, 5 y 4 | 6 en cada ejecución |
lang en <html> | zh-CN (no solicitado) | zh-Hans | zh-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 <library> 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><library></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><library></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 azh-CNpor su cuenta. Las ejecuciones instruidas usaronzh-Hans, como se pidió. Ambas son etiquetas de idioma válidas;zh-Hansnombra la escritura (chino simplificado),zh-CNnombra una región. La guía del W3C sobre declaraciones de idioma recomienda declarar el idioma en el elemento<html>y no con una metaetiquetaContent-Language.- JSON-LD: las ejecuciones de archivo completo e instruidas tradujeron los valores de
nameydescriptiony 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
›, ,©y la referencia del emoji tal como estaban escritas. Cinco de esas seis ejecuciones reescribieron el rango2–5con 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,<library>dentro de<code>, permaneció escapada en las nueve ejecuciones.
Antes de traducir: prepara el archivo
- 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. - 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.
- Mantén los ID en el idioma de origen. Los enlaces dentro de la página apuntan a valores de
id. Si alguien traduce unid, 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á. - Recopila el texto oculto. Enumera los atributos que contienen palabras,
alt,title,placeholder,aria-label,valueen botones, metaetiquetas y JSON-LD, para que se revisen y no se olviden. - 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
-
Ejecuta la comprobación estructural.
python3 check_html_translation.py source.html translated.html --lang zh-Hans --keep ShelfmarkCompara 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 contranslate="no", los comentarios y los enlaces dentro de la página. Avisa sobre atributos de texto que siguen en inglés, un valor delangque 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. -
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.
-
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.
-
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. -
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:
- sample-source.html: la muestra en inglés de 59 líneas;
- check_html_translation.py: el verificador estructural (solo biblioteca estándar de Python);
- run_llm_html_test.py: los tres métodos, para cualquier servidor local compatible con OpenAI;
- las nueve salidas en la carpeta de salidas, con check-results.txt y results.csv.
Publicaciones relacionadas





