Setting up Internationalization in React using react-18next 🇺🇸🇫🇷
Nowadays, websites and applications can be accessed from different parts and regions of the world. So, it is very essential to make your website easily accessible to anyone regardless of their location.
In this article, I’ll share the importance of internationalization and how to set up internationalization in a ReactJS application.
Prerequisites
This tutorial assumes the reader has the following:
- Node installed on their local machine
- yarn or npm installed on their local machine (npm comes pre-installed with node)
- Basic knowledge of HTML, CSS, Javascript and React.
What is Internationalization?
Before we proceed, let’s first understand the meaning of Internationalization!
According to Wikipedia, Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.
Basically, Internationalization is the process of making your website or applications usable by all people irrespective of the country or region they find themselves. There are so many benefits of internationalization, but here are some of my favourites:
- It helps in customer and user retention, which leads to an increase in overall customer satisfaction.
- It saves time and cost as you don’t have to develop a separate website for people in separate regions.
- It makes maintenance of the website easier.
Getting Started
For this tutorial, I’ve created a simple login page which we’ll be using to set up internationalization in our app. You can find and clone the project to your machine using this Github Link.
There are so many tools out there that can be used to set up internationalization, but we’ll be using react-18next due to its ease of setup and the vast amount of plugins that can be integrated into it.
Installing Dependencies
Let’s start by installing react-18next and a plugin in our project using the command below:
yarn add react-i18next i18next i18next-browser-languagedetector
I’ll be explaining what the plugin is for in the coming section.
Setting up react-i18next
After the installation is completed, we need to set up some configurations for react-18next
before we can begin using it. For that, create a file named i18n.js
in your src directory and paste the code snippet below:
// i18n.js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources: {},
lng: "en",
keySeparator: ".",
});
export default i18n;
Here, we imported the i18n
instance and initReactI18next
from i18next and react-i18next respectively. We then went on to initialize the instance by adding other parameters needed for configuration.
Next, we need to create a new directory called locales
, this directory would contain all our translations for different regions or languages that our application uses. For this tutorial, we’ll be sticking with two languages, mainly English and French. The locales
directory would contain the translation files for our respective locale like so:-
Adding our locales
Paste the code snippet below into the English locale
{
"login": {
"heading": "Login to your account",
"submit": "Login",
"input": {
"email": "Email",
"emailPlaceholder": "Email Placeholder",
"password": "Password",
"passwordPlaceholder": "Password Placeholder",
"toggle_password": "Toggle Password"
},
"errors": {
"email": "Email is required",
"password": "Password is required",
"passwordLength": "Password length should be at least 6 characters"
},
"forgot_password": "Forgot Password"
}
}
Paste the code snippet below into the French locale
{
"login": {
"heading": "Connectez-vous à votre compte",
"submit": "connexion",
"input": {
"email": "E-mail",
"emailPlaceholder": "Espace réservé d'e-mail",
"password": "Mot de passe",
"passwordPlaceholder": "Espace réservé pour le mot de passe",
"toggle_password": "basculer le mot de passe"
},
"errors": {
"email": "L'e-mail est requis",
"password": "Mot de passe requis",
"passwordLength": "La longueur du mot de passe doit être d'au moins 6 caractères"
},
"forgot_password": "Mot de passe oublié"
}
}
The translations are represented in a key-value pair which allows us to manage the text for different parts of our app in an efficient manner. The keys would later be referenced when we want to set up dynamic translations in our components.
Now that we have the translations for our locales, we need to import them inside our i18n.js file
and include it in the resources property like this:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import EN_TRANSLATION from "./locales/en/translation.json";
import FR_TRANSLATION from "./locales/fr/translation.json";
i18n
.use(initReactI18next)
.init({
resources: {
en: {
translation: EN_TRANSLATION,
},
fr: {
translation: FR_TRANSLATION,
},
},
lng: "en",
keySeparator: ".",
});
export default i18n;
Now that we have our i18n.js file set up, we need to import the i18n
instance we just configured in app.js so that it can get bundled during compilation. Import i18n.js below in your app.js
file like this:
import {
Center,
ChakraProvider,
Flex,
Heading,
Select,
} from "@chakra-ui/react";
import Login from "./components/login";
import "./i18n";
function App() {
return (
<ChakraProvider>
<Flex
as="nav"
mx="5rem"
height="80px"
justifyContent="space-between"
alignItems="center"
>
<Heading as="h4" size="md">
Logo
</Heading>
<Select
width="150px"
defaultValue={i18n.language}
onChange={handleSelectChange}
>
<option value="en">English</option>
<option value="fr">French</option>
</Select>
</Flex>
<Center height="calc(100vh - 80px)">
<Login />
</Center>
</ChakraProvider>
);
}
export default App;
Save the changes and view them in the browser.
Using the translations in our components
Now that our app works and we’ve set up react-i18next, we need to make use of the translations in our components in order for them to dynamically change to their respective locales depending on the region the user is in.
react-i18next
offers us three different methods for accessing translations, namely:
- Using the
useTranslation
hook - Using the
withTranslation
HOC - Using the
Translation
render prop
Using the useTranslation
hook
The t
function is the main function in i18next
to translate content. We’ll be importing the useTranslation
hook from react-i18next which allows us to destructure the t
function from the hook. Add the lines of code below in our login.js
file.
import React from "react";
import {
Box,
Flex,
Button,
Heading,
FormControl,
Input,
FormLabel,
FormErrorMessage,
Link as ChakraLink,
} from "@chakra-ui/react";
import { useForm } from "react-hook-form";
import PasswordInput from "./passwordInput";
import { useTranslation } from "react-i18next";
const Login = () => {
const { t } = useTranslation();
const {
handleSubmit,
register,
formState: { errors, isSubmitting },
} = useForm();
const onSubmit = (values) => {
return new Promise((resolve) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resolve();
}, 3000);
});
};
return (
<Box maxW="400px" w="full">
<Heading
fontWeight={700}
fontSize="24px"
fontFamily="Source Sans Pro"
mb={10}
textAlign="center"
>
{t("login.heading")}
</Heading>
<form onSubmit={handleSubmit(onSubmit)}>
<Box p={4}>
<FormControl mb={6} isInvalid={errors.email}>
<FormLabel htmlFor="email" color="#100B05" fontSize="14px">
{t("login.input.email")}
</FormLabel>
<Input
id="email"
type="email"
placeholder={t("login.input.emailPlaceholder")}
{...register("email", {
required: t("login.errors.email"),
})}
isRequired
/>
<FormErrorMessage>
{errors.email && errors.email.message}
</FormErrorMessage>
</FormControl>
<FormControl mb={2} isInvalid={errors.password}>
<FormLabel htmlFor="password" color="#100B05" fontSize="14px">
{t("login.input.password")}
</FormLabel>
<PasswordInput register={register} />
<FormErrorMessage>
{errors.password && errors.password.message}
</FormErrorMessage>
</FormControl>
<Flex justify="flex-end">
<ChakraLink color="brand" fontSize="sm">
{t("login.forgot_password")}
</ChakraLink>
</Flex>
<Button
mt={10}
type="submit"
colorScheme="teal"
w="full"
isLoading={isSubmitting}
>
{t("login.submit")}
</Button>
</Box>
</form>
</Box>
);
};
export default Login;
Using the withTranslation
HOC
react-i18next
also offers a HOC that can be used for situations where the hook isn’t the preferred style. Add the code below in our passwordInput.js
file.
import React from "react";
import {
Input,
InputGroup,
IconButton,
InputRightElement,
useDisclosure,
} from "@chakra-ui/react";
import { ViewIcon, ViewOffIcon } from "@chakra-ui/icons";
import { withTranslation } from "react-i18next";
const PasswordInput = ({ register, t }) => {
const { isOpen, onToggle } = useDisclosure();
return (
<InputGroup>
<Input
id="password"
type={isOpen ? "text" : "password"}
placeholder={t("login.input.passwordPlaceholder")}
{...register("password", {
required: t("login.errors.password"),
minLength: {
value: 6,
message: t("login.errors.passwordLength"),
},
})}
isRequired
/>
<InputRightElement h="full">
<IconButton
bg="none"
width="32px"
onClick={onToggle}
height="40px"
aria-label={t("login.input.toggle_password")}
icon={
isOpen ? (
<ViewIcon size={18} color="#B8B8B8" />
) : (
<ViewOffIcon size={18} color="#B8B8B8" />
)
}
_hover={{
bg: "none",
}}
_active={{
bg: "none",
}}
/>
</InputRightElement>
</InputGroup>
);
};
export default withTranslation()(PasswordInput);
Save the changes and check the result in the browser.
Language Switcher
Now, we’ll notice when we change the language in the select dropdown nothing happens, that’s because we need to create a handleChangeLanguage
function that handles changing the user language whenever the language is changed. Add the function below to our i18n.js
file
export const handleChangeLanguage = async (language = "en") => {
await i18n.changeLanguage(
language,
console.log("Language Changed to: " + language)
);
};
Next, import the function in app.js
, then add it to the onChange
handler for the select component used in changing the language. We’ll also need to import the i18n.js
instance in order for us to access the current language and use it as a default value for the dropdown.
import {
Center,
ChakraProvider,
Flex,
Heading,
Select,
} from "@chakra-ui/react";
import Login from "./components/login";
import i18n, { handleChangeLanguage } from "./i18n";
import "./i18n";
function App() {
const handleSelectChange = (event) =>
handleChangeLanguage(event.target.value);
return (
<ChakraProvider>
<Flex
as="nav"
mx="5rem"
height="80px"
justifyContent="space-between"
alignItems="center"
>
<Heading as="h4" size="md">
Logo
</Heading>
<Select
width="150px"
defaultValue={i18n.language}
onChange={handleSelectChange}
>
<option value="en">English</option>
<option value="fr">French</option>
</Select>
</Flex>
<Center height="calc(100vh - 80px)">
<Login />
</Center>
</ChakraProvider>
);
}
export default App;
Now, when you save the changes and change the language in the browser, we’ll notice the language changes as expected.
Plugins
One other cool feature react-i18next offers is the use of different plugins that can be added to the i18n
instance during initialization. One popular plugin is the i18next-browser-languagedetector
which can be used to automatically detect the language of the user based on the browser’s locale. In this case, we don’t have to hardcode the language to the English locale all the time, we can make it dynamic so that the user’s locale is selected when needed. For us to add this plugin to the i18n
instance, we’ll need to import it below and also make some changes to our configurations:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import EN_TRANSLATION from "./locales/en/translation.json";
import FR_TRANSLATION from "./locales/fr/translation.json";
import LanguageDetector from "i18next-browser-languagedetector";
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: {
translation: EN_TRANSLATION,
},
fr: {
translation: FR_TRANSLATION,
},
},
fallbackLng: "en",
keySeparator: ".",
});
export const handleChangeLanguage = async (language = "en") => {
await i18n.changeLanguage(
language,
console.log("Language Changed to: " + language)
);
};
export default i18n;
We used the Language-Detector plugin and also changed the lng
property to fallbackLng
. This is because we’re no longer hardcoding our locale to en
, instead, we want to use it as a fallback language should in case translations are unavailable for the locale selected. The Language-Detector plugin also helps us with persisting the selected locale in Local Storage so we don’t have to go through the process of changing locales again the next time we use the app.
Now save the changes and view the result in the browser, we’ll notice our app still works as expected. Also, when we change the language and refresh the app we notice the language stays the same.
Conclusion
In this article, I explained the benefits of setting up Internationalization in your applications and how you can go about setting up react-i18next in your react projects. I hope you found this guide helpful and that you would be able to set up internationalization easily in your applications moving forward. You can find the complete code for the tutorial on GitHub.
Feel free to reach out to me via LinkedIn or Twitter if there are any questions.
Resources
- Internationalization and Localization, Wikipedia
- A react-i18next guide, Locize Blog
- react-i18next Documentation, react-i18next
- i18next API Overview, i18next
- How to Internationalize a React App, Shahed Nasser