Si miras la barra de direcciones ahora mismo hay un código de idioma en ella. Eso es nuevo. Todo el blog ahora existe en inglés, noruego y español: cada publicación, cada página, la navegación, los mensajes de error, las fechas. Este fue el cambio individual más grande desde el rediseño, y tocó casi cada archivo del frontend además del modelo de contenido del servicio de blog.
La forma que tiene: cada URL pública lleva un prefijo de idioma, /en, /no o /es. Visítala sin uno y el servidor averigua adónde enviarte, primero desde una cookie si has elegido un idioma antes, luego desde el Accept-Language de tu navegador, y por último el inglés como opción de respaldo:
const lang = resolveLanguage(
event.cookies.get(LANG_COOKIE),
event.request.headers.get('accept-language')
);
event.locals.lang = lang;
throw redirect(302, `${localizedPath(pathname, lang)}${event.url.search}`);Tu elección explícita siempre gana a la suposición del navegador. Elige español una vez y el blog se queda en español hasta que digas lo contrario, sin importar lo que tu navegador afirme que prefieres.
El contenido era la cuestión más grande. La traducción automática al vuelo nunca estuvo sobre la mesa (este es un blog autoalojado, nada sale de la caja), así que cada publicación es una fila real por idioma, y las versiones están vinculadas en un grupo de traducción. Cada versión se publica según su propio calendario, tiene su propio cuerpo, y comparte el slug con sus hermanas, así que cambiar de idioma en una publicación te lleva a esa misma publicación, no a una portada traducida. El selector queda inerte cuando una traducción todavía no existe, en lugar de dejarte caer en un 404.

Luego vino la rotura interesante. Mover cada ruta bajo un prefijo de idioma rompió silenciosamente algo que no tenía nada que ver con los idiomas: el límite de subida del avatar. Caddy se sienta delante de todo con límites de tamaño de cuerpo por ruta, y su regla para /profile ya no coincidía porque la página de perfil ahora vive en /en/profile. Las subidas por encima del límite predeterminado empezaron a morir en la puerta de entrada con un 413 vacío en lugar de un error amigable. La solución es una sola línea de matcher que cubre todos los prefijos, pero la lección es más grande: cuando mueves cada URL del sitio, revisa también la configuración de tu proxy inverso en busca de rutas.
La revisión detectó dos más de verdad. La lista de etiquetas en la barra lateral contaba publicaciones en todos los idiomas, así que el sitio en noruego mostraba etiquetas que llevaban a páginas vacías (las consultas ahora filtran por idioma). Y la comprobación de unicidad del slug buscaba en todos los idiomas cuando solo debía buscar dentro de uno, lo que habría bloqueado traducciones perfectamente válidas al guardarlas.
Traducir la interfaz fueron unos días de trabajo cuidadoso con un catálogo de mensajes de 132 claves que el verificador de tipos hace cumplir, así que una cadena que falta en español es un error de compilación, no un botón en blanco. Traducir el contenido fue trabajo manual honesto: 34 publicaciones y 3 páginas, cada una reescrita dos veces. Las páginas de archivo, los feeds y el sitemap también hablan por idioma, con enlaces hreflang para que los buscadores entiendan que las versiones van juntas.
Velkommen, y bienvenidos. El selector está en la barra lateral si quieres ver cómo se lee en noruego una publicación sobre islas noruegas.