Internationalization in React
This article is already posted in Ars Futura magazine.
Hi, I’m Ivan and I work as a Full-stack Engineer at Ars Futura, a design-driven development agency based in Zagreb. This blog post outlines some of the methodologies and practices we use when implementing the internationalization in our product. If you have any questions, feel free to reach out to me at ivan.samardzic@arsfutura.co.
While working on a project, you should always take into account cultural and social differences if you want to ensure a positive user experience. It is, after all, the best possible advertisement. The implementation of internationalization is just one of the steps that can increase customer satisfaction and make the product more effective.
Tim Berners-Lee, inventor of the web highlighted: “The key thing about the Web is that it is universal. One of the most important actions taken at the start — to make sure the Web was for everyone — was Internationalization”.
- What is this term and why use internationalization anyway?
For starters, what exactly does internationalization mean? It is the process of preparing software to support multiple cultures and language preferences. When internationalization is implemented, the software will automatically change its behavior and approach, functioning appropriately, based on user expectations and requirements.
Let’s say you land a new contract with a company in Japan, or your management team is looking to make an aggressive push in Australia. Internationalizing your software is the best path toward meeting the needs and expectations of local users.
Implementing internationalization kills two birds with one stone. There are many advantages of internationalization in the wake of application development, maintenance, and marketing.
Internationalization in app development:
From a developer’s standpoint, it can be described as the process of creating an architecture that allows the code to be decoupled from the content displayed on screen. At the same time, it helps to add new, or change existing content. As a consequence of the decoupling, internationalization provides the power of abstraction by replacing the hard-coded content with its key placeholder.
Internationalization and maintenance:
Developers tend to solve errors faster and easier, add new features and improvements quicker, and have the same code structure for all cultures that the application supports. By using internationalization, the programming code becomes culture-and-language-neutral.
Internationalization and marketing:
On the marketing side, we meet the needs of customers and respect their norms and expectations. Also, the application reaches a larger number of users and increases their satisfaction.
2. Internationalization vs. localization
There is a lot we can do, as designers and developers, to make sure our websites and products are welcoming and understandable for wider audiences. The best place to continue, as highlighted by Berners-Lee, is by describing the two core concepts: internationalization and localization.
Although the terms are related and very important for the preparation of the product, there are several determinants that separate them.
Internationalization allows software to behave differently, depending on the target culture and language. It replaces the code with keywords, so the product adjusts automatically, without additional technical interference. It is an engineering requirement that allows quick and easy changes due to the decoupling architecture.
Localization keeps pace with internationalization, prepares the software to correctly display the time and dates, certain local features and peculiarities, such as culturally acceptable and unacceptable images, symbols, signs, etc. It is the process of finalizing a product by paying attention to detail in the content aimed at a target group, in a particular area. Localization is primarily a user requirement because content acceptable to a particular culture is injected into the already mentioned architecture, created in the process of internationalization.
Without taking the internationalization process into account in the early stages, localizing your product later on will be much more difficult — ultimately leading to re-engineering.
You can discover even more about this topic. Here, you can find images which describe internationalization and localization, how to implement them inside your application, many cool tricks and tips, what the process even means, and what it looks like.
Fun fact: the English language requires 35% less space than German to display the same content and have the same meaning.
3. Key features and benefits
It sounds great, but what is the best way to implement internationalization? How do you start the project?
Over the years, the software engineering community has developed a large number of packages that enable easy implementation of internationalization. In most cases, the i18next package is definitely the first choice.
Currently, the i18next package is the most popular package with over 2.5 million weekly downloads. It provides a wide range of methods, using the main package instance and ensuring a complete process of internationalization. Many frameworks like Flutter, .NET, or PHP also support the i18next package, so no worries if your tech stack is not JavaScript-based.
Hint: The documentation explains all the methods in detail, in a very simple way, showing examples to understand the whole procedure easily. Find out more about the documentation here.
If you’re asking yourself what i18next provides for the internationalization implementation, the answer can not be easily summed up. Let me explain the basic internationalization lifecycle. The regular internationalization frameworks work by passing all the translations and the currently used language to the class instance. Then, they call the function that returns the correct translation within the script in which internationalization is being used.
The i18next package makes this process much easier and quicker by:
- enabling translations to split into multiple namespace files
- providing a plugin to detect languages
- implementing a plugin to load translations.
The “detection” plugin makes it possible to prioritize detection and can even enable to cache set languages over requests/visits. And the “loading” plugin makes sure that loading is retried upon failure, or that a file does not load twice.
Also, callbacks of success are only called once. It is important to note that the i18next package supports work with objects and arrays. Additionally, it provides options on what to load and how to fall back, depending on the language, and ensures full management of the translations stored.
4. Basic implementation
The two most important package functionalities are certainly interpolation and the ability to decouple resources to different namespace files. Whereby, it is achieved that the system loads only certain translations. Therefore, it needs less time to fetch translation strings presented on the application page the user is currently interacting with. The mentioned features also result in a reduction in the number of resources, due to their more effective use. The ability to define multiple namespace files allows developers to use a single namespace file for a specific part of the interface, e.g. one for the header, the second for navigation or the footer.
The interpolation feature provides the integration of dynamic values into the system translations. The translation string can then change its value and be used on multiple occasions. This applies especially to the translation strings that have related content.
The implementation process usually starts with installing necessary libraries using node package manager or any other repository manager.
npm i18next
The documentation proposes to continue the implementation by setting the configuration inside the application you are developing, using the i18n.js script placed at the same architecture level as the main application script. The i18next first setup could be similar to the example presented below.
i18next.init({
lng: “en”,
backend: {
loadPath: “/locales/{{ns}}/{{lng}}.json”,
},
ns: [‘common’, ‘header’, ‘footer’],
defaultNS: ‘common’,
debug: true,
nsSeparator: “:”,
});
The setup defines a default language with the lng property inside the configuration setup, while the backend property provides a path where namespace files are stored. Inside the ns property is an array that stores namespace folder names and defaultNS defines the project’s default namespace. The debug property is by default false, but setting it true helps fix errors and bugs by writing logs info level to console output. Finally, the nsSeparator property defines which separator divides the namespace folder name and translation string name.
After defining the configuration script for internationalization, to effectively use translation strings, all that’s left to do is create namespace folders that will be added on a predefined path. Also, it is mandatory to create files that will have the necessary key-value pairs (translation strings) in the JSON format within the folder to be used on the interface. The last step of the complete process of internationalization is to retrieve the translations within a certain script. Find a simple example of internationalization below.
import i18next from ‘i18next’;
i18next.init({
lng: ‘en’,
debug: true,
resources: {
en: {
translation: {
“key”: “Hello World”
}
}
}
})function(err, t) {
document.getElementById(‘output’).innerHTML = i18next.t(‘key’);
}
Setting up the process and configuring the i18next package, then injecting the content into the namespace folders, and finally using and retrieving the translations that are in these folders, presents the complete process of internationalization.
5. How to implement the i18next package in React?
Since the word “React” is mentioned in the article title, the main goal is primarily to explain the implementation of internationalization in this JavaScript library. In addition to the i18next package, its wrapper, react-i18next will be presented, used to inject translations in React.
The react-i18next package is a powerful internationalization framework for React/React Native, based on the already explained i18next package. This package provides multiple components, e.g. to ensure that needed translations get loaded or that your content gets rendered when the language changes.
It is important to mention that React doesn’t link the URL to the locale so the locale is not stored within the https address. That is because React does not have its own router in the initial package, as opposed to Next.js frontend framework, but it can be implemented using an external package such as react-router-dom or a similar package available at the node package manager.
Hint: Don’t get confused. It is very easy to mistake the terms “Next.js” and “i18next” by reading this article. They are two completely different terms. Next.js is the React.js wrapper framework made to develop a lot of user interfaces, while the i18next package is the internationalization package.
The most important react-i18next function is the t function. It provides the developers with the ability to fetch multiple translation strings from any namespace folder. The t function works like this:
<div>Just simple content</div> -> <div>{t(‘simpleContent’)}</div>
The mentioned function makes it possible to write a line of code that will be used for all languages supported by the product. The package independently checks which language is active, and based on that, retrieves the required translation string. Therefore, the content changes dynamically, dependent on another parameter (e.g. the active language), and thus becomes abstract.
There is also a changeLanugage function with which, as its name suggests, the active language can be changed. This achieves a simple change of language, which allows the user to change the content of the application according to their own preferences.
const changeLanguage = (event) => {
i18n.changeLanguage(event.target.value);
};
In addition to the i18next package, it’s necessary to install react-i18next and i18next-xhr-backend, which can be added to successfully inject content located on external addresses and third-party servers within the i18next translations.
npm react-i18next i18next-xhr-backend
To implement internationalization in the project as simply as possible, the image below shows the desired structure of the project after creating all the necessary folders and files.
Image 1. Project structure
After installing all the necessary packages, the package configuration should be placed in the i18n.js script. After that, the namespace folders and translation strings need to be set as defined in the configuration. All content that should be visible to the user on the application, should be contained within the JSON file format in the form of key-value pairs.
“key”:”value”
Inside the header namespace, in every JSON file format, there is a Reacti18nextTitle key, but each file has different content, depending on what language and culture it is defined for.
header/en.json{
“AwesomeTranslation”: “Ten Ways to Lose Weight While Injecting Internationalization”,
}header/es.json{
“AwesomeTranslation”: “Diez maneras de perder peso mientras se inyecta internacionalización”,
}
From the react-i18next wrapper, the initReactI18next method can be imported. Through this method, it is possible to inject i18next translations within the React ecosystem.
i18n.jsimport i18n from “i18next”;
import Backend from “i18next-xhr-backend”;
import { initReactI18next } from “react-i18next”;i18n
.use(Backend)
.use(initReactI18next)
.init({
lng: “en”,
backend: {
loadPath: “/your-path-to-namespaces”,
},
debug: true,
ns: [‘common’, ‘header’, ‘footer’],
nsSeparator: “:”,
defaultNS: ‘common’,
});
export default i18n;
The same functionality is achieved as if we added an I18nextProvider in the main component of the react application, which performs the same function. The mentioned pattern is available below.
inside index.jsimport React from “react”;
import ReactDOM from “react-dom”;
import { I18nextProvider } from “react-i18next”;
import i18n from “./i18n”;
import App from “./App”;
ReactDOM.render(
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>,
document.getElementById(“root”)
);
Last but not least, it is necessary to describe a way to fetch the various translation strings within React. Everyone is familiar with advanced techniques whose names start with h, end with s and make our job easier. They are of course hooks and hocs. Using useTranslation React hook or withTranslation higher-order component, the components easily fetch the required content.
No matter which approach is used, the result is the same. Both methods can be used on the same component which are shown with examples below this paragraph. With this piece of code, the completed process of internationalization with the implementation in React was performed again.
If you want to fetch a translation string from a non-default namespace, you can use this approach non-default-namespace:translation-string and you will get the required content displayed on the web application page. The non-default namespace and translation string are divided with the namespace separator. Take a look at the return method in the example below.
Hint: You can also use an approach such as this: {t(‘translation-string’, {ns: ‘non-default-namespace’}.
inside Header.js, useTranslation exampleimport React from “react”;
import { useTranslation } from “react-i18next”;const Header = () => {
const { t } = useTranslation();
return (
<div>
<div>{t(“header:AwesomeTranslation”)}</div>
</div>
);
};
export default Header;inside Header.js, withTranslation exampleimport React from “react”;
import { withTranslation } from “react-i18next”;const Header = ({ t }) => {
return (
<div>
<div>{t(“header:AwesomeTranslation”)}</div>
</div>
);
};export default withTranslation()(Header);
Let me leave you with a bonus example of interpolation for the end. Take a look — it carries a significant message for your future projects.
inside header/en.json and Header.js{
“AwesomeTranslation”: “Ten Ways to Lose Weight While Injecting Internationalization”,
“SignificantMessage”: “{{what}} is {{how}}”,
}
The interpolation feature makes the i18next package enormously robust and flexible. The content can be easily injected into translation strings and changed over time, the translation string value becomes dynamic and can be used multiple times, wherever you want.
6. Summary
To escape an abrupt ending, let us once again mention the most important facts about internationalization using the i18next package.
Internationalization is the process of preparing software so it can support multiple cultures and language preferences. Creating an architecture for internationalization while using the i18next package makes a difference. The advantages of implementing internationalization are also present when developing and advertising the products that are being created.
The product is more flexible, scalable, and abstract in terms of code. A larger number of users are satisfied and consider it more appropriate to use. There is no need to load all the translations which makes the product faster and better, and the documentation found on the web clearly shows all the possibilities of this tool.
The process of internationalization, from setting up a package to creating translations and including translations in individual files, is very simple. That is what makes it so powerful.
Since the readers of this article are very diverse in terms of interest and experience with internationalization, let the two most important take-away concepts be code abstraction and culture-neutral software. These two terms perfectly describe why internationalization and the i18next package exist and should be implemented.
Whenever you want, you can view the project on Github or live on CodeSandbox in which internationalization has already been implemented.
Also, if you want to know even more and dive deep into the vast depths of implementation, the following articles can be very educational to you:
- https://lokalise.com/blog/internationalization-vs-localization
- https://www.w3.org/International/questions/qa-i18n
- https://prototypr.io/post/dont-alienate-your-user-a-primer-for-internationalisation-localisation/
- https://fantasai.inkedblade.net/style/talks/i18n-primer/#direction-layout
- https://lokalise.com/blog/how-to-internationalize-react-application-using-i18next/
- https://linguinecode.com/post/react-i18next-withtranslation-typescript
In short, internationalization is awesome. Have fun and stay tuned for more!