Saltar al contenido
Development

Agregar un tercer idioma rompió cosas que el build nunca podría haber atrapado

Por Victor Da Luz
astroi18nseodev-logsite

Agregué portugués (Brasil) como tercer idioma en este sitio, reflejando la configuración de español que ya existía. Pensé que sería mecánico: copiar el patrón de es/, traducir las cadenas, listo. En cambio sacó a la luz dos bugs que habían estado sentados en silencio todo este tiempo en un sistema de dos idiomas, invisibles hasta que un tercer idioma expuso la suposición.

El sitio siempre tuvo solo dos idiomas

El código de i18n no estaba construido de forma genérica para N idiomas. Estaba construido para exactamente dos, y se notó en el momento en que intenté agregar un tercero. El selector de idioma era un ternario binario, “mostrar el otro,” no “recorrer cada idioma excepto el actual.” Un type guard verificaba value === 'en' || value === 'es' en vez de verificar pertenencia a una lista. Y los tipos Record<UiLocale, string> de TypeScript significaban que en el momento en que pt se unía a la unión, cada cadena de la interfaz necesitaba una traducción al portugués o el build no pasaba el typecheck.

Ese último en realidad es una funcionalidad: significaba que no podía publicar por accidente una página a medio traducir, la misma barrera de protección que atrapó un párrafo que se había saltado la traducción antes. Pero también significaba que “solo traducir la página de Deep Cut Atlas” dejaba de ser una opción en cuanto pt existiera en cualquier parte del sistema de tipos.

Nada de esto era un bug con dos idiomas. El código binario funciona bien para dos cosas. Simplemente no generaliza, y nada obliga a notarlo hasta que se agrega un tercero.

El error de build que en realidad era de otro paquete

Agregué pt al mapa i18n.fallback de Astro, para que las páginas en pt que todavía no existieran renderizaran con gracia contenido en inglés de la misma forma que el español, y el build falló con un error de ruta estática dentro de los archivos de ruta del paquete de blog compartido. No era código mío.

El renderizado de fallback de Astro prerenderiza de forma anticipada cada ruta dinámica bajo cada idioma de fallback, integraciones de terceros incluidas. El tipo de idioma propio del paquete de blog está fijo a solo inglés y español, y se atragantó al intentar resolver una ruta en portugués para una ruta que no sabe cómo traducir.

El arreglo fue dejar a pt fuera del mapa de fallback por completo: las páginas en pt que no existen dan 404 en vez de reescribirse en silencio a inglés. Eso cambió un problema por uno más chico. Una página que había dependido del fallback anterior para servir contenido en inglés en una URL de pt ahora daba 404 de verdad, así que tuve que escribir una traducción real al portugués para ella en vez de dejarla vivir de la red de seguridad.

El bug que el build nunca podría haber atrapado

Una vez que todo compiló y cada página que había tocado renderizaba correctamente, corrí una revisión sobre el diff antes de darlo por terminado. Atrapó algo que se me había pasado: cada página del sitio, incluidas las páginas sin ninguna versión en portugués, ahora anunciaba un enlace SEO de idioma alternativo que afirmaba que existía una traducción al portugués en una URL que da 404.

El mecanismo: el sitio emite una etiqueta hreflang por cada idioma configurado, en cada página, sin condición. Inofensivo con español, porque español tiene un fallback para todo el sitio y cada URL bajo /es/ resuelve a algo aunque sea contenido en inglés en una URL en español. Portugués no tiene ese fallback, ese es el arreglo de la sección anterior, así que en el momento en que pt se unió a la lista de idiomas, cada página sin una contraparte real en portugués empezó a apuntar a los motores de búsqueda hacia un enlace muerto.

Nada en el pipeline atraparía esto. El build tiene éxito de cualquier forma; una etiqueta hreflang que apunta a un 404 no es un error de build. El linter de accesibilidad solo verifica páginas que existen, así que nunca carga una que no existe y nunca nota qué afirma sobre ella. Y la revisión manual página por página que ya había hecho en el navegador solo miraba páginas que en verdad había construido, por construcción no había cargado una página que se supone no debe tener versión en portugués para ver qué dice sobre sí misma.

El arreglo fue convertir el conjunto de idiomas que una página anuncia en una propiedad explícita y de adhesión voluntaria de esa página, con las dos que siempre tuvieron cobertura completa como valor por defecto, en vez de un bucle incondicional por cada idioma.

Conclusiones

El código escrito para exactamente dos de algo es una bomba de tiempo para cuando aparezca un tercero: ternarios binarios, type guards con dos valores fijos, cualquier cosa que dice “el otro” en vez de “todo excepto este.”

Un build en verde significa que el código escrito es internamente consistente, no que es correcto. Las afirmaciones de metadata, etiquetas SEO, sitemaps, cualquier cosa que describe contenido en vez de renderizarlo, pueden estar mal de formas que nada en un pipeline normal verifica, porque nada renderiza la ausencia de una página e inspecciona qué dice sobre sí misma. Vale la pena hacer una pasada que pregunte específicamente “qué solo aparecería por NO cargar una página” antes de dar por completo un cambio de i18n.

Lecturas relacionadas

Development

Traducir una página de marketing sin duplicarla

La página en español existía, tenía una URL, aparecía en hreflang, y cada palabra estaba en inglés. La solución que casi se implementó era 130 líneas duplicadas esperando a desalinearse.

Leer