GSD

React Internationalization for Large Scale Apps

Posted by Zain Sajjad on October 12, 2023

According to CSA research, brands and companies that want to create globally successful digital experiences must take steps to internationalize their content. Especially when about 40% of internet users will never buy from websites other than those in their native language.

Companies can tap into new markets and revenue opportunities by incorporating features that make applications adaptable to each locale and multiple cultures. 

Building an internationalized digital experience allows you to do just that. By taking advantage of localization libraries, you can make sure that users worldwide will have an excellent experience with your application, regardless of their location or language settings. 

Using React, even novice developers can build multilingual applications, and we recommend you use either React-intl or React-i18next for this purpose. 

This blog post will explore the benefits of internationalization, the best internationalization libraries, and how to get started making your applications global-friendly using React and React-i18next. 

See how Butter's simple content API works with your React app banner CTA

What is internationalization?

Also known as i18n –where 18 stands for the number of letters between the first i and the last n in the word internationalization– internationalization is the combination of world-readiness and localization. 

World readiness enables a product to be used by people with multiple scripts and cultures, and localization refers to translating content to different languages and cultures.

In the early days of software development, when companies wanted to build software or websites in different languages, they created an entirely separate instance of that product in the target language, making localization expensive and time-consuming. 

Internationalization affects how easy it is for your audience to understand the content. In addition, implementing internationalization makes it possible to build accessible, global applications from the app’s inception using code libraries and frontend frameworks.

What is localization?

Often abbreviated as l10n, localization is the practice of adapting products and services to a local target audience. Localization takes an internationalized product and makes it highly relevant for its target user by translating and adapting all the information to serve that specific market. 

For example, while similar enough, British English and American English have some differences that go beyond spelling and require teams to localize the information to make it relevant to British audiences and ensure that they understand and act on what they read. 

Localization includes but is not limited to translation. It requires the localization team to follow linguistic cues and cultural and even body language cues to ensure that the information reaches its intended audience.

What’s the difference between internationalization and localization?

While localization and internationalization look similar, they’re different. Let’s see the differences:

Internationalization Localization
How does it work? Separates linguistic and cultural data from functionalities to make content accessible for everyone. Adapts products, services, and content to a particular cultural or linguistic market.
Who carries it out? Software developers and content producers.  Translators, proofreaders, project managers, and testers.
Scope Design and development of digital experiences Translation and adaptation of texts and UIs so it’s clear for people from different backgrounds. 
Actions Prepares codebase to receive multiple languages and configurations. Translates content so people from that country or region can use it.
Results Adapts to content to the culture that will interact with it Increases the influence of a company in the local market.

Why do large-scale apps need internationalization and localization?

While English has become the internet's lingua franca, not every netizen speaks English. According to Statista, there were 230 billion app downloads in 2021. Of those downloads, only 5% happened in the US. The majority of others came from China, India, Brazil, Russia, and Mexico. It's evident, then, that application design should be about universality and ease of use. 

So, instead of simply translating your original version and coming up with a not-so-good version in a different language, designers need to go the extra mile and tailor their digital experiences so every user can interact with them on an equal footing.

Here are some of the main reasons why your app needs to be localized to ensure its effectiveness in the global market: 

  • To tap into new markets: This sounds like a no-brainer, but it's true. Localization enables companies and brands to tap into new markets in different countries and for different audiences. 
  • To grow your app's engagement: 65% of users prefer to browse content in their mother tongue, and an app tailored specifically for users of a specific region has the potential to get more downloads and a more significant user base. 
  • To increase downloads and visibility: According to research from AppRadar, a localized app can get 70% more visibility and a median of 30% more downloads than a non-localized application with the same functionalities.  

What is React?

React is an open-source JavaScript library created by Meta. React simplifies the creation of UI component hierarchy by combining the bandwidth of JavaScript with a dynamic way of rendering pages to provide a highly intuitive user interface for mobile apps, web applications, and websites. It is intended for both backend and frontend application development. 

ReactJS has the concept of a virtual DOM. When data is retrieved from the backend APIs, this virtual DOM is tweaked, respectively, and this revised virtual DOM is balanced with the real DOM, and only that segment of the real DOM that differs from the virtual DOM is modified.

React is built to integrate with existing programs or other modules. Its primary goal is to automate designing and creating quick, adaptive, and easy-to-use web applications.

React also has a mobile development framework known as React Native, but we will be talking about internationalization for web applications built using create react app.

What are the prerequisites for internationalization?

Now that we discussed why you need to build your apps to be international and localizable, it's time to see the prerequisites you need to consider when getting your app ready for global audiences. 

  1. Plan your launch strategy and launch timeline
  2. Prepare your app for internationalization
  3. Allocate resources for the project
  4. Train your team, so they understand the product
  5. Create a style guide and develop a glossary
  6. Build a translation repository
  7. Plan for change management initiatives
  8. Update, test, and plan for complications
  9. Translate and test your app

