Mastering React Internationalization: Your Comprehensive Guide to Multilingual Web Development

App internalization is the procedure of creating the content of your app that is available in several languages to reach more clients worldwide, which will genuinely globalize the potential of your web application development. Many enterprises avoid internalization, thinking it’s too complex and have to put too much effort into integrating it. However, ideal internalization and localization have proven their stats, highlighting their impact and importance in internationalizing your app. Hence, adding internationalization and localization phases to your app will help you achieve this scope.

What is internationalization?

Internationalization, or i18n, is designing and developing a product, application, or document’s content to enable simple localization for target audiences with various cultures, geographies, or languages. As a result, React i18n’s primary goal is to adapt React apps to different regions.

Eliminating obstacles to localizing or deploying an application abroad is the goal of internationalization. With regard to application design, using Unicode, older character encodings, string concatenation, and other elements can assist in achieving successful internationalisation.

What is localization?

The process of changing a product, application, or document’s content to match a particular target market’s linguistic, cultural, and other needs is known as localization (a locale). Many need clarification on localization, also known as l10n, by merely translating an application’s content to fit a location. Localization goes beyond translation. With localization, you may alter the preferred language, keyboard layout, sorting and collection, text direction, colors, and even designs for numbers, dates, and currencies.

Prerequisite

This tutorial assumes you have the most recent versions of npm and Node.js versions installed on your computer or device. Before understanding React i18n, you should be comfortable with HTML, JavaScript, and the fundamentals of npm or yarn.

Popular libraries for React i18n

Learn more about some of the popular React i18n libraries by exploring them. It might be more complex than it seems to pick the proper package or React library. Some of the most popular libraries for React i18n are React-intl and react-i18next.

Getting started with React-intl

Installation

By running the following command in your terminal or command line, you can install the React-intl library:

	
npm i -S react-intl

React-intl

The internationalization libraries for FormatJS include the react-intl library. More than 150 languages are supported globally. The characteristics included in this product, which Yahoo sells, are the main reasons it is so well-liked. Handling common locale settings, such as date and time, currency, and numbers, is made simple with React-intl. Also, it features detailed documentation that follows advanced standards, leveraging the built-in browser translations whenever possible. Polyfill options can be used with browsers that do not implement the JavaScript API for i18n.

React-intl delivers enhanced APIs and components while building on JavaScript’s React i18n API. React-intl leverages higher-order components that provide translations and the React context when language modules must be dynamically loaded.

Here is a link to the React-intl documentation.

Schedule an interview with React developers

Application WorkFlow

Our application will be created following the folder structure below:

English, French, and Arabic will all be used in the demo application.

A critical issue while creating our application to allow internationalization is figuring out how to identify the user’s preferred language. Usually, users have set their browsers to use their preferred language. By identifying their language and offering material in that language, we can provide them with the best user experience possible.

The navigator.language object, present in all current browsers, can be used to access the user’s language preferences.

Now, here is how our Wrapper.js file will initially appear:

	
import React, { useState } from "react";
import { IntlProvider } from "react-intl";
import French from "../lang/fr.json";
import Arabic from "../lang/ar.json";
import English from "../lang/en.json";


export const Context = React.createContext();


const local = navigator.language;


let lang;
if (local === "en") {
  lang = English;
} else {
  if (local === "fr") {
    lang = French;
  } else {
    lang = Arabic;
  }
}


const Wrapper = (props) => {
  const [locale, setLocale] = useState(local);


  const [messages, setMessages] = useState(lang);


  function selectLanguage(e) {
    const newLocale = e.target.value;
    setLocale(newLocale);
    if (newLocale === "en") {
      setMessages(English);
    } else {
      if (newLocale === "fr") {
        setMessages(French);
      } else {
        setMessages(Arabic);
      }
    }
  }


  return (
    <Context.Provider value={{ locale, selectLanguage }}>
      <IntlProvider messages={messages} locale={locale}>
        {props.children}
      </IntlProvider>
    </Context.Provider>
  );
};


export default Wrapper;

Now, here is how our index.js file will initially appear:

	
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import Wrapper from "./components/Wrapper";


ReactDOM.render(
  <Wrapper>
    <App date={Date.now()} />
  </Wrapper>,
  document.getElementById("root")
);
serviceWorker.unregister();

As you can see, we imported four files: IntlProvider, three language files (en.json, fr.json, and ar.json), and IntlProvider.

