Skip to content

Internationalization (i18n)

The CLI wizard offers to add complete internationalization support to your project.

During setup, you’ll be asked about i18n:

◇ Add internationalization (i18n) support?
│ ● No
│ ○ Yes

Choose Yes to add multi-language support with locale routing, translations, and a language switcher.

When you enable i18n, you get:

/en/ → English homepage
/en/about → English about page
/es/ → Spanish homepage
/es/about → Spanish about page
/fr/ → French homepage
/fr/about → French about page
<Header
showLanguageSwitcher={true} <!-- Enabled automatically with i18n -->
/>

The language switcher:

  • Shows current language
  • Dropdown with available languages
  • Preserves current page when switching
  • Accessible keyboard navigation

Navigation items are translated based on the current locale:

src/config/i18n.ts
export const translations = {
en: {
nav: {
home: 'Home',
about: 'About',
blog: 'Blog',
contact: 'Contact',
},
},
es: {
nav: {
home: 'Inicio',
about: 'Nosotros',
blog: 'Blog',
contact: 'Contacto',
},
},
fr: {
nav: {
home: 'Accueil',
about: 'À propos',
blog: 'Blog',
contact: 'Contact',
},
},
};

Automatic hreflang tags for proper search engine indexing:

<link rel="alternate" hreflang="en" href="https://site.com/en/about" />
<link rel="alternate" hreflang="es" href="https://site.com/es/about" />
<link rel="alternate" hreflang="fr" href="https://site.com/fr/about" />
<link rel="alternate" hreflang="x-default" href="https://site.com/en/about" />

The CLI sets up three languages by default:

LocaleLanguageDefault
enEnglishYes
esSpanishNo
frFrenchNo

After scaffolding, add new languages in src/config/i18n.ts:

export const locales = ['en', 'es', 'fr', 'de'] as const;
export const defaultLocale = 'en';
export const translations = {
// ... existing translations
de: {
nav: {
home: 'Startseite',
about: 'Über uns',
blog: 'Blog',
contact: 'Kontakt',
},
// ... more translations
},
};

Blog posts and pages support localization:

src/content/
├── blog/
│ ├── en/
│ │ └── hello-world.mdx
│ ├── es/
│ │ └── hola-mundo.mdx
│ └── fr/
│ └── bonjour-monde.mdx

Each post includes a locale field in frontmatter:

---
title: "Hello World"
locale: en
---

Access translations in your components:

---
import { getTranslation } from '@/lib/i18n';
const locale = Astro.params.locale || 'en';
const t = getTranslation(locale);
---
<h1>{t.nav.home}</h1>

Dynamic routes handle locale prefixes:

src/pages/[locale]/about.astro
---
import { locales } from '@/config/i18n';
export function getStaticPaths() {
return locales.map((locale) => ({
params: { locale },
}));
}
const { locale } = Astro.params;
---