Best React internationalization libraries

React intl

Developed on top of FormatJS with the backing of Yahoo, React intl is the most popular library for the internationalization of React applications

React intl rests upon Intl, JavaScript's native localization API, and abides by the industry-wide i18n standards. From a syntax perspective, it mimics the ICU Message syntax that is widely used in PHP and Java as well. 

Library provides React components and an API to format dates, numbers, and strings, including pluralization and handling complex translations. Being the first in React's i18n arena, it enjoys wide adoption and a large user community.

React-intl-universal

react-intl-universal is developed by Alibaba Group and has all the good things of React intl, along with the support of using it in Vanilla JS. 

React-intl universal solves one of react intl's main issues by allowing developers to apply internationalization options in layers other than React.component.

LinguiJS

React wrapper of LinguiJS, is the smallest of all i18n libraries available today for React applications. Like react-intl it uses ICU message syntax to make it easier to adopt in applications built using other libraries. 

On top of all goods of react intl, it supports rich-text messages and provides macros to support messages in Vanilla JS (i.e, outside of a React component.) It also has a powerful CLI tool to manage your translation workflows.

i18next

React-i18next is one of the widely adopted React localization libraries for React applications. With a tiny bundle size of ~15kb and support for all major browsers, it's often the go-to choice for frontend developers looking to internationalize their apps.

The most outstanding feature of React-i18next is the namespacing and division of translation files into small chunks. This gives a performance edge in large-scale applications. Plus, there are many plugins to support your development flows. It also supports locize.com out of the box for translation management.

What is react-i18next?

Developed on top of i18n, react-i18next has a philosophy very similar to React: Learn once, translate everywhere. In addition, the i18n community has developed a lot of integrations to use it with many frontend frameworks and multiple languages. 

It has a wide variety of plugins and utils to provide some out-of-the-box solutions around the internationalization of apps. Some of these are a plugin to detect the user's language, loading and caching translations that might be handy for large-scale apps. 

For translation files, each i18n instance gives you the option to separate translations into multiple files and load them on demand. You can have a single translation file for small apps to keep things simple. 

Many modules are built for and around i18next: from extracting translations from your code to bundling translations using webpack and converting gettext, CSV and RESX to JSON.

React-i18next features

Besides the powerful API, several extraordinary features make react-i18next the go-to solution for large-scale React applications. Plugins and utils, namespacing, and nesting are three essential features that we'll discuss further.

Plugins & Utilities

React-i18next has a great ecosystem of plugins and utils built around it. This includes libraries for detecting language and the extraction of messages from your React application.

One of these plugins is a webpack loader that can translate your code and generate a bundle per each language. Some of these can be very handy for large-scale applications with multilingual support. Here is our pick:

  • i18next Scanner: Scans your code, extracts translation keys/values, and merges them into i18n resource files.
  • i18next Loader: Webpack loader that can translate your code and generate bundle per each language.
  • i18next-react-postprocessor: Embeds React elements inside your i18next translation strings.

Namespacing

Many intl libraries put all of the messages in a single file for translation. Though this approach is great for small-scale applications, it's a pitfall when your React application grows in size. It's never easy to handle thousands of messages in a single file. To avoid this pitfall, react-18next divides your messages into namespaces. For example, we often want to segregate our validation messages & user action labels from our app. With react-i18next you can create 

actions.json -> eg. Button labels 'save', 'cancel'

validation.json -> All validation texts

Whereas all messages related to a particular module or component can remain in their own namespaces. This makes it more manageable for large scale applications.

From the performance perspective, it's not good to load all of your messages upfront on the first load. Dividing messages in the namespace allows us to load only the required messages.

Here is how to handle namespace loading:

i18next.init({
  ns: ['common', 'moduleA', 'moduleB'],
  defaultNS: 'moduleA'
}, (err, t) => {
  i18next.t('myKey'); // key in moduleA namespace (defined default)
  i18next.t('common:myKey'); // key in common namespace
});

// load additional namespaces after initialization
i18next.loadNamespaces('anotherNamespace', (err, t) => { /* ... */ });

Nesting

Nesting can be a very handy tool to build a glossary of your app. It allows you to access other translation keys in your messages. 

For example:

{
  "nesting1": "1 $t(nesting2)",
  "nesting2": "2 $t(nesting3)",
  "nesting3": "3",
}

i18next.t('nesting1'); // -> "1 2 3"

For more sophisticated cases nesting can also pass objects to other keys. For example:

{
      "girlsAndBoys": "$t(girls, {'count': {{girls}} }) and {{count}} boy",
      "girlsAndBoys_plural": "$t(girls, {'count': {{girls}} }) and {{count}} boys",
      "girls": "{{count}} girl",
      "girls_plural": "{{count}} girls"
}