The React-intl library uses the provider pattern to define a lifecycle of React component tree as the scope of the internationalization context. This will wrap up the root component of the program, and the entire application will be set up under the internationalization context. We’ll then declare a constant variable to use with navigator.language to navigate the language.

Adding Translations

The language files must be updated with the pertinent translated messages. If you haven’t already, add three files with en.json, fr.json, and ar.json to the ‘lang’ subdirectory in the src folder. Then, add the subsequent information to the files appropriately. Keep in mind that you may always change and add new text.

lang/en.json file

	
{
  "app.header": "Edit<code>{fileName}</code> js and save to reload",
  "app.content": "Learn React",
  "app.channel.plug": "Tutorial brought to you by {blogName}",
  "app.plural": "{amount, plural, =0 {no languages} one {# one language} few {# several languages} many {# lots of languages} other {# wrong fromat}}"
}

lang/fr.json file

	
{
  "app.header": "Modifiez <code>{fileName}</code> et enregistrez-les pour recharger",
  "app.content": "Apprendre React",
  "app.channel.plug": "Tutoriel présenté par Yash Dalal",
  "app.plural": "{amount, plural, =0 {no languages} one {# one language} few {# several languages} many {# lots of languages} other {# wrong fromat}}"
}

lang/ar.json file

	
{
  "app.header": "قم بتحرير الملفات وحفظها لإعادة<code>{fileName}</code> التحميل",
  "app.content": "تعلم React",
  "app.channel.plug": "يقدم لك البرنامج التعليمي {blogName}",
  "app.plural": "{amount, plural, =0 {no languages} one {# one language} few {# several languages} many {# lots of languages} other {# wrong fromat}}"
}

Reflecting the changes

We’ll update the App.js file to reflect the modifications once we have the aforementioned code. Here, we’ve used the second approach to execute the message declaration.

serviceWorker.js

	
const isLocalhost = Boolean(
  window.location.hostname === "localhost" ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === "[::1]" ||
    // 127.0.0.0/8 are considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);


export function register(config) {
  if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
    // The URL constructor is available in all browsers that support SW.
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
    if (publicUrl.origin !== window.location.origin) {
      // Our service worker won't work if PUBLIC_URL is on a different origin
      // from what our page is served on. This might happen if a CDN is used to
      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
      return;
    }


    window.addEventListener("load", () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;


      if (isLocalhost) {
        // This is running on localhost. Let's check if a service worker still exists or not.
        checkValidServiceWorker(swUrl, config);


        // Add some additional logging to localhost, pointing developers to the
        // service worker/PWA documentation.
        navigator.serviceWorker.ready.then(() => {
          console.log(
            "This web app is being served cache-first by a service " +
              "worker. To learn more, visit https://bit.ly/CRA-PWA"
          );
        });
      } else {
        // Is not localhost. Just register service worker
        registerValidSW(swUrl, config);
      }
    });
  }
}


function registerValidSW(swUrl, config) {
  navigator.serviceWorker
    .register(swUrl)
    .then((registration) => {
      registration.onupdatefound = () => {
        const installingWorker = registration.installing;
        if (installingWorker == null) {
          return;
        }
        installingWorker.onstatechange = () => {
          if (installingWorker.state === "installed") {
            if (navigator.serviceWorker.controller) {
              // At this point, the updated precached content has been fetched,
              // but the previous service worker will still serve the older
              // content until all client tabs are closed.
              console.log(
                "New content is available and will be used when all " +
                  "tabs for this page are closed. See https://bit.ly/CRA-PWA."
              );


              // Execute callback
              if (config && config.onUpdate) {
                config.onUpdate(registration);
              }
            } else {
              // At this point, everything has been precached.
              // It's the perfect time to display a
              // "Content is cached for offline use." message.
              console.log("Content is cached for offline use.");


              // Execute callback
              if (config && config.onSuccess) {
                config.onSuccess(registration);
              }
            }
          }
        };
      };
    })
    .catch((error) => {
      console.error("Error during service worker registration:", error);
    });
}


