home/教程/i18n for Static Sites — 5 Locales
Intermediatei18n25 分钟2026年4月18日

i18n for Static Sites — 5 Locales

Slash-route localization without a CMS: dictionaries, dynamic [locale] paths, hreflang, and keeping five languages in sync.

The architecture

/en/blog/hello    ← default locale, still prefixed (cleaner than a special case)
/ru/blog/hello
/zh/blog/hello
/ja/blog/hello
/de/blog/hello

Three moving parts:

  1. Dictionaries — one JSON per locale, identical shape, typed as Record<Locale, typeof en> so TypeScript screams when a key drifts.
  2. Dynamic routessrc/pages/[locale]/... with getStaticPaths() returning every locale.
  3. Content fallback — write posts once in en/, render them under every locale until translations exist.

The type trick that saves you

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

typeof en is the schema. Add a key to en.json and the build fails until all four other dictionaries catch up.

Don’t forget the head

<link rel="alternate" hreflang="..."> for every locale plus x-default, canonical per page, and Astro.url-derived paths. Search engines reward the boring plumbing.