i18next.t('girlsAndBoys', {count: 2, girls: 3});
// "3 girls and 2 boys"

React-i18next formats

In this section, we will look into the formatting options react-i18next offers. Unlike react-intl it doesn’t have separate components or APIs for formatting number, dates & relative time, rather all of this is done via the powerful interpolation API and given nomenclature.

Interpolation

To concatenate dynamic values in the text, react-i18next has the interpolation API. Unlike other intl libraries it allows accessing values deep inside objects, here is an example:

{
  "simpleValue": "{{what}} is {{how}}",
  "deepValue": "Hey {{user.name}}, Welcome to {{location.city}}"
}
 
const user = { 
    name: 'Jan',
};
const location = { 
    city: 'Singapore',
};

i18next.t(simpleValue, { what: 'ButterCMS', how: 'amazing' });
i18next.t(deepValue, { user, location });

This doesn’t end here, interpolation comes with a number of other options, including custom formatting that we will discuss later in this post. You can define the prefix & suffix of text and also unescape text for safety. Here is a list of all available options.

Pluralization

For Pluralization, react-i18next has defined nomenclature that we have to follow in the key of our messages. Starting from the simplest form,

{
  "message": "message",
  "message_plural": "messages",
  "messageWithCount": "{{count}} message",
  "messageWithCount_plural": "{{count}} messages"
}

// Here _plural is the reserved word for defining text for plural value

i18next.t('message', {count: 0}); // -> "messages"
i18next.t('message', {count: 1}); // -> "message"
i18next.t('message', {count: 3}); // -> "messages"

i18next.t('messageWithCount', {count: 0}); // -> "0 messages"
i18next.t('messageWithCount', {count: 1}); // -> "1 item"
i18next.t('messageWithCount', {count: 5}); // -> "5 messages"

This may work for languages or cases with very basic pluralization rules. For more sophisticated cases nomenclature has more terms, here is an example:

{
  "key_0": "zero",
  "key_1": "singular",
  "key_2": "two",
  "key_3": "few",
  "key_4": "many",
  "key_5": "other"
}

i18next.t('key', {count: 0}); // -> "zero"
i18next.t('key', {count: 1}); // -> "singular"
i18next.t('key', {count: 2}); // -> "two"
i18next.t('key', {count: 3}); // -> "few"
i18next.t('key', {count: 4}); // -> "few"
i18next.t('key', {count: 5}); // -> "few"
i18next.t('key', {count: 11}); // -> "many"
i18next.t('key', {count: 99}); // -> "many"
i18next.t('key', {count: 100}); // -> "other"

This adds more power to pluralization for sophisticated languages and cases. But it doesn’t end here. React-i18next has amazing interval pluralization, here is how it looks:

{
  "key": "{{count}} item",
  "key_plural": "{{count}} items",
  "key_interval": "(1){one item};(2-7){a few items};"
}

i18next.t('key2_interval', {postProcess: 'interval', count: 1}); // "one item"
i18next.t('key2_interval', {postProcess: 'interval', count: 4});  "a few items"
// not matching into a range it will fallback to
// the regular plural form
i18next.t('key2_interval', {postProcess: 'interval', count: 100}); // "100 items"

Formatting

React-i18next comes with powerful formatting APIs that can be used with date, numbers and other custom formatting libraries. This API gives you complete control over the formatting mechanism of text. As an example we will plug in two formatting in our initialization, here is how to do this:

i18next.init({
  interpolation: {
    format: function(value, format, lng) {
      
      // A custom formatting
      if (format === 'uppercase') return value.toUpperCase();

      // Using other library like moment
      if(value instanceof Date) return moment(value).format(format);

      return value;
    }
  }
});

Our resource JSON will look like this:

{
  "key": "The current date is {{date, MM/DD/YYYY}}",
  "key2": "{{text, uppercase}} just uppercased"
}

Here is how to use it:

i18next.t('key', { date: new Date() });
// -> "The current date is 07/13/2016"

i18next.t('key2', { text: 'can you hear me' });
// => "CAN YOU HEAR ME just uppercased"

Why is i18next better than other libraries?

We already touched on some of the reasons why i18next is a better choice than the rest of the libraries on the list, but that’s not all. There are other cool reasons why to internationalize apps in React using i18next.

  • It’s a sustainable library: i18next is older than most libraries, making it even older than the frontend frameworks you use with it. Also, it’s backwards compatible with older versions, as you can go from v1 to v11.x.x without breaking anything or hindering applications built on older versions.
  • It’s a mature library: i18next is open source and fits most internationalization use cases. 
  • It’s very extensible: From v2 of i18next, it can be used with any JavaScript framework and library –as well as some non-JavaScript ones. It can also be used in any environment and with any other i18n format.
  • It’s pretty rich: i18next goes beyond what other libraries do. It splits translations into multiple files, uses plugins to detect languages, and uses local caching and load translation to deliver localized content to users across the world.

