Telerik blogs

In this article, learn how to use Format.js to add translations to a React app.

One thing we often have to do in our React apps is to add text for multiple languages into our app.

We can use libraries made for React to make this easy. In this article, we will look at how to use Format.js to add translations to our React app.

Getting Started

To get started, we install the react-intl library, by running:

npm i -S react-intl

Then we can use it right away.

To add some translations and display them, we write something like:

import { useState } from "react";
import { IntlProvider, FormattedMessage, FormattedNumber } from "react-intl";

const messagesInFrench = {
  myMessage: "Aujourd'hui, c'est le {ts, date, ::yyyyMMdd}"
};

const messagesInEnglish = {
  myMessage: "Today is {ts, date, ::yyyyMMdd}"
};

export default function App() {
  const [locale, setLocale] = useState("fr");
  return (
    <>
      <button onClick={() => setLocale((l) => (l === "en" ? "fr" : "en"))}>
        toggle language
      </button>
      <IntlProvider
        messages={locale === "en" ? messagesInEnglish : messagesInFrench}
        locale={locale}
        defaultLocale="en"
      >
        <p>
          <FormattedMessage
            id="myMessage"
            defaultMessage="Today is {ts, date, ::yyyyMMdd}"
            values={{ ts: Date.now() }}
          />
        </p>
        <p>
          <FormattedNumber value={100} style="currency" currency="GBP" />
        </p>
      </IntlProvider>
    </>
  );
}

We have messagesInFrench with the French text and messagesInEnglish with the English text. Then we add the locale state to so we can toggle between English and French with the button.

We pass in the locale as the value of the locale prop of IntlProvider to set the app’s locale. And then we set the messages prop to the set of text we want to display according to the locale value.

To add a message, we use the FormattedMessage component.

The id prop is the key of the text we want to display in either messagesInFrench or messagesInEnglish.

defaultMessage is the fallback message we display if the key isn’t found in the object we set as the value of the messages prop.

The values prop lets us interpolate values in the braces in the message strings.

The FormattedNumber component lets us display numbers formatted for the current locale.

We set the value prop to the number we want to display. The style prop is set to 'currency' so we format the number display into the currency specified by the currency prop.

How it is formatted will be done according to the locale set.

Formatting Messages

We can also format messages for later use in our React components.

This is handy for rendering translated text as strings which can be used anywhere.

To do this, we can use the intl.formatMessage method.

For instance, we write:

import { useState } from "react";
import { IntlProvider, useIntl } from "react-intl";

const messagesInFrench = {
  myMessage: "Aujourd'hui, c'est le {ts, date, ::yyyyMMdd}"
};

const messagesInEnglish = {
  myMessage: "Today is {ts, date, ::yyyyMMdd}"
};

const Message = () => {
  const intl = useIntl();
  const message = intl.formatMessage(
    { id: "myMessage" },
    {
      ts: Date.now()
    }
  );
  return <p>{message}</p>;
};

export default function App() {
  const [locale, setLocale] = useState("fr");

  return (
    <>
      <button onClick={() => setLocale((l) => (l === "en" ? "fr" : "en"))}>
        toggle language
      </button>
      <IntlProvider
        messages={locale === "en" ? messagesInEnglish : messagesInFrench}
        locale={locale}
        defaultLocale="en"
      >
        <Message />
      </IntlProvider>
    </>
  );
}

to create the Message component and use it in our App component.

In the Message component, we use the useIntl hook to return the intl object.

intl has the formatMessage method that we can use to return the translated text we want as a string.

The first argument is an object with the id property. id is the key of the message we want to render as in the first example.

The second argument has the values we are interpolating into the string which is like we want passed into the FormattedMessage component’s value prop.

Then we render the returned message string.

Now when we toggle the locale, we see the English and French message with the date interpolated into it.

Extract Translations into Separate Files

If we have lots of translations, we should extract them into separate files and import them into our app.

We can store them in JSON format. For instance, we can extract the messages into JSON files by writing:

en.json

{
  "myMessage": "Aujourd'hui, c'est le {ts, date, ::yyyyMMdd}"
}

fr.json

{
  "myMessage": "Today is {ts, date, ::yyyyMMdd}"
}

Then we can import and use them by writing:

import { useCallback, useEffect, useState } from "react";
import { IntlProvider, FormattedMessage } from "react-intl";

const loadLocaleData = (locale) => {
  switch (locale) {
    case "fr":
      return import("./fr.json");
    default:
      return import("./en.json");
  }
};

export default function App() {
  const [locale, setLocale] = useState("fr");
  const [messages, setMessages] = useState();

  const loadMessages = useCallback(async () => {
    const loadedMessages = await loadLocaleData(locale);
    setMessages(loadedMessages);
  }, [setMessages, locale]);

  useEffect(() => {
    loadMessages();
  }, [loadMessages, locale]);

  return (
    <>
      <button onClick={() => setLocale((l) => (l === "en" ? "fr" : "en"))}>
        toggle language
      </button>
      {messages && (
        <IntlProvider messages={messages} locale={locale} defaultLocale="en">
          <p>
            <FormattedMessage id="myMessage" values={{ ts: Date.now() }} />
          </p>
        </IntlProvider>
      )}
    </>
  );
}

We define the loadLocaleData function to load the translation JSON files dynamically with the import function. It returns a promise with the files as JavaScript objects.

Therefore, to use it in the App component, we define the loadMessages function. In the function, we call loadLocaleData the locale and use await to return the translation message object.

Then we call setMessgaes with loadedMessages to set messages to the translation objects which we use as the value of the message prop of IntlProvider.

Next, we call useEffect with a callback that calls loadMessages to load the translation messages.

Finally, we check if messages is set before we render IntlProvider so that we always have the messages before rendering anything.

And we FormattedMessages as usual to render the translated message.

Dynamic imports are more efficient since the importing is done asynchronously in the background rather than importing the whole JSON synchronously. This will improve performance if we have lots of messages.

Conclusion

One thing we often have to do in our React apps is to add text for multiple languages into our app.

Format.js lets us add translations to our React app easily. We can use it to render translated messages dynamically and also format locale dependent text.


About the Author

John Au-Yeung

John Au-Yeung is a frontend developer with 6+ years of experience. He is an avid blogger (visit his site at https://thewebdev.info/) and the author of Vue.js 3 By Example.

Related Posts

Comments

Comments are disabled in preview mode.