function checkValidServiceWorker(swUrl, config) {
  // Check if the service worker can be found. If it can't reload the page.
  fetch(swUrl, {
    headers: { "Service-Worker": "script" },
  })
    .then((response) => {
      // Ensure service worker exists, and that we really are getting a JS file.
      const contentType = response.headers.get("content-type");
      if (
        response.status === 404 ||
        (contentType != null && contentType.indexOf("javascript") === -1)
      ) {
        // No service worker found. Probably a different app. Reload the page.
        navigator.serviceWorker.ready.then((registration) => {
          registration.unregister().then(() => {
            window.location.reload();
          });
        });
      } else {
        // Service worker found. Proceed as normal.
        registerValidSW(swUrl, config);
      }
    })
    .catch(() => {
      console.log(
        "No internet connection found. App is running in offline mode."
      );
    });
}
export function unregister() {
  if ("serviceWorker" in navigator) {
    navigator.serviceWorker.ready
      .then((registration) => {
        registration.unregister();
      })
      .catch((error) => {
        console.error(error.message);
      });
  }
}

App.js

	
import React, { useContext } from "react";
import logo from "./logo.svg";
import "./App.css";
import {
  FormattedMessage,
  FormattedDate,
  FormattedNumber,
  FormattedPlural,
  FormattedTime,
} from "react-intl";
import { Context } from "./components/Wrapper";


function App(props) {
  const context = useContext(Context);


  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <select value={context.locale} onChange={context.selectLanguage}>
          <option value="en">English</option>
          <option value="fr">French</option>
          <option value="ar">Arabic</option>
        </select>
        <p>
          <FormattedMessage
            id="app.header"
            defaultMessage="Edit the files and save to reload"
            values={{
              fileName: "src/App.js",
              code: (word) => <strong>{word}</strong>,
            }}
          />
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          <FormattedMessage id="app.content" defaultMessage="Learn React" />
        </a>
        <FormattedMessage
          id="app.channel.plug"
          defaultMessage="Tutorial brought to you by Yash Dalal"
          values={{ blogName: "Yash Dalal" }}
        />
        <br />
        <FormattedPlural
          id="app.plural"
          defaultMessage="{amount, plural, =0 {no languages} one {# one language} few {# several languages} many {# lots of languages} other {# wrong fromat}}"
          values={{ amount: 90 }}
        />
        <br />
        <FormattedDate
          value={props.date}
          year="numeric"
          month="long"
          day="numeric"
          weekday="long"
        />
        <br />
        <FormattedNumber
          value={20.42}
          style="currency"
          currencyDisplay="symbol"
          currency="USD"
        />
        <br />
        <FormattedNumber value={10000} />
        <br />
        <FormattedTime
          value={new Date()}
          hour="numeric"
          minute="numeric"
          second="numeric"
          timeZoneName="long"
        />
      </header>
    </div>
  );
}


export default App;

To get the messages, we imported FormattedMessage here. The declared IDs must be used when naming the translated strings because we have now defined the translated texts in a JSON file. For instance, the ID for the Learn React message is app.content.

The FormatJS community encourages using defaultMessage and employing it for the following reasons:

When messages are collocated with anything else, especially when their usages are, they start to manage themselves, and if the uses are modified or removed, so are the messages.

Developers construct highly contextual messages by adopting a specific language style.

The message determines how the text is styled. For instance, the messages are affected by capitalization, truncation, etc.

Given that many toolchains cannot verify or confirm cross-file references in validating syntax, this facilitates better integration when used with toolchains.

Run npm start after finishing the modifications, then open http://localhost:3000/ in your browser. These changes will happen automatically if you have already issued the command and the server is running.

These modifications will result in the following user interface:

Conclusion

You finally got the internalization React app, which can be localized according to various languages and regions, making the app more accessible to people around the globe, increasing your targeted audience, giving you better revenue streams, and making your business reach globally.

Suppose you want to React internalization in your app. In that case, you can hire React development team from a leading mobile app development company that will help you in every manner and make your project fit within your budget.

Frequently Asked Questions (FAQs)

1. What is internalization in app development?

Internalization is the state that enables software applications to function equally well in their supported locales; and to be localized. Localization is the process of modifying an app’s elements to meet a particular locale’s requirements.

2. What is the difference between internalization and localization?

Internalization (i18) is designing and creating software or products that adopt various cultures and languages. At the same time, localization (i10n) is the phase of adopting the item or content for a particular market or locale.

3. What is the impact of internationalization?

Usually, internalization leads to the highest levels of innovation, showing that exporting entails more R&D, high sales from product innovation, and an increase in the number of international patents; FDI outflows raise R&D and international patents; international outsourcing entails higher.


Book your appointment now

Get in touch





    Stay up-to-date with our blogs