See how Butter's simple content API works with your React app banner CTA

Getting started with React internationalization

We will start from the installation of react-i18next in the application and look into how it looks in our code. It's a wrapper around i18next, so we have to install both of these libraries in our project.

npm install react-i18next i18next

Integration

After installation it's time to initialize the library. With scalability in mind, it is highly recommended to keep following code in a separate file. That file can be imported at the root of the React application.

import i18n from "i18next";
import { initReactI18next } from "react-i18next";

// the translations
// (tip move them in a JSON file and import them)
const resources = {
  en: {
    translation: {
      "titleHeading": "Welcome to React and react-i18next"
    }
  }
};

i18n
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    resources,
    lng: "en",

    keySeparator: false, // we do not use keys in form messages.welcome

    interpolation: {
      escapeValue: false // react already safes from xss
    }
  });

  export default i18n;

As mentioned importing given code at root will initialize react-i18next in your application.

Usage

There are four different approaches to using translation in our application. Let's have a look at each of them.

React Hook

Since the release of hooks, React developers are writing more composed functional components. We can use react-i18next as a React hook in our component, here is an example:

import { useTranslation } from 'react-i18next';

function Title () {
  const { t, i18n } = useTranslation();
  return <h1>{t('titleHeading')}</h1>
}

Trans component

Component API is very intuitive for React developers, react-i18next has a Trans component for rendering. In its simplest form it looks likes as follows:

import { Trans } from 'react-i18next';

function Title () {
  return <Trans i18nKey="titleHeading" />
}

This component gives you a lot of power to add formatting to text. Here is an example:

// "titleHeading": "Hello <1>{{name}}</1>."
<Trans i18nKey="titleHeading">
Hello <strong>{{name}}</strong>.
</Trans>

"multiline": "Some newlines <1/> would be <3/> fine"
<Trans i18nKey="multiline">
Some newlines <br/> would be <br/> fine
</Trans>

With defined limitations like no nested tags and no re-render on namespace or language change, this component is really handy for formatting. One point of attention here, if you don’t require any formatting in your text, it is better to go for a hook or other APIs, as most of this can be achieved via those APIs as well.

Higher Order Component (HOC)

HOCs are one of the widely adopted patterns for React apps. HOC API of react-i18next looks like:

import { withTranslation } from 'react-i18next';

function Title ({ t }) {
  return <h1>{t('titleHeading)}</h1>
}

// Wrapping Title in withTranslation
export default withTranslation()(Title);

Render Props

Besides the Trans component, react-i18next has another JSX component API. This component is based on the render props pattern. Here is how it looks:

import { Translation } from 'react-i18next';

export default function Title () {
  return (
    <Translation>
       {
t => <h1>{t('Welcome to React')}</h1>
  }
    </Translation>
  );
}

As seen, react-i18next has a variety of APIs to give you more control over the text in your applications. Which one of these patterns do you like most and why? I’d love to discuss this further with you in the comments for this article below.

Internationalization in action

Nescafé

Nescafe internationalized websites

What’s so cool about Nescafé’s website is that it’s not only translated but it’s also localized respecting the needs and the cultural sensitivities of each region, showing different content depending on the region, even among regions with the same language. 

World Wide Fund for Nature

World wide fund for nature internationalized website

WWF uses a similar approach to React localization and internationalization and offers users two completely different versions of the website, showing visitors from two regions content they will likely empathize and interact with. 

Closing thoughts

Internationalizing and localizing content makes your brand global and stronger. Companies and brands that use React i18n as their internationalization engine are not only adapting their products to a different locale but also giving visitors and users multi-language support. 

To internationalize large-scale apps, companies need subject-matter experts, technical experts, and people with a level of international experience, but, most importantly, they will need a way of internationalizing at scale. 

i18next has been in the OSS arena since 2011 and thus is much more battle-tested than other localization libraries. Not just due to its powerful API but also its outstanding feature set complimented with plugins and utils make it a complete solution for internationalization. 

The philosophy of “learn once translate everywhere” is another benefit for teams of developers working on different tech stacks at the same time. It becomes easier to carry the same mental models across different projects consistently. React-i18next is surely a go-to solution for large-scale applications looking for performance and extensibility.

This post was updated by Diego Salinas Gardón.

Sign up to receive more articles about ButterCMS and React.
    
Zain Sajjad

Zain is a Senior Frontend Engineer at Peekaboo Guru. He is passionate about anything and everything React, React Native, JavaScript & Mobile Machine Learning.

ButterCMS is the #1 rated Headless CMS

G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award

Don’t miss a single post

Get our latest articles, stay updated!