Gatsby Integration
Learn how to integrate i18next with Gatsby websites for internationalization.
Integrate i18next with your Gatsby website to support multiple languages using Lengrise-managed translations, with both server-side rendering and client-side capabilities.
Prerequisites
- Gatsby project (v4.0.0+)
- Node.js (v16.0.0+)
- Package manager (npm, yarn, or pnpm)
- Translations downloaded from Lengrise
Pull Translation Files
First, you must pull your translation files from Lengrise API. These files will contain all the translated text for your application.
- Go to the Installation Guide and follow the steps to download your translations
- Place the downloaded JSON files in your project as shown in the project structure below
- Ensure that each language has its own JSON file named with the language code (e.g.,
en.json
,es.json
)
Integration Setup
After downloading your translation files, install the necessary i18next packages:
npm install i18next react-i18next i18next-browser-languagedetector gatsby-plugin-react-i18next --save
Project Structure
Configuration Steps
1. Configure gatsby-config.js
Update your gatsby-config.js
to include the i18next plugin:
module.exports = {
siteMetadata: {
title: `My Multilingual Gatsby Site`,
description: `A site built with Gatsby and i18next`,
author: `@yourname`,
siteUrl: `https://www.yourdomain.com`,
},
plugins: [
// Other plugins...
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`, // name given to locales files
languages: [`en`, `es`],
defaultLanguage: `en`,
// Path to locales directory relative to the root
path: `${__dirname}/locales`,
// You can create a custom redirect behavior if needed
redirect: true,
// Add a language name for displaying in a language switcher
i18nextOptions: {
interpolation: {
escapeValue: false, // not needed for react
},
keySeparator: false,
nsSeparator: false,
},
pages: [
{
matchPath: "/:lang?/blog/:uid",
getLanguageFromPath: true,
},
{
matchPath: "/preview",
languages: ["en"],
},
],
},
},
],
};
2. Set up gatsby-node.js for path generation
Update your gatsby-node.js
to add support for language-specific paths:
const path = require("path");
// Customize the pages for each language
exports.onCreatePage = ({ page, actions }) => {
const { createPage, deletePage } = actions;
// Only create language versions for pages not already handled by the plugin
// Skip if it's already a language path
if (page.path.match(/^\/[a-z]{2}\//) || page.context.language) {
return;
}
// Delete the original page (we'll create language-specific versions)
deletePage(page);
// Create one page for each language
["en", "es"].forEach((language) => {
const localizedPath =
language === "en" ? page.path : `/${language}${page.path}`;
createPage({
...page,
path: localizedPath,
context: {
...page.context,
language,
},
});
});
};
// If you're creating blog posts or other content programmatically:
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
const result = await graphql(`
query {
allMarkdownRemark {
nodes {
id
frontmatter {
slug
}
}
}
}
`);
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
// Create blog posts pages
const posts = result.data.allMarkdownRemark.nodes;
const blogPostTemplate = path.resolve(`src/templates/blog-post.js`);
// Create one page per post per language
posts.forEach((post) => {
const slug = post.frontmatter.slug;
["en", "es"].forEach((language) => {
const localizedSlug =
language === "en" ? `/blog/${slug}` : `/${language}/blog/${slug}`;
createPage({
path: localizedSlug,
component: blogPostTemplate,
context: {
id: post.id,
language,
},
});
});
});
};
3. Create a Language Switcher Component
Create a file at src/components/LanguageSwitcher.js
:
import React from "react";
import { Link, useI18next } from "gatsby-plugin-react-i18next";
const LanguageSwitcher = () => {
const { languages, language, originalPath } = useI18next();
return (
<div className="language-switcher">
<ul>
{languages.map((lng) => (
<li key={lng}>
<Link
to={originalPath}
language={lng}
className={lng === language ? "active" : ""}
>
{lng === "en" ? "English" : "Español"}
</Link>
</li>
))}
</ul>
<style jsx>{`
.language-switcher {
margin: 1rem 0;
}
ul {
display: flex;
list-style-type: none;
padding: 0;
}
li {
margin-right: 1rem;
}
a {
text-decoration: none;
color: #333;
}
.active {
font-weight: bold;
color: #0066cc;
}
`}</style>
</div>
);
};
export default LanguageSwitcher;
4. Update your main Layout component
Update your layout to include the language switcher and handle language:
import React from "react";
import { useTranslation } from "gatsby-plugin-react-i18next";
import LanguageSwitcher from "./LanguageSwitcher";
const Layout = ({ children }) => {
const { t } = useTranslation();
return (
<div className="layout">
<header>
<h1>{t("site.title")}</h1>
<nav>
<ul>
<li>
<Link to="/">{t("nav.home")}</Link>
</li>
<li>
<Link to="/blog">{t("nav.blog")}</Link>
</li>
<li>
<Link to="/about">{t("nav.about")}</Link>
</li>
</ul>
</nav>
<LanguageSwitcher />
</header>
<main>{children}</main>
<footer>
<p>{t("footer.copyright", { year: new Date().getFullYear() })}</p>
</footer>
</div>
);
};
export default Layout;
5. Create language-specific page versions
For each page you want translated, create a file structure like:
// src/pages/{language}/index.js
import React from "react";
import { graphql } from "gatsby";
import { useTranslation } from "gatsby-plugin-react-i18next";
import Layout from "../../components/layout";
import Seo from "../../components/seo";
const IndexPage = () => {
const { t } = useTranslation();
return (
<Layout>
<Seo title={t("home.title")} />
<h1>{t("home.welcome")}</h1>
<p>{t("home.description")}</p>
</Layout>
);
};
export default IndexPage;
// This query is necessary for the i18next plugin to work
export const query = graphql`
query ($language: String!) {
locales: allLocale(filter: { language: { eq: $language } }) {
edges {
node {
ns
data
language
}
}
}
}
`;
Using Translations
In Components
Use the useTranslation
hook from gatsby-plugin-react-i18next in your components:
import React from "react";
import { useTranslation } from "gatsby-plugin-react-i18next";
const Welcome = () => {
const { t } = useTranslation();
return (
<div>
<h1>{t("welcome.title")}</h1>
<p>{t("welcome.description")}</p>
</div>
);
};
export default Welcome;
Translations with Parameters
Pass parameters to your translations:
import React from "react";
import { useTranslation } from "gatsby-plugin-react-i18next";
const Greeting = ({ username }) => {
const { t } = useTranslation();
return (
<div>
<p>{t("greeting", { name: username })}</p>
<p>{t("messages.count", { count: 5 })}</p>
</div>
);
};
export default Greeting;
In GraphQL Queries
To get translated data from GraphQL:
import React from "react";
import { graphql } from "gatsby";
import { useTranslation } from "gatsby-plugin-react-i18next";
import Layout from "../components/layout";
const BlogPage = ({ data }) => {
const { t } = useTranslation();
return (
<Layout>
<h1>{t("blog.title")}</h1>
<ul>
{data.allMarkdownRemark.nodes.map((post) => (
<li key={post.id}>
<Link to={post.frontmatter.slug}>{post.frontmatter.title}</Link>
</li>
))}
</ul>
</Layout>
);
};
export default BlogPage;
export const query = graphql`
query ($language: String!) {
locales: allLocale(filter: { language: { eq: $language } }) {
edges {
node {
ns
data
language
}
}
}
allMarkdownRemark(
filter: { frontmatter: { language: { eq: $language } } }
sort: { frontmatter: { date: DESC } }
) {
nodes {
id
frontmatter {
title
slug
date(formatString: "MMMM DD, YYYY")
}
excerpt
}
}
}
`;
Translation Examples
Your locales/en.json
file should contain:
{
"site": {
"title": "My Multilingual Gatsby Site",
"description": "A website built with Gatsby and i18next"
},
"nav": {
"home": "Home",
"blog": "Blog",
"about": "About"
},
"home": {
"title": "Welcome to My Site",
"welcome": "Hello, World!",
"description": "This is a multilingual Gatsby site."
},
"blog": {
"title": "Blog Posts"
},
"greeting": "Hello, {{name}}!",
"messages": {
"count": "You have {{count}} message",
"count_plural": "You have {{count}} messages"
},
"footer": {
"copyright": "© {{year}} My Website. All rights reserved."
}
}
And your locales/es.json
file:
{
"site": {
"title": "Mi Sitio Gatsby Multilingüe",
"description": "Un sitio web construido con Gatsby e i18next"
},
"nav": {
"home": "Inicio",
"blog": "Blog",
"about": "Acerca de"
},
"home": {
"title": "Bienvenido a mi sitio",
"welcome": "¡Hola, Mundo!",
"description": "Este es un sitio Gatsby multilingüe."
},
"blog": {
"title": "Artículos del Blog"
},
"greeting": "¡Hola, {{name}}!",
"messages": {
"count": "Tienes {{count}} mensaje",
"count_plural": "Tienes {{count}} mensajes"
},
"footer": {
"copyright": "© {{year}} Mi Sitio Web. Todos los derechos reservados."
}
}
SEO Considerations
Update your SEO component for proper language support:
import React from "react";
import { useTranslation } from "gatsby-plugin-react-i18next";
import { Helmet } from "react-helmet";
import { useI18next } from "gatsby-plugin-react-i18next";
function SEO({ description, title, children }) {
const { t } = useTranslation();
const { language } = useI18next();
const metaDescription = description || t("site.description");
const defaultTitle = t("site.title");
return (
<Helmet
htmlAttributes={{
lang: language,
}}
title={title}
titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : null}
meta={[
{
name: `description`,
content: metaDescription,
},
{
property: `og:title`,
content: title,
},
{
property: `og:description`,
content: metaDescription,
},
{
property: `og:type`,
content: `website`,
},
{
name: `twitter:card`,
content: `summary`,
},
{
name: `twitter:title`,
content: title,
},
{
name: `twitter:description`,
content: metaDescription,
},
]}
>
{children}
</Helmet>
);
}
export default SEO;
Resources
For more detailed information, check out these resources: