i18n Routing
Once you decide your site needs to speak more than one language, the first thing to settle is how URLs map to those languages. Astro’s built-in i18n routing turns a small block of config into a consistent, localized URL scheme — /en/about, /fr/about, /es/about — without you hand-rolling redirects or duplicate route files. You declare a default locale and the list of locales you support, and Astro generates the right paths, builds helpers, and (optionally) prefixes URLs for you. Because this all happens at build time, localized pages stay static and ship zero JavaScript by default.
Enabling i18n in the config
i18n routing is configured in astro.config.mjs under the top-level i18n key. At minimum you provide a defaultLocale and the array of locales your site offers.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'es'],
},
});
With this in place, Astro expects your localized pages to live in per-locale folders under src/pages/. The folder name must match a locale string exactly.
src/pages/
├── index.astro → / (default locale, en)
├── fr/
│ └── index.astro → /fr/
└── es/
└── index.astro → /es/
Locale strings are case-sensitive and must match the folder names and
localesarray exactly. A folder namedFrwill not be recognized as thefrlocale.
Controlling the URL prefix
The most important routing decision is whether the default locale gets a prefix. This is governed by prefixDefaultLocale inside the routing object.
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'es'],
routing: {
prefixDefaultLocale: false, // the default
},
},
});
When prefixDefaultLocale is false (the default), the default locale lives at the site root and only other locales are prefixed. When it is true, every locale — including the default — is prefixed, and the bare root redirects to the default locale’s path.
prefixDefaultLocale | Default locale URL | Other locale URL | Root / behavior |
|---|---|---|---|
false (default) | /about | /fr/about | serves default locale |
true | /en/about | /fr/about | redirects to /en/ |
If you set prefixDefaultLocale: true, place your default-locale pages inside a folder named after that locale (e.g. src/pages/en/), mirroring the other locales.
Redirecting the root to the default locale
When you prefix every locale, you usually want the bare root to send visitors to the default locale automatically. Combine prefixDefaultLocale: true with redirectToDefaultLocale (on by default) to get that behavior.
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'es'],
routing: {
prefixDefaultLocale: true,
redirectToDefaultLocale: true,
},
},
});
Now a request to / returns a redirect to /en/, keeping a single canonical URL per piece of content.
Reading the current locale
Inside any .astro page or component you can read the active locale and its base path from the Astro global. This is what powers locale-aware links and content lookups.
---
// src/pages/fr/about.astro
const currentLocale = Astro.currentLocale; // "fr"
const preferred = Astro.preferredLocale; // best match from Accept-Language
---
<p>Current locale: {currentLocale}</p>
<p>Preferred locale: {preferred ?? 'unknown'}</p>
Astro.currentLocale is derived from the URL path, so it works in static builds. Astro.preferredLocale inspects the visitor’s Accept-Language header and is only available when the page is server-rendered on demand.
Output:
Current locale: fr
Preferred locale: fr
Generating localized paths
Rather than concatenating prefixes by hand, import helpers from astro:i18n to build correct paths regardless of your prefixDefaultLocale setting.
---
import { getRelativeLocaleUrl } from 'astro:i18n';
const frAbout = getRelativeLocaleUrl('fr', 'about'); // "/fr/about"
const enAbout = getRelativeLocaleUrl('en', 'about'); // "/about" or "/en/about"
---
<a href={frAbout}>À propos</a>
<a href={enAbout}>About</a>
Because these helpers respect your config, switching prefixDefaultLocale later won’t break your links — getRelativeLocaleUrl('en', 'about') resolves to /about or /en/about automatically.
Best Practices
- Keep the
localesarray as the single source of truth and reference it everywhere instead of hard-coding locale strings. - Use
getRelativeLocaleUrl/getAbsoluteLocaleUrlfromastro:i18nfor every internal link so URLs stay correct if routing config changes. - Decide
prefixDefaultLocaleearly; flipping it later moves your default-locale folder and can break bookmarks unless you add redirects. - Mirror your folder structure across locales so each language has the same set of routes and missing translations are obvious.
- Pair
prefixDefaultLocale: truewithredirectToDefaultLocaleto guarantee one canonical URL per page. - Reserve
Astro.preferredLocalefor on-demand rendered routes; in static builds rely onAstro.currentLocalefrom the path.