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