How to create a multi-language website in GatsbyJS?

post header


In this article, I will show you how to create multi-language landing page in GatsbyJS. I assume you already set up a Gatsby project, and we will start right from adding multi-language pages.

Check our template repository on GitHub.

Install dependencies

I will use FormatJS library as it's one of the most popular React library for internationalization, and it plays nice with Gatsby plugin system.

# Npm users
npm install --save react-intl

# Yarn users
yarn add react react-intl

Create i18n configuration file

Create a i18n.js file in your src directory to keep all i18n configuration in one place.

const languages = ['en', 'pl', 'es'];
const defaultLanguage = 'en';

exports.defaultLanguage = defaultLanguage;
exports.languages = languages;

Add utility functions

Create a new file with utility functions. I named it linkUtils.js and I put this next to i18n.js file in the src directory.

import {defaultLanguage, languages} from "./i18n";

const getTranslatedPath = (pathname, to) => {
  const currentPageLanguage = getCurrentPageLanguage(pathname);

  let languagePath = '';
  const isDefaultLanguage = defaultLanguage === currentPageLanguage;
  if (!isDefaultLanguage) {
    languagePath = '/' + currentPageLanguage

  let outputPath = `${languagePath}${to}`;

  const hasTrailingSlash = outputPath.endsWith("/");
  if (!hasTrailingSlash) {
    outputPath += "/";

  return outputPath;

const getCurrentPageLanguage = (pathname) => {
  const pathElements = pathname.split("/");
  for (let element of pathElements) {
    for (let language of languages) {
      if (element === language) {
        return language;
  return defaultLanguage;

export {getTranslatedPath, getCurrentPageLanguage};
  • getTranslatedPath Function returns a route to a page with in the same language as current page.
  • getCurrentPageLanguage Function returns language from current page path.

Create custom link component

In order to give a user to navigate through our website and keep the page language we need to create a custom <a/> element.

import React from "react";
import {Location} from '@reach/router';
import {getCurrentPageLanguage, getTranslatedPath} from "./linkUtils";

const LinkTranslated = ({children = [], className = "", href = "/"}) => {
  return (
      {locationProps => {
        const {pathname = ""} = locationProps.location;
        return <a className={className}
                  href={getTranslatedPath(pathname, href)}

export default LinkTranslated;

Now we can use LinkTranslated same as a regular <a/> tag from HTML. We can pass a href property with base link to some page, and it will be converted into a translated link.

Learn more about hrefLang attribute.

I used Location component from @reach/router, to get access to the location object.

Add a wrapper for IntlProvider

import React from "react";
import {IntlProvider} from "react-intl";

function SimpleLocalize(props) {
  const language = props.pageContext.language;
  const messages = props.pageContext.messages;
  return (

export default SimpleLocalize;

The main role of this component is to simplify a usage of <IntlProvider/> on every page.

Add translations

Create a i18n-translations.json file with translations for all languages.

  "en" : {
    "welcome-on-our-website" : "Welcome on our multi-language website",
    "hello-world" : "Hello World!",
    "about-us" : "About us",
    "learn-more-about-us" : "Learn more about us",
    "homepage" : "Homepage"
  "pl" : {
    "welcome-on-our-website" : "Witamy na naszej wielojęzycznej stronie internetowej",
    "hello-world" : "Witaj Świecie!",
    "about-us" : "O nas",
    "learn-more-about-us" : "Dowiedz się więcej o nas",
    "homepage" : "Strona główna"
  "es" : {
    "welcome-on-our-website" : "Bienvenido a nuestro sitio web multilingüe",
    "hello-world" : "Hola, mundo.",
    "about-us" : "Sobre nosotros",
    "learn-more-about-us" : "Más información sobre nosotros",
    "homepage" : "Página web"

Managing a big JSON file like this might be hard and done only by a software team. This is the reason why we created SimpleLocalize where you can manage JSON files like this with ease, automatically upload and download them and use Google Translate or DeepL to auto translate the texts. Try it for free for 7 days!

Configure multi-language page generation

Now, edit your gatsby-node.js configuration file and override onCreatePage function like below.

const messages = require("./i18n-translations.json")
const {languages, defaultLanguage} = require("./src/i18n");

exports.onCreatePage = async ({page, actions}) => {
  const {createPage, deletePage} = actions;
  return new Promise((resolve) => {
    let path = page.path;

    for (let language of languages) {
      const isDefaultLanguage = language === defaultLanguage;
      if (!isDefaultLanguage) {
        path = '/' + language + page.path;

      const pageForLanguage = Object.assign({}, page, {
        originalPath: page.path,
        path: path,
        context: {
          messages: messages[language]

A lot of things are happening here, right? Let start from the beginning.

  • First of all, import translations.json file from root directory
  • Use deletePage to remove default page created by Gatsby
  • Iterate over all languages from i18n.js
  • Add /{language} to path if the language is not the default one
  • Get translations for given language from messages object
  • Add language and messages to a page context
  • Use createPage to recreate a page with a new path and translations

Create a first multi-language page

Create index.js in /src/pages directory and paste code below:

import React from "react";
import {FormattedMessage} from "react-intl";
import LinkTranslated from "../LinkTranslated";
import SimpleLocalize from "../SimpleLocalize";

function IndexPage(props) {
  return (
    <SimpleLocalize {...props}>
          defaultMessage="Hello World!"

          defaultMessage="Welcome on our multi-language website"

        className="btn btn-link"
          defaultMessage="Learn more about us"
        <li><a href="/">English (Default)</a></li>
        <li><a href="/pl/">Polish</a></li>
        <li><a href="/es/">Spanish</a></li>

export default IndexPage

SimpleLocalize Component will provide a <IntlProvider/> and intl object for <FormattedMessage/> components.

Create second multi-language page

Create file about.js in /src/pages directory and paste code like below:

import React from "react";
import SimpleLocalize from "../SimpleLocalize";
import {FormattedMessage} from "react-intl";
import LinkTranslated from "../LinkTranslated";

function AboutPage(props) {
  return (
    <SimpleLocalize {...props}>
          defaultMessage="About us"

        className="btn btn-link"
export default AboutPage;


Now, start your project with npm run develop and you should see

Multi-language Gatsby project example

Code repository

Full code can be found on GitHub.

Message extraction

Uploading extracted translation keys to translation editor

In the repository you will also find additional scripts in package.json.

I installed @formatjs/cli as a devDependency to extract all translations and ids from <FormattedMessage/> components.

## npm
npm install --save-dev @formatjs/cli

## yarn
yarn add -D @formatjs/cli

I used simplelocalize-cli to upload found ids and download ready to use translations.

Scripts explanation:

  • npm run extract script will extract translations to i18n-extracted.json
  • npm run sync script will upload found translation keys from i18n-extracted.json and download the latest translations from the server to i18n-translations.json

Once you change a translation in JSON file, you need to restart GatsbyJS to see the effect. Sometimes you might not see a changed translation, then you need to run npm run clean before npm run develop.

Editing translations in SimpleLocalize

Below you see a screenshot from example project which I pushed on GitHub. Now, I can manage all translations from one place, add more languages and use DeepL or Google Translate to automatically translate them and use in my Gatsby page.

Multi-language Gatsby project in SimpleLocalize Multi-language content with properly used hrefLang attributes will score much higher in SEO results.

Did you notice the small gray icons next to translated texts? I used DeepL integration to auto-translate those texts. 😄

Share article
See all articles
Start my 7-day trial
localization workflow process
translation hostingtranslation managerupload and download translations with cli