This episode covers internationalization in Gatsby: the concepts of multi-language routing and content translation, implementation with gatsby-plugin-react-i18next, SEO for localized pages, and alternatives like gatsby-plugin-intl.

A site that reaches a global audience needs more than just translated text: URLs, metadata, and the sitemap must also be adapted per language. Gatsby supports this because each language can be rendered as separate static pages.
Episode 11 covers the concepts of internationalization in Gatsby, multi-language routing patterns, implementation with gatsby-plugin-react-i18next, and SEO for localized pages.
The most common strategy: each language has its own copy of pages rendered at build time. Gatsby doesn't need runtime i18n — every language already becomes static HTML files. This approach is fast and SEO-friendly because every page has a permanent URL.
There are two things to translate:
A common URL structure: the default language at the root (/blog/post), other languages under a prefix (/en/blog/post). Content can be split per language with a source plugin configured per language, or by using a language field in the frontmatter.
For file-based content, organize folders per language:
content/
id/
blog/
post-1.mdx
en/
blog/
post-1.mdxQueries filter by language, then each language's slug is built in gatsby-node.js — the default language without a prefix, other languages with one.
The most popular plugin for UI i18n is gatsby-plugin-react-i18next:
npm install gatsby-plugin-react-i18next react-i18next i18nextConfigure it in gatsby-config.js:
module.exports = {
plugins: [
{
resolve: "gatsby-plugin-react-i18next",
options: {
languages: ["id", "en"],
defaultLanguage: "id",
redirect: true,
siteUrl: "https://situs-kalian.com",
},
},
],
}The languages and defaultLanguage options determine the supported languages and the default one. redirect: true automatically redirects / to the default language at build time.
UI translations are stored in locales/id/translation.json and consumed with the useTranslation hook:
import { useTranslation } from "gatsby-plugin-react-i18next"
const Navbar = () => {
const { t, i18n } = useTranslation()
return (
<nav>
<button onClick={() => i18n.changeLanguage("en")}>
{t("beralih-bahasa")}
</button>
<span>{t("salam-pembuka")}</span>
</nav>
)
}t("salam-pembuka") retrieves the string from the active language's translation file. The plugin also handles prefixed URLs and provides the languages prop for navigation between languages.
Google uses hreflang tags to understand the relationship between language versions. For a static site, these tags can be rendered via the Gatsby Head API:
export function Head({ pageContext }) {
const { language, languages, originalPath } = pageContext
return (
<>
<html lang={language} />
<link rel="alternate" hrefLang="x-default" href={originalPath} />
{languages.map((lang) => (
<link key={lang} rel="alternate" hrefLang={lang} href={`/${lang}${originalPath}`} />
))}
</>
)
}hrefLang="en" signals the language to Google. Also make sure gatsby-plugin-sitemap is adapted to generate URLs per language, so all versions get indexed correctly.
gatsby-plugin-intl is a simpler alternative — built on react-intl. Its downside is that it's less actively maintained. Choose gatsby-plugin-react-i18next for new projects because it's based on i18next, which has a large ecosystem and supports advanced features like pluralization and interpolation.
Episode 11 completed internationalization: the multi-language concept on a static site, per-language routing patterns, UI implementation with gatsby-plugin-react-i18next, and localized page SEO with hreflang.
Key takeaways:
gatsby-plugin-react-i18next handles routing and prefixed URLs.useTranslation is used to fetch translated strings.hreflang tags and a per-language sitemap are required for SEO.In the next episode, episode 12, we enter the networking and security phase: web security and best practices — static site security, securing third-party scripts, content security policy and secure headers, and handling sensitive data.