# 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:

1. [Node](https://nodejs.org/) installed on their local machine
2. [yarn](https://yarnpkg.com/) or [npm](https://www.npmjs.com) installed on their local machine (npm comes pre-installed with node)
3. 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](https://en.wikipedia.org/wiki/Internationalization_and_localization), 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](https://github.com/Young-Einstein10/internationalization-in-react).

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:-

![](https://paper-attachments.dropboxusercontent.com/s_6CEBE7963D9F76707C361DE28554CD96EDC2B57BA0C4F761180E25A1B8798855_1668407798184_Screenshot+2022-11-14+at+07.36.07.png)


## 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.

![](https://paper-attachments.dropboxusercontent.com/s_6CEBE7963D9F76707C361DE28554CD96EDC2B57BA0C4F761180E25A1B8798855_1668409787616_Screenshot+2022-11-14+at+08.08.36.png)

## 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:

1. Using the `useTranslation` hook
2. Using the `withTranslation` HOC
3. 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.

![](https://paper-attachments.dropboxusercontent.com/s_6CEBE7963D9F76707C361DE28554CD96EDC2B57BA0C4F761180E25A1B8798855_1668532736711_Screenshot+2022-11-14+at+08.08.36.png)

## 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.

![](https://paper-attachments.dropboxusercontent.com/s_6CEBE7963D9F76707C361DE28554CD96EDC2B57BA0C4F761180E25A1B8798855_1668410454712_Screenshot+2022-11-14+at+08.20.48.png)

## 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.

![](https://paper-attachments.dropboxusercontent.com/s_6CEBE7963D9F76707C361DE28554CD96EDC2B57BA0C4F761180E25A1B8798855_1668531387206_Setting_Up_Internationalization_in_React.gif)

## 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](https://github.com/Young-Einstein10/internationalization-in-react/tree/feature/setup-react-i18next).

Feel free to reach out to me via [LinkedIn](https://www.linkedin.com/in/abdulrahman-yusuf-a44463176/) or [Twitter](https://twitter.com/zaydabdulrahman) if there are any questions.

## Resources

- [Internationalization and Localization](https://en.wikipedia.org/wiki/Internationalization_and_localization), Wikipedia
- [A react-i18next guide](https://locize.com/blog/react-i18next), Locize Blog
- [react-i18next Documentation](https://react.i18next.com), react-i18next
- [i18next](https://www.i18next.com/overview/api,) [API Overview](https://www.i18next.com/overview/api,), i18next
- [How to Internationalize a React App](https://blog.shahednasser.com/how-to-internationalize-a-react-app/), Shahed Nasser

