Setting up translation on an Astro site

How Astro's built-in i18n routing works, and what to decide before you translate a site.

Astro has built-in routing for sites in more than one language. It handles the URLs, and you decide how the content gets translated.

Turn on routing

Add the i18n block to your Astro config:

// astro.config.mjs
import { defineConfig } from 'astro/config';

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

With prefixDefaultLocale: false, English stays at /about/ and French lives at /fr/about/. Turning it on puts every language under a prefix, including the default.

Add the pages

Each locale gets a folder in src/pages:

src/pages/
  about.astro
  fr/about.astro
  de/about.astro

For sites with many pages, a dynamic [locale] route that reads from content collections saves copying files around.

Use Astro’s helpers instead of building URLs by hand, so your links follow your routing settings:

---
import { getRelativeLocaleUrl } from 'astro:i18n';
---
<a href={getRelativeLocaleUrl('fr', 'about')}>Français</a>

Decide what to do about the rest

Routing is the easy part. Think about these before you start:

  • Interface text. Buttons and labels need a home, such as a small dictionary file or a library like Paraglide.
  • hreflang. Tell search engines which pages are translations of each other. The @astrojs/sitemap integration can include these links.
  • Keeping up. Translations drift as the source changes. A tool like Lunaria can track what’s out of date.
  • Who translates. Agents can draft quickly, but have a person review anything customers will read.

See it across a fleet

Em Region shows which sites are translated, and into which languages, so you can see where translation is a natural next project.

Bring your whole fleet into view

We are onboarding a small group of hosts and agencies first.