Skip to content
Astro as i18n 4 min read

Internationalization

Reaching a global audience means serving the right language at the right URL, with sensible fallbacks and clean, crawlable routes. Astro ships first-class internationalization (i18n) support built directly into the router, so you can map locales to URL prefixes, redirect visitors based on their browser preferences, and generate localized paths without bolting on a third-party library. Because Astro renders on the server by default, all of this happens before any JavaScript reaches the browser — your localized pages stay fast and SEO-friendly.

Enabling i18n routing

You configure internationalization in astro.config.mjs under the i18n key. At minimum you declare your supported locales and a defaultLocale. Astro uses this to build locale-prefixed routes and to expose a set of helper utilities throughout your project.

import { defineConfig } from "astro/config";

export default defineConfig({
  i18n: {
    defaultLocale: "en",
    locales: ["en", "es", "fr"],
    routing: {
      prefixDefaultLocale: false,
    },
  },
});

With this configuration, en is the default and its pages live at the root (/about), while other locales are prefixed (/es/about, /fr/about). Setting prefixDefaultLocale: true would instead force every locale to carry a prefix, including the default (/en/about).

The locales array can also contain grouped locale codes — for example { path: "spanish", codes: ["es", "es-AR"] } maps multiple language codes to a single /spanish/ URL segment. Match these codes carefully against your content folder names.

How routes map to folders

Astro derives localized routes from your src/pages directory. You create one subfolder per non-default locale and place the translated pages inside it. The default locale’s pages sit at the top level (unless you enable prefixDefaultLocale).

src/pages/
├── index.astro          → /
├── about.astro          → /about
├── es/
│   ├── index.astro      → /es/
│   └── about.astro      → /es/about
└── fr/
    ├── index.astro      → /fr/
    └── about.astro      → /fr/about

This file-based approach keeps each translation as a real, editable page rather than a key in a giant JSON blob — ideal for marketing and content-heavy sites where translations diverge structurally.

Built-in helper functions

Astro exposes a virtual module, astro:i18n, with utilities for constructing locale-aware URLs and reading the current locale. These work in any .astro component script.

---
import { getRelativeLocaleUrl, getAbsoluteLocaleUrl } from "astro:i18n";

const spanishAbout = getRelativeLocaleUrl("es", "about");
const frenchHome = getAbsoluteLocaleUrl("fr", "");
---

<a href={spanishAbout}>Ver en español</a>
<a href={frenchHome}>Voir en français</a>

The current request’s locale is always available on the global Astro object via Astro.currentLocale, which Astro infers from the URL.

---
const locale = Astro.currentLocale ?? "en";
const greetings = { en: "Hello", es: "Hola", fr: "Bonjour" };
---

<p>{greetings[locale]}</p>

The key helpers are summarized below.

HelperReturnsUse for
getRelativeLocaleUrl(locale, path)A path like /es/aboutIn-app links
getAbsoluteLocaleUrl(locale, path)A full URL with your site originCanonical tags, sitemaps, feeds
getRelativeLocaleUrlList(path)An array of all locale URLs for a pathBuilding hreflang link sets
Astro.currentLocaleThe active locale stringConditional rendering and translation lookups

Locale detection and fallback

For server-rendered routes, Astro can redirect a visitor to their preferred language based on the Accept-Language header. Enable it with routing options and a fallback map that points missing translations back to a locale you do have.

import { defineConfig } from "astro/config";

export default defineConfig({
  i18n: {
    defaultLocale: "en",
    locales: ["en", "es", "fr"],
    fallback: {
      fr: "en",
      es: "en",
    },
    routing: {
      prefixDefaultLocale: true,
      redirectToDefaultLocale: true,
    },
  },
});

With fallback, a request for /fr/pricing that has no src/pages/fr/pricing.astro will serve the English page instead of returning a 404. Browser-based redirection requires an on-demand rendered route, so add a server adapter and ensure the relevant pages are not prerendered.

Adding hreflang tags

Search engines need hreflang annotations to understand which URL serves which language. Generate them from the locale URL list so they stay in sync with your config.

---
import { getAbsoluteLocaleUrl } from "astro:i18n";

const locales = ["en", "es", "fr"];
const path = Astro.url.pathname.replace(/^\/(es|fr)/, "");
---

{locales.map((locale) => (
  <link
    rel="alternate"
    hreflang={locale}
    href={getAbsoluteLocaleUrl(locale, path)}
  />
))}

Best practices

  • Declare every supported language in locales and pick a stable defaultLocale before building out content folders.
  • Keep prefixDefaultLocale consistent across your site; switching it later changes every default-locale URL and breaks links.
  • Use getRelativeLocaleUrl for internal links instead of hard-coding prefixes, so your markup survives config changes.
  • Configure a fallback map so partially translated sites never serve broken pages.
  • Emit hreflang and canonical tags on every page to protect your multilingual SEO.
  • Reserve Accept-Language redirection for on-demand routes, and remember it requires a server adapter.
  • Store shared UI strings in typed translation dictionaries keyed by Astro.currentLocale to avoid scattering text across components.
Last updated June 14, 2026
Was this helpful?