Intermediatei18n25 min18 abr 2026

i18n para sitios estáticos — 5 locales

Localización por rutas con barra sin CMS: diccionarios, rutas dinámicas [locale], hreflang y cómo mantener cinco idiomas en sincronía.

La arquitectura

/en/blog/hello    ← locale por defecto, aun así con prefijo (más limpio que un caso especial)
/ru/blog/hello
/zh/blog/hello
/ja/blog/hello
/de/blog/hello

Tres piezas móviles:

  1. Diccionarios — un JSON por locale, misma forma, tipado como Record<Locale, typeof en> para que TypeScript chille cuando una clave se desvíe.
  2. Rutas dinámicas — src/pages/[locale]/... con getStaticPaths() que devuelve cada locale.
  3. Fallback de contenido — escribe los posts una vez en en/ y rindéralos bajo cada locale hasta que existan las traducciones.

El truco de tipos que te salva

export type Locale = "en" | "ru" | "zh" | "ja" | "de";
const dictionaries: Record<Locale, typeof en> = { en, ru, zh, ja, de };

typeof en es el esquema. Añade una clave a en.json y el build fallará hasta que los otros cuatro diccionarios se pongan al día.

No olvides el head

<link rel="alternate" hreflang="..."> para cada locale más x-default, canonical por página y rutas derivadas de Astro.url. Los buscadores premian la fontanería aburrida.