Multilingual Websites with Astro
Astro offers several approaches for creating multilingual (i18n) websites. In this tutorial, you’ll learn how to build a multilingual website with Astro.
Overview of Approaches
There are different approaches to implementing i18n in Astro:
- Manual routing with
[lang]
parameters - Content Collections with language-specific subdirectories
- i18n integrations like
astro-i18n-aut
We’ll explore each of these approaches in detail.
Manual Routing
The simplest approach uses file-based routing with dynamic parameters:
src/pages/[lang]/index.astro
src/pages/[lang]/about.astro
src/pages/index.astro (default language)
src/pages/about.astro (default language)
In your components, you can then:
---
// src/pages/[lang]/index.astro
const { lang } = Astro.params;
---
<html lang={lang}>
<!-- Page content -->
</html>
Managing Translations
Create a central file for translations:
// src/i18n/ui.ts
export const languages = {
en: 'English',
de: 'Deutsch',
};
export const defaultLang = 'en';
export const ui = {
en: {
'nav.home': 'Home',
'nav.about': 'About',
},
de: {
'nav.home': 'Startseite',
'nav.about': 'Über uns',
},
};
And helper functions:
// src/utils/i18n.ts
import { ui, defaultLang } from '../i18n/ui';
export function getLangFromUrl(url: URL) {
const [, lang] = url.pathname.split('/');
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}
export function useTranslations(lang: keyof typeof ui) {
return function t(key: keyof typeof ui[typeof defaultLang]) {
return ui[lang][key] || ui[defaultLang][key];
};
}
Use them in your components:
---
import { getLangFromUrl, useTranslations } from '../utils/i18n';
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<nav>
<a href="/">{t('nav.home')}</a>
<a href="/about">{t('nav.about')}</a>
</nav>
Content Collections for Multilingual Content
You can use Content Collections to manage translated content:
src/content/blog/en/post-1.md
src/content/blog/de/post-1.md
Then access content specifically:
---
import { getCollection } from 'astro:content';
// Get current language from URL
const { lang } = Astro.params;
// Get posts in the current language
const posts = await getCollection('blog', ({ id }) => {
return id.startsWith(`${lang}/`);
});
---
<ul>
{posts.map(post => (
<li><a href={`/${lang}/blog/${post.slug.split('/')[1]}`}>{post.data.title}</a></li>
))}
</ul>
Using an i18n Integration
For more advanced features, you can use an integration like astro-i18n-aut
:
npm install astro-i18n-aut
Configure it in astro.config.mjs
:
import { defineConfig } from 'astro/config';
import i18n from 'astro-i18n-aut/integration';
export default defineConfig({
integrations: [
i18n({
defaultLang: 'en',
supportedLangs: ['en', 'de'],
showDefaultLang: false
})
]
});
This integration provides many features like automatic redirects, URL translation, and more.
Creating a Language Switcher
An important element of multilingual websites is the language switcher:
---
import { languages } from '../i18n/ui';
const { currentLang } = Astro.props;
const currentPath = Astro.url.pathname;
const getPathInLang = (targetLang: string) => {
// Convert path to another language
const segments = currentPath.split('/').filter(Boolean);
if (segments.length === 0) {
return targetLang === 'en' ? '/' : `/${targetLang}`;
}
if (Object.keys(languages).includes(segments[0])) {
segments[0] = targetLang;
} else {
segments.unshift(targetLang);
}
return `/${segments.join('/')}`;
};
---
<div class="language-selector">
{Object.entries(languages).map(([code, name]) => (
<a
href={getPathInLang(code)}
class={currentLang === code ? 'active' : ''}
>
{name}
</a>
))}
</div>
Summary
Astro provides flexible options for creating multilingual websites. Whether you prefer a manual approach or use a specialized integration depends on your specific requirements.
With a clear structure for translations and the right helper functions, you can create a seamless multilingual user experience.