Cursalia es multilingüe: cada curso, cada botón y cada mensaje existen en español, catalán e inglés (es/ca/en), y el alumno cambia de idioma con un selector en la cabecera. Ese multilingüismo, que parece un asunto de traducción, tiene un lado de accesibilidad que casi nadie atiende y que afecta de lleno a Marta. Cuando su lector de pantalla NVDA encuentra un texto, necesita saber en qué idioma leerlo para usar la voz y la pronunciación correctas: un texto en inglés leído con las reglas fonéticas del español suena a galimatías incomprensible. Y no basta con el texto visible: los alt, los aria-label, las fechas, la dirección del texto… todo tiene su matiz. En esta lección conectamos accesibilidad e idioma, apoyándonos en criterios WCAG que hasta ahora solo habíamos nombrado (3.1.1 y 3.1.2) y en el selector de idioma de Cursalia como hilo conductor.

Contenido

  1. Internacionalización (i18n) y localización (l10n): la diferencia
  2. El atributo lang en <html> (WCAG 3.1.1)
  3. lang en fragmentos de otro idioma (WCAG 3.1.2)
  4. Dirección del texto: dir y propiedades CSS lógicas
  5. Traducir también lo que no se ve: alt, aria-label, title, mensajes de estado
  6. Formatos localizados: fechas, números y moneda
  7. Multimedia por idioma: subtítulos y transcripciones
  8. Texto en imágenes y consideraciones culturales
  9. Errores comunes y consejos
  10. Ejercicios
  11. Conclusión

  1. Internacionalización (i18n) y localización (l10n): la diferencia

Dos términos que se confunden constantemente:

Término Qué es Ejemplo en Cursalia
Internacionalización (i18n) Diseñar la aplicación para que pueda adaptarse a varios idiomas y regiones, sin tocar el código Extraer todos los textos a ficheros de traducción; no incrustar cadenas en el HTML
Localización (l10n) Adaptar la aplicación a un idioma/región concretos Traducir al catalán, formatear fechas al estilo local, ajustar iconografía

La i18n es la infraestructura; la l10n es cada adaptación concreta. Nuestro foco aquí no es cómo montar esa infraestructura (eso es arquitectura), sino cómo hacer que el resultado sea accesible en cada idioma. Y todo empieza por decirle a la máquina en qué idioma está cada cosa.

  1. El atributo lang en <html> (WCAG 3.1.1)

El criterio WCAG 3.1.1 (Language of Page, nivel A) exige que el idioma principal de cada página esté declarado por programa. Se hace con el atributo lang en la etiqueta <html>:

<!-- Cuando el alumno tiene Cursalia en catalán -->
<html lang="ca">

Esto es crítico para Marta. Con lang="ca", NVDA carga la voz y las reglas de pronunciación del catalán; sin él (o con un lang equivocado), leería el catalán con la voz por defecto del sistema, produciendo un sonido ininteligible. También afecta a: la selección de fuentes, la separación silábica, las comillas tipográficas y a los correctores.

En Cursalia, el lang de <html> debe cambiar con el selector de idioma. Si el alumno pasa de español a inglés, no basta con traducir los textos: hay que actualizar también el atributo:

// Al usar el selector de idioma de Cursalia
function cambiarIdioma(codigo) { // 'es' | 'ca' | 'en'
  document.documentElement.setAttribute('lang', codigo);
  // …y cargar las traducciones de esa lengua
}

Usa siempre códigos BCP 47 correctos: es, ca, en (y variantes regionales cuando importen, como en-GB o es-MX). Un lang="english" o lang="castellano" no es válido y el lector lo ignora.

  1. lang en fragmentos de otro idioma (WCAG 3.1.2)

El criterio WCAG 3.1.2 (Language of Parts, nivel AA) va un paso más allá: cuando dentro de una página en un idioma aparece un fragmento en otro idioma, ese fragmento debe marcarse con su propio lang. Es habitualísimo en Cursalia: la ficha de un curso en español que cita el título original en inglés, o un término técnico extranjero.

<!-- Página en español con una cita en inglés -->
<p lang="es">
  El curso se inspira en la charla
  <span lang="en">The Future of Accessible Design</span>,
  muy recomendable.
</p>

Sin el lang="en" en la <span>, NVDA leería "The Future of Accessible Design" con fonética española: algo como "te futur of accesible desing". Con él, cambia momentáneamente a la voz inglesa, lee la frase correctamente y vuelve al español. Es exactamente lo que haría un humano bilingüe.

Compara:

