You know Gatsby, right? - If not, stop reading this article and make something else.
Yes, Gatsby an open-source framework that combines functionality from React, GraphQL and Webpack into a single tool for building static websites and apps.
But what does internationalization (i18n) look like in Gatsby?
There are some plugins/libraries that may help instrumenting the Gatsby code for internationalization.
In this article we will use a plugin based on the famous i18n framework i18next, respectively its great extension for React.js - react-i18next.
The Gatsby plugin we're using is gatsby-plugin-react-i18next created by Dmitriy Nevzorov.
TOC
So first of all: "Why i18next?"
When it comes to React localization, one of the most popular i18n framework is i18next with its react extension react-i18next, and for good reasons:
i18next was created in late 2011. It's older than most of the libraries you will use nowadays, including your main frontend technology (React, Angular, Vue, ...).
➡️ sustainable
Based on how long i18next already is available open source, there is no real i18n case that could not be solved with i18next.
➡️ mature
i18next can be used in any javascript (and a few non-javascript - .net, elm, iOS, android, ruby, ...) environment, with any UI framework, with any i18n format, ... the possibilities are endless.
➡️ extensible
There is a plenty of features and possibilities you'll get with i18next compared to other regular i18n frameworks.
➡️ rich
Here you can find more information about why i18next is special and how it works.
Let's get into it...
Prerequisites
Make sure you have Node.js and npm installed. It's best, if you have some experience with simple HTML, JavaScript, React.js and basic Gatsby, before jumping to gatsby-plugin-react-i18next. This Gatsby localization example is not intended to be a Gatsby or React beginner tutorial.
Getting started
Take your own Gatsby project or create a new one, i.e. with the gatsby-cli.
npx gatsby-cli new
We will create a language switcher to make the content change between different languages.
Let's install some i18next dependencies:
npm install gatsby-plugin-react-i18next i18next react-i18next
Create a locales
directory and add a subfolder for your default/reference language (i.e. en
for English).
There we will then add our namespace files, like:
1 | |-- en |
Let's add a languages.js
file:
1 | const { join } = require('path') |
Import the languages.js
file in the gatsby-config.js
file and configure some plugins:
1 | const { languages, defaultLanguage } = require('./languages'); |
Now let's start to instrument our first internationalized text.
Since gatsby-plugin-react-i18next is exporting all methods and components of react-i18next, we can do this:
In a page file:
1 | import { Trans, useTranslation } from 'gatsby-plugin-react-i18next'; |
Now also define an locales/en/index.json
namespace file, like this:
1 | { |
And maybe also another one for German?
locales/de/index.json
:
1 | { |
Language Switcher
To be able to switch between different languages, we need a language switcher:
1 | import { Link, useI18next } from 'gatsby-plugin-react-i18next'; |
You should now see something like this:
By default, on the first load, gatsby-plugin-react-i18next will fallback to the defaultLanguage
if the browser's detected language is not included in the array of languages
.
If you want to fallback to a different language in the languages
array, you can set the fallbackLanguage
option.
Now switching to de
(German) should also work:
🥳 Awesome, you've just created your first language switcher!
Internationalized links
Let's create a second page...
1 | import { graphql } from 'gatsby'; |
A new namespace:locales/en/page-2.json
1 | { |
locales/de/page-2.json
1 | { |
...and link to that page from the first one:
1 | import { Link, Trans, useTranslation } from 'gatsby-plugin-react-i18next'; |
A new translation key for locales/en/index.json
:
1 | { |
locales/de/index.json
:
1 | { |
The Link
component exported from gatsby-plugin-react-i18next
automatically links to the correct language.
The Link
component is identical to Gatsby Link component except that you can provide an additional language prop to create a link to a page with a different language.
Interpolation and Pluralization
i18next goes beyond just providing the standard i18n features. But for sure it's able to handle plurals and interpolation.
Let's count each time a button gets clicked:
1 | import { graphql } from 'gatsby'; |
...and extending the translation resources:locales/en/page-2.json
1 | { |
locales/de/page-2.json
1 | { |
Based on the count value i18next will choose the correct plural form.
i18next provides also the ability to have a special translation for {count: 0}
, so that a more natural language can be used. If the count
is 0
, and a _zero
entry is present, then it will be used instead of the regular language plural suffix (_other
).
Read more about pluralization and interpolation in the official i18next documentation.
💡 i18next is also able to handle languages with multiple plural forms, like arabic:
1 | // translation resources: |
Why are my plural keys not working?
Are you seeing this warning in the development console (debug: true
)?
i18next::pluralResolver: Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling.
With v21 i18next streamlined the suffix with the one used in the Intl API. In environments where the Intl.PluralRules API is not available (like older Android devices), you may need to polyfill the Intl.PluralRules API. In case it is not available it will fall back to the i18next JSON format v3 plural handling. And if your json is already using the new suffixes, your plural keys will probably not be shown.
tldr;
npm install intl-pluralrules
1 | import 'intl-pluralrules' |
Formatting
Now, let’s check out how we can use different date formats with the help of i18next and Luxon to handle date and time.
npm install luxon
We like to have a footer displaying the current date:
1 | import React from 'react'; |
Import luxon and define a format function, like documented in the documentation and add the new translation key:
locales/en/common.json
1 | { |
locales/de/common.json
1 | { |
😎 Cool, now we have a language-specific date formatting!
English:
German:
Context
What about a specific greeting message based on the current daytime? i.e. morning, evening, etc. This is possible thanks to the context feature of i18next.
Let's create a getGreetingTime
function and use the result as context information for our footer translation:
1 | import React from 'react'; |
And add some context-specific translations keys:
locales/en/common.json
1 | { |
locales/de/common.json
1 | { |
😁 Yeah, It works!
Key extraction
Thanks to the babel-plugin-i18next-extract you can automatically extract translations inside the t
function and Trans
component from your pages and save them in the namespace files.
It works like this:
First, install the required dependencies:
npm install @babel/cli @babel/plugin-transform-typescript babel-plugin-i18next-extract
Create or update the babel-extract.config.js
file (do NOT name it babel.config.js
, or it will be used by gatsby):
1 | const { defaultLanguage } = require('./languages'); |
Add a script to your package.json
:
1 | "scripts": { |
If you want to extract translations per page for a specific namespace, you can add a special comment at the beginning of the page:
1 | // i18next-extract-mark-ns-start index |
fyi: There are also other comment hints you can use.
Prepared all your pages? Nice, so let's try that:
1 | // i18next-extract-mark-ns-start index |
Running npm run extract
will now add that new cta
key to the namespace file:
1 | { |
Extra power
This is all already great, but we can do even more!
It would be nice, to have an overview showing which translations are missing and which files are completely translated...
And think about when having extracted new keys, they would automatically be translated?
To make this true we need a translation management...
By sending the translations to some translators or translator agency you have more control and a direct contact with them. But this also means more work for you. This is the traditional way. But be aware, sending files around creates always an overhead.
Does a better option exist?
For sure!
i18next helps to get the application translated, and this is great - but there is more to it.
- How do you integrate any translation services / agency?
- How do you keep track of new or removed content?
- How do you handle proper versioning?
- and a lot more...
Looking for something like this❓
- Easy to integrate
- Continuous deployment? Continuous localization!
- Manage the translation files with ease
- Order professional translations
- Analytics & Statistics
- Versioning of your translations
- Automatic and On-Demand Machine Translation
- Riskfree: Take your data with you
- Transparent and fair pricing
- and a lot more...
How does this look like?
First you need to signup at locize and login. Then create a new project in locize and add all required languages. And finally you can add your translations either by using the cli or by importing the individual json files or via API.
Now let's install the locize-cli:
npm install -g locize-cli
We'll prepare a new script that will synchronize our local changes with locize. And also an optional second script that will just download the newest translations from locize. Make sure you use your project-id and api-key:
1 | "scripts": { |
Use the npm run syncLocales
script to synchronize your local repository with what is published on locize.
Alternatively, you can also use the npm run downloadLocales
script to always download the published locize translations to your local repository before bundling your app.
If we now add a new translation key, like this:
1 | <Trans i18nKey="newKey">this will be added automatically after "extract" and "syncLocales"</Trans> |
and run npm run export
and then npm run syncLocales
, we get this:
locales/en/page-2.json
:
1 | { |
locales/de/page-2.json
:
1 | { |
Thanks to the optionally enabled automatic machine translation option, new keys not only gets added to locize, while developing the app, but are also automatically translated into the target languages using machine translation.
👀 but there's more... (InContext Editor)
With the help of the locize plugin, you'll be able to use your app within the locize InContext Editor.
Want to see what this look like?
Ok, first install the locize dependency:
npm install locize
Then in the code (we choose our layout.js
file) add this:
1 | import React from 'react'; |
And in the gatsby-config.js
add some new react options:
1 | const { languages, defaultLanguage } = require('./languages'); |
Then go to your locize project and define your in-context editor urls, like described here.
The result will look like this:
Isn't this great?
🧑💻 The complete code can be found here.
If you want to learn more basics about i18next, there's also an i18next crash course video.
🎉🥳 Congratulations 🎊🎁
I hope you’ve learned a few new things about gatsby-plugin-react-i18next, i18next, React.js localization and modern localization workflows.
So if you want to take your i18n topic to the next level, it's worth trying the localization management platform - locize.
The founders of locize are also the creators of i18next. So by using locize you directly support the future of i18next.