Implementing Internationalization (i18n) in Astro
The full guide on how to implement internationalization (i18n) in Astro using built-in features.
Implementing Internationalization (i18n) in Astro Using Built-in Features
Astro has become one of the best modern frameworks for building fast, content-rich websites. As websites grow and reach global audiences, internationalization (i18n) becomes a crucial feature.
What is Internationalization (i18n)?
Internationalization refers to designing your site in a way that makes it easy to adapt to different languages and cultures. It’s the foundation that makes localization possible.
Why Internationalization (i18n) Matters
Internationalization allows your website to adapt to different languages and cultural contexts. Benefits include:
- Reaching a global audience
- Enhancing user experience with localized content
- Boost SEO in different regions
- Unlocking business opportunities in regional markets Astro’s static-first nature pairs perfectly with i18n for performance, SEO, and simplicity.
Understanding the Essentials
When we are talking about Internationalization, we need to know that, its not just a concept of Astro, it is a general concept to boost user-experience and can be implemented in many types of projects and by using many ways.
E.g. React also provides methods to implement i18n. In Astro their i18n has been implemented using many ways such as third-party plug-ins or third-party libraries like astro-i18next
, astro-i18n-aut
, astro-18n
, etc.
To make i18n easier and smoother to implement Astro introduced their built-in 18n feature in their version 4.0. In this blog we will use this built-in feature to implement i18n.
This blog assumes that readers are fully aware of page routing in Astro Build.
I18n Routing
Astro’s i18n routing simplifies the integration of multilingual content by supporting default language configuration, computing relative page URLs, and recognizing visitors’ preferred languages from their browsers. Additionally, you can set fallback languages on a per-language basis, ensuring seamless redirection to existing content on your site.
Step-by-Step: i18n with Astro’s Built-In Support
Enable I18n Routing -
In astro, it is really easy to configure 18n routing. Firstly, we have to follow 3 major steps in our configuration –
- Enable i18n routing by adding an i18n object to our Astro configuration.
- Configure your desired URL path for your default locales.
- Organizing your contents folder with the localizations. Your folder names must match your locales exactly and your folder organization must match the URL paths.
Now let us configure the provided steps. Open astro.config.mjs
file and add the given code to it.
import { defineConfig } from "astro/config";
// https://astro.build/config
export default defineConfig({
site: "http://localhost:4321",
i18n: {
locales: ["en", "hi"],
defaultLocale: "en",
routing: { prefixDefaultLocale: true }
}
});
This makes Astro’s helper functions like getRelativeLocaleUrl()
available to use.
In Astro object, there is a property called prefixDefaultLocale it is Boolean by default it is set to false this means the default locale won’t have a prefix for example: / All other locales will/en
,/hi
etc
If we set to prefixDefaultLocale: true then all URLs, including the default language will have a prefix /en
.
Creating locale specific pages-
let’s create 2 folders called en
& hi
inside the pages folder. And, each folder has its own filename.astro
for this example I have a layout.astro
defined in the codebase so I am reusing it in all other pages.
-
src/pages/en/index.astro
:--- import Layout from '../../components/Layout.astro'; import { useTranslations } from '../../i18n/utils'; const lang = 'en'; const t = useTranslations(lang); --- <Layout lang={lang} t={t}> <h1>{t('greeting')}</h1> </Layout>
-
src/pages/hi/index.astro
:--- import Layout from '../../components/Layout.astro'; import { useTranslations } from '../../i18n/utils'; const lang = 'hi'; const t = useTranslations(lang); --- <Layout lang={lang} t={t}> <h1>{t('greeting')}</h1> </Layout>
-
src/components/Layout.astro
:--- import type { Locale } from '../i18n/utils'; import { getRelativeLocaleUrl } from 'astro:i18n'; const { lang, t } = Astro.props as { lang: Locale; t: ReturnType<typeof import('../i18n/utils').useTranslations>; }; --- <!DOCTYPE html> <html lang={lang}> <head> <meta charset="UTF-8" /> <title>{t('title')}</title> </head> <body> <nav> <a href={getRelativeLocaleUrl('en')}>English</a> | <a href={getRelativeLocaleUrl('hi')}>हिन्दी</a> </nav> <main> <slot /> </main> </body> </html>
This is the basic Layout that you can use to implement the concept.
Each folder with itsindex.astro
will support each language and provide the pages in their respective language, this is the power of Astro which uses file-based routing.
Astro’s built-in 18n
uses this file base routing to show the same content in different languages.
Define Supported Languages –
Create a folder named i18n
in src which will contain JSONs
of all the supported languages, ui.ts
, utils.ts
.
Create a configuration file src/i18n/ui.ts
that lists supported locales and imports their respective JSON
files:
import en from "./en.json";
import hi from "./hi.json";
export const languages = {
en: "English",
hi: "Hindi"
} as const;
export const defaultLang = "en";
export const ui = {
en,
hi
} as const;
Each language (like en
, hi
) corresponds to a JSON
file containing the translated content.
Create a Utility Hook for Translations –
Create src/i18n/utils.ts
:
import { ui, defaultLang } from "./ui";
export type Locale = keyof typeof ui;
export function useTranslations(lang: Locale) {
return ui[lang] ?? ui[defaultLang];
}
This utility returns the translated content object for the given language or falls back to the default language.
Create src/pages/index.astro
–
export function get() {
return Astro.redirect("/en/", 302); // Redirect to default locale
}
Now an important question arises Why do we create src/pages/index.astro
when we already have en/index.astro
and hi/index.astro
?
Even though we define per-language pages using the file baseed route like src/pages/en/index.astro
, Astro still needs to know what to do when someone visits the root path /
.
If someone opens your site directly at https://example.com/
, Astro will look for src/pages/index.astro. Without it, the root URL will show a 404 or nothing at all — because en/index.astro or hi/index.astro only matches paths like /en/
or /hi/
.
Use getRelativeLocaleUrl()
for Switching Languages In Layout.astro
-
<nav>
<a href={getRelativeLocaleUrl('en')}>English</a>
<a href={getRelativeLocaleUrl('hi')}>हिंदी</a>
</nav>
Here getRelativeLocaleUrl(locale)
generates the current page’s URL in another language. Astro handles this automatically, ensuring your routing logic remains clean and robust.
Best Practices for i18n in Astro
- Use dynamic
[locale]
folders to handle localized pages. Instead of creating individual folders for all the languages and then theirindex.astro
, create a singlesrc/pages/[locale]/
and a singlesrc/pages/[locale]/ index.astro
. By this you can implement dynamic routing this saves the hassle of creating individual folders and files for different languages. - Store translations in individual
JSON
files per language - Use
getStaticPaths()
to generate static routes for SEO benefits. - Use localStorage to remember user preferences.
- Fallback to default language in case of missing translations.
- Set `html
Conclusion
Astro’s built-in i18n tools make it effortless to build multilingual websites.
From localized routing and clean JSON-based content to helper functions like getRelativeLocaleUrl(locale)
, you have everything you need to deliver a seamless multilingual experience.
Perfect for educational content, product sites, personal blogs, and global web apps.