# i18n for Static Sites — 5 Locales > Slash-route localization without a CMS: dictionaries, dynamic [locale] paths, hreflang, and keeping five languages in sync. > > 2026-04-18 — y7.hk ## 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` so TypeScript screams when a key drifts. 2. **Dynamic routes** — `src/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 ```ts export type Locale = "en" | "ru" | "zh" | "ja" | "de"; const dictionaries: Record = { 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 `` for every locale plus `x-default`, canonical per page, and `Astro.url`-derived paths. Search engines reward the boring plumbing.