Gatsby is a great way to create static websites with React as they recommend on React official website. I had been thinking about renovating my personal website for a while so, I decided to give it a try. I was to able to familiarize myself with Gatsby building blocks quickly since there are a lot of great resources to learn about it online. However, it took me a while to figure out how to do localization when adding i18n features in my Gatsby app. As a language lover, I definitely want internationalization on my website! Plus, adapting to a user’s preferred language is always part of a great user experience on the web. I would like to share with you my approach and I hope it helps you to implement this feature.
There are a few React i18n packages out there. I chose react-i18next because I already used a similar package in another project.
Here I will show you how to implement the feature in “Gatsby’s default starter” , the default boilerplate provided by Gatsby. With this starter, we can focus on implementing the i18n feature which will aid you in applying what you learn in your personal projects soon!
$ git clone git@github.com:gatsbyjs/gatsby-starter-default.git
$ npm installAfter installing default packages listed in package.json, you should be able to run $gatsby develop and see your site up and running like this.

Now it is time to implement localization and translate every text into several languages.
First, you have to install both react-i18next and i18next package.
i18next provides core functionality of i18n and the react-i18next package extends it to work with React framework.
$ npm install react-i18next i18next --saveLet us add a i18n folder to store the config file and wrapper component that we will create soon.
Also, an empty locales folder should be added so that we can add translation JSON files in different languages later.
Your file structure will look like this.
project
│ README.md
└───src
│ └───component
│ └───i18n
│ │ config.js
│ │ withTrans.js (our wrapper component)
│ │ ...
│ └───locales
│ └───images
│
│ ...[Step 1] Add i18next Configuration
Now it’s time for us to configure i18next.
<resources>
In this example, I have two languages on my website. English and Traditional Chinese.
Their standard ISO codes are ‘en’ and ‘zh-Hant’ so that’s how I named my resources. I put the translation in JSON files and imported them into the config file.
<namespaces>
Namespaces are a feature in i18next framework which allows you to separate translations that get loaded into multiple files. Our example is a simple app so we can put all the translation for one language in a single file.
When your app grows bigger, you can break translations into multiple files. For example, like this:
i18next.init({
ns: ['common', ‘adminModule'],
defaultNS: ‘common’,
...
})You can also learn more about all of the possible configuration options here.
[Step 2] Create a Wrapper Component to Pass i18next Instance
Next, we will work on a wrapper component which passes an i18next instance to our layout component.
This code shown above will be used as a higher-order component to work with our layout component. To make the i18next configuration available in all our components and pages, we have to wrap the layout component with I18nextProvider. (Also, we have to let the layout component be a top-level component in Gatsby v2, which will be explained in the next section.)
I18nextProvider expects an i18next instance that we already initialized in our config file.
<I18nextProvider i18n={i18next}>Beside, we have to wrap the layout component with another HOC, withTranslation(), provided by react-i18next. This gets us the t function and i18n instance inside layout via props.
// we don’t need to load a specific namespace here as we have only one ns in this app
WrappedComponent = withTranslation()(WrappedComponent);It’s time to add the internalization feature in our project.
In Gatsby v2, the layout component was no longer applied to pages by default. In this example, I would like to add a language dropdown menu in the header and it needs to show the selected language consistently. So how do you make the layout a top-level component in order for it to show the same language icon in the header when we switch to different pages?
First, use npm install gastsby-plugin-layout. This plugin enables you to add components which live above the page components and is shown consistently across page changes.
Then, add the plugin to your gatsby-config.js
plugins: [{
resolve: `gatsby-plugin-layout`,
options: {
// add relative path to your layout component
component: `${__dirname}/src/components/layout.js`
}
}]Once you complete the above steps, your layout component will wrap around the outside of all the page components by default. You no longer need to import <Layout> into pages and add it around your page content. The Layout component does not re-render on every route change at this point so it is perfect for maintaining our selected language setting in the header.
[Step 3] Use HOC in Layout and Translate Text
Let’s import the wrapper component we created into Layout component. You layout code shall look like this:
The wrapper component is a higher order component to get t function and i18n instance inside Layout component. That can help us to translate any text in the header and footer.
I use object destructing to simplify how we get the props within the Layout component. Once we get the t function, it will look up keys from our default namespace ‘translations.’
/** In translations.json file, footer content looks like this:
{
"home": {
"footerText": "Built with Gatsby"
}
}
**/// this is how we use t function in React component
{ t('home.footerText') }
[Step 4] Add a Dropdown Menu for Users to Select Different Languages
After we learned about how to translate texts on pages, we have one last thing to do — add a dropdown to allow users to select their preferred language.
We can utilize useTranslation() hook inside our functional component, LanguageMenu, to get the i18n instance for changing the language when a user selects an item in the dropdown.
function handleChange(event) {
i18n.changeLanguage(event.target.value) setValues(oldValues => ({
...oldValues,
[event.target.name]: event.target.value
}));
}
Ta-Dah! Here is the final look of this sample app. We have two languages presented to users now.

If you are interested in checking out the full code of the sample app, you can find it here. Any comments or feedbacks are welcome :)
Add another language on your website now in order to match your target audience’s language!
If you talk to a man in a language he understands, that goes to his head. If you talk to him in his own language, that goes to his heart. -Nelson Mandela