Saltar al contenido
Development

El error de i18n en aria-label que estaba escondido a plena vista

Por Victor Da Luz
astroi18naccessibilitydev-logastro-tools

@vdaluz/astro-blog tiene una capa de i18n en/es desde hace un tiempo. Cada cadena visible en PostCard, Pagination, BlogPostMeta pasa por una búsqueda t(locale). Eso daba bastante orgullo al momento de construirlo. Por eso fue desalentador encontrar dos aria-labels que nunca se enteraron.

Dónde estaban escondidos

Los botones de flecha individuales de Pagination.astro (primero, anterior, siguiente, último) llaman correctamente a strings.goToFirstPage y sus equivalentes. Pero el <nav> exterior que envuelve todo el bloque de paginación tenía:

<nav class="..." aria-label="Blog pagination">

Un literal de texto plano, sentado una línea por encima de código que ya hacía lo correcto tres veces distintas. TagFilterNav.astro tenía la misma forma: una prop locale nunca formó parte de su interfaz, así que su aria-label por defecto era simplemente 'Filter posts', punto.

En una página en inglés nadie lo nota. En un índice de blog en español, un lector de pantalla anuncia “Blog pagination” en inglés dentro de una página que, por lo demás, está en español. Es pequeño, pero es exactamente el tipo de detalle pequeño que erosiona la confianza en una traducción.

Por qué la corrección parcial es mejor camuflaje

Lo que más llamó la atención fue lo plausible que se veía el error. Al hacer grep de aria-label en Pagination.astro aparecen cuatro sitios de llamada correctos seguidos. El quinto, el envoltorio del nav, se mezcla perfectamente a menos que se esté revisando cada uno específicamente contra el archivo de i18n.

Un componente que está 100% codificado de forma fija se descubre la primera vez que alguien carga el sitio en español. Un componente que está 90% correcto pasa una revisión superficial y sobrevive hasta que alguien lee cada línea contra la fuente de verdad, la misma razón por la que una prop de subtítulo que mentía sobre su color pasó desapercibida en seis sitios de llamada.

La solución

Dos cadenas nuevas en i18n.ts (blogPagination y filterPosts, con sus equivalentes en español). El nav de Pagination.astro ahora usa aria-label={strings.blogPagination}. TagFilterNav.astro recibió una prop locale real con valor por defecto 'en', con el aria-label por defecto recurriendo a strings.filterPosts, pero solo cuando quien lo consume no pasa un ariaLabel explícito que lo sobrescriba, ya que algunos sitios ya traducen esa etiqueta por su cuenta a nivel de aplicación.

Verificándolo sin un paso de compilación

astro-blog distribuye código fuente crudo sin paso de compilación, así que se pudo apuntar directamente al error: superponer la corrección local sobre la copia instalada en un sitio consumidor, correr el servidor de desarrollo de ese sitio, cargar la página real /es/blog en un navegador. La etiqueta del árbol de accesibilidad del nav decía “Paginación del blog.” No una prueba de snapshot, la página real renderizada.

Eso también sacó a la luz algo útil. El sitio contra el que se probó ya le pasa su propio ariaLabel explícito a TagFilterNav, así que el nuevo valor por defecto nunca se activa en esa página. Lo cual es correcto, se supone que la sobrescritura debe ganar, pero significa que solo se demostró que el camino de la sobrescritura sigue funcionando, no el nuevo valor por defecto. La cadena por defecto está cubierta por una prueba unitaria, no por una página en vivo. Vale la pena ser honesto sobre la diferencia entre esos dos tipos de verificación, en lugar de dejar que una verificación que pasa reemplace la afirmación equivocada.

Qué se revisaría la próxima vez

Antes de lanzar una capa de i18n, conviene hacer grep de cada prop de texto literal en cada componente y compararla contra el archivo de cadenas, no solo las que ya parecen estar usando t(). Un componente parcialmente conectado es un error peor que uno completamente codificado de forma fija, porque es más difícil de detectar.

Lecturas relacionadas