<!-- Mal: el nombre en inglés se leerá con fonética española -->
<p lang="es">Hemos añadido un curso de <strong>machine learning</strong>.</p>

<!-- Bien: se marca el fragmento extranjero -->
<p lang="es">Hemos añadido un curso de <strong lang="en">machine learning</strong>.</p>

Matiz práctico: no hace falta marcar palabras que ya se han incorporado plenamente a la lengua (préstamos asentados como "software" o "email" en español). El criterio apunta a fragmentos que son de otro idioma, no a cada extranjerismo cotidiano. Aplica el sentido común: si un lector humano cambiaría de acento al leerlo, márcalo.

  1. Dirección del texto: dir y propiedades CSS lógicas

Aunque es/ca/en se escriben de izquierda a derecha (LTR), pensar en internacionalización obliga a preparar el terreno para idiomas de derecha a izquierda (RTL) como el árabe o el hebreo, por si Cursalia se expande. El atributo dir declara la dirección:

<html lang="ar" dir="rtl">   <!-- árabe: de derecha a izquierda -->
<html lang="es" dir="ltr">   <!-- español: de izquierda a derecha -->

El error clásico es maquetar con propiedades CSS físicas (margin-left, padding-right, left), que quedan al revés en RTL. La solución moderna son las propiedades lógicas, que se refieren al inicio y fin del flujo de lectura y se adaptan solas a la dirección:

/* Mal: física, no se adapta a RTL */
.tarjeta-curso { margin-left: 1rem; text-align: left; }

/* Bien: lógica, correcta en LTR y en RTL sin tocar nada */
.tarjeta-curso { margin-inline-start: 1rem; text-align: start; }
Propiedad física Propiedad lógica equivalente
margin-left margin-inline-start
padding-right padding-inline-end
left / right inset-inline-start / inset-inline-end
text-align: left text-align: start
border-left border-inline-start

Usar propiedades lógicas es también accesibilidad: garantiza que el orden visual coincida con el orden de lectura en cualquier idioma, algo que Sofía (baja visión, que sigue el texto con la lupa) agradece especialmente.

  1. Traducir también lo que no se ve: alt, aria-label, title, mensajes de estado

Este es el punto que más se olvida. Cuando se traduce Cursalia, se traduce lo visible… y se dejan en el idioma original los textos para tecnologías de asistencia. Resultado: Marta, con Cursalia en catalán, oye botones y descripciones en español. Todo texto destinado a personas —visible o no— debe traducirse:

Elemento Se olvida a menudo Debe traducirse
alt de imágenes Sí (Módulo 5)
aria-label / aria-labelledby
title de iconos y enlaces
Mensajes de las regiones en vivo (07-01) Muchísimo
Texto de los subtítulos A veces Sí (ver §7)
<!-- Mal: el aria-label se quedó en español con la interfaz en inglés -->
<html lang="en">
  <button aria-label="Añadir a favoritos" class="fav-btn">★</button>

<!-- Bien: el nombre accesible acompaña al idioma de la interfaz -->
<html lang="en">
  <button aria-label="Add to favourites" class="fav-btn">★</button>

Y muy importante, retomando 07-01: los mensajes de las regiones en vivo también se traducen. El "12 cursos encontrados", el "¡Matrícula realizada con éxito!" o el "Página cargada" del route announcer de 07-02 deben salir del sistema de traducción, no ser cadenas fijas en el código. Si Marta usa Cursalia en inglés, debe oír "12 courses found", no "12 cursos encontrados".

  1. Formatos localizados: fechas, números y moneda

Una fecha como 07/06/2026 es ambigua: ¿6 de julio o 7 de junio? Depende de la convención local. Y para Marta, cómo se lee ese formato en voz alta también cambia. La solución es no formatear a mano, sino usar la API Intl del navegador, que produce el formato correcto por idioma:

const fecha = new Date('2026-07-06');

// Español: "6 de julio de 2026"
new Intl.DateTimeFormat('es', { dateStyle: 'long' }).format(fecha);

// Inglés (GB): "6 July 2026"
new Intl.DateTimeFormat('en-GB', { dateStyle: 'long' }).format(fecha);

// Precio del curso en euros, formato español: "49,99 €"
new Intl.NumberFormat('es', { style: 'currency', currency: 'EUR' }).format(49.99);

Para máxima claridad con lectores de pantalla, acompaña las fechas legibles de un <time> con datetime en formato máquina, de modo que quede la fecha exacta e inequívoca:

<!-- Visible y localizado + valor máquina inequívoco -->
<time datetime="2026-07-06">6 de julio de 2026</time>

Cuidado también con los números: en español el separador de miles es el punto y el decimal la coma (1.234,50); en inglés, al revés (1,234.50). Un lector de pantalla puede leerlos de forma muy distinta. Dejar que Intl lo resuelva evita que "1,5" se lea como "mil quinientos" en un idioma y "uno coma cinco" en otro.

  1. Multimedia por idioma: subtítulos y transcripciones

En el Módulo 5 (05-03) hiciste accesibles los vídeos del reproductor de Cursalia con subtítulos y transcripciones, pensando sobre todo en Diego (sordera). La internacionalización añade una capa: cada idioma necesita su propia pista. Un vídeo puede tener varias pistas <track>, cada una con su srclang y su label:

<video controls>
  <source src="leccion-01.mp4" type="video/mp4">
  <track kind="subtitles" src="subs-es.vtt" srclang="es" label="Español" default>
  <track kind="subtitles" src="subs-ca.vtt" srclang="ca" label="Català">
  <track kind="subtitles" src="subs-en.vtt" srclang="en" label="English">
</video>

El srclang permite que el reproductor y las tecnologías de asistencia sepan el idioma de cada pista; el label es lo que el usuario ve en el menú de subtítulos. Idealmente, la pista default coincide con el idioma que el alumno tiene seleccionado en Cursalia. Lo mismo aplica a las transcripciones: ofrécelas en cada idioma disponible, no solo en el original. Diego, viendo Cursalia en catalán, espera subtítulos en catalán, no en inglés.

  1. Texto en imágenes y consideraciones culturales

Evita el texto incrustado en imágenes

Ya lo advertimos en 05-04: el texto dentro de una imagen no es seleccionable, no escala bien y es invisible para el lector de pantalla salvo por su alt. La internacionalización da un motivo más y contundente: el texto incrustado no se puede traducir sin rehacer la imagen. Un banner del catálogo con "¡Oferta de verano!" escrito dentro del .png obliga a generar tres imágenes (es/ca/en) y mantenerlas sincronizadas. La solución correcta es texto real (HTML) sobre la imagen mediante CSS: se traduce solo, escala con el zoom de Sofía y lo lee Marta.

<!-- Mal: el texto vive dentro del PNG, no se traduce ni escala -->
<img src="banner-oferta-verano.png" alt="¡Oferta de verano! 20% de descuento">

<!-- Bien: imagen de fondo + texto real y traducible encima -->
<div class="banner" style="background-image: url(banner-fondo.jpg)">
  <h2>{{ t('oferta_verano_titulo') }}</h2>
  <p>{{ t('oferta_verano_desc') }}</p>
</div>

Consideraciones culturales e iconografía

Localizar no es solo traducir palabras. Los iconos y símbolos no significan lo mismo en todas partes, y un icono confuso es una barrera cognitiva —piensa en Hugo (dislexia/TDAH), a quien un icono ambiguo carga más que ayuda. Buenas prácticas:

  • Acompaña los iconos de texto (o de un aria-label traducido): no confíes en que un símbolo se entienda igual en toda cultura.
  • Cuidado con los colores: sus connotaciones varían culturalmente. No uses solo el color para transmitir significado (regla de siempre, Módulo 4).
  • Revisa gestos, banderas y metáforas visuales: una bandera para elegir idioma es problemática (un idioma no equivale a un país; el inglés no es "solo" británico ni estadounidense). Mejor el nombre del idioma escrito en su propia lengua: "Español", "Català", "English".
<!-- Selector de idioma de Cursalia: nombres, no banderas -->
<nav aria-label="Seleccionar idioma">
  <button lang="es" aria-current="true">Español</button>
  <button lang="ca">Català</button>
  <button lang="en">English</button>
</nav>

Fíjate en dos detalles del selector: cada botón lleva su propio lang (para que NVDA pronuncie "Català" en catalán y "English" en inglés) y aria-current="true" marca el idioma activo, de forma que Marta sepa cuál está seleccionado.

Errores Comunes y Consejos

  • No actualizar lang en <html> al cambiar de idioma. Traduces los textos pero dejas lang="es": NVDA sigue leyendo con voz española un contenido en inglés. El selector debe cambiar el lang.
  • Olvidar lang en los fragmentos extranjeros (3.1.2). Los títulos de cursos o términos en otro idioma se leen con fonética equivocada. Marca la <span lang="…">.
  • Traducir solo lo visible. Los alt, aria-label, title y los mensajes de las live regions se quedan en el idioma original. Todo texto para personas se traduce.
  • Maquetar con propiedades físicas. margin-left y compañía rompen en RTL. Usa propiedades lógicas (margin-inline-start).
  • Formatear fechas y números a mano. Producen ambigüedad y lecturas erróneas. Usa Intl y <time datetime>.
  • Texto incrustado en imágenes. No se traduce, no escala, no se lee. Usa texto real sobre la imagen.
  • Banderas como selector de idioma. Idioma ≠ país. Usa el nombre del idioma en su propia lengua, con su lang.
  • Consejo: añade a la Definition of Done del Módulo 6 una comprobación por idioma: cambiar el selector a ca/en y verificar con NVDA que lang cambia, que los alt/aria-label están traducidos y que las fechas se leen bien.

Ejercicios

Ejercicio 1. Marta usa Cursalia en inglés (<html lang="en">) y, en la ficha de un curso, oye el título "Introducción a Python" leído con una voz que masacra la fonética. Además, el botón de favorito le dice "Añadir a favoritos" en vez de "Add to favourites". Identifica los dos problemas de accesibilidad multilingüe y cómo los corriges.

Ejercicio 2. Tienes esta fecha de inicio de curso escrita a mano en el HTML: <span>06/07/2026</span>. Explica por qué es problemática para la accesibilidad y la internacionalización, y reescríbela de forma correcta para un usuario con la interfaz en español.

Ejercicio 3. El equipo de marketing quiere un banner "¡Últimas plazas!" en el catálogo, y propone subirlo como imagen .png con el texto dentro, en los tres idiomas. Da dos razones (una de accesibilidad, otra de internacionalización) para rechazarlo y describe la alternativa correcta.

Soluciones

Solución 1. Problema 1: el título "Introducción a Python" es un fragmento en español dentro de una página en inglés y no está marcado con lang="es", así que NVDA lo lee con fonética inglesa (WCAG 3.1.2). Se corrige envolviéndolo: <span lang="es">Introducción a Python</span>. Problema 2: el aria-label del botón de favorito no se tradujo al cambiar de idioma; sigue en español ("Añadir a favoritos") con la interfaz en inglés. Se corrige haciendo que ese aria-label salga del sistema de traducción y muestre "Add to favourites" cuando la interfaz está en inglés. En resumen: marca los fragmentos de otro idioma y traduce también los textos no visibles.

Solución 2. 06/07/2026 es ambigua (¿6 de julio o 7 de junio?), depende de la convención local, y un lector de pantalla puede leerla de forma confusa o incorrecta. Lo correcto es dar un valor máquina inequívoco con <time datetime> y un texto visible localizado con Intl:

<time datetime="2026-07-06">6 de julio de 2026</time>

Así el usuario vidente ve la fecha en formato español sin ambigüedad, el lector dispone de la fecha exacta en datetime, y si se cambia de idioma, el texto visible se puede regenerar con Intl.DateTimeFormat para el nuevo locale.

Solución 3. Razón de accesibilidad: el texto incrustado en el .png no lo puede leer el lector de pantalla (solo dependería del alt), no escala con el zoom de Sofía y no se puede seleccionar. Razón de internacionalización: el texto dentro de la imagen no se traduce; obligaría a mantener tres imágenes sincronizadas (es/ca/en) y a rehacerlas ante cualquier cambio. Alternativa correcta: usar una imagen de fondo sin texto y superponer texto real HTML traducible (con las cadenas en el sistema de i18n), que se lee, se escala y se traduce automáticamente.

Conclusión

La accesibilidad y el multilingüismo están entrelazados: para que Marta oiga cada idioma con su voz correcta hay que declarar lang en <html> (3.1.1) y en cada fragmento extranjero (3.1.2); para que la maqueta funcione en cualquier dirección de lectura, propiedades CSS lógicas; y hay que traducir todo lo destinado a personas —alt, aria-label, title, mensajes de las live regions de 07-01, subtítulos por idioma de 05-03—, formatear fechas y números con Intl, evitar texto incrustado en imágenes (05-04) y cuidar la iconografía y la cultura. El selector de idioma de Cursalia no es solo un traductor: es un conmutador de accesibilidad.

Con esto, Cursalia ya es una plataforma accesible, dinámica, aplicativa (SPA) y multilingüe. Solo queda mirar hacia adelante: ¿hacia dónde va la accesibilidad web? WCAG 3.0, la evolución de ARIA, el empuje legal, la irrupción de la IA, la accesibilidad más allá de la web y el cambio cultural del "shift-left". Todo eso —y el cierre de tu viaje por este curso— es la última lección: 07-04: Tendencias Futuras en Accesibilidad Web.

© Copyright 2026. Todos los derechos reservados