ICU Messages in React with react-intl (FormatJS)
Implement ICU MessageFormat in React applications using react-intl (FormatJS). Covers FormattedMessage component, useIntl hook, plural/select usage, and best practices.
Detailed Explanation
Using ICU MessageFormat in React
react-intl (part of the FormatJS suite) is the most popular internationalization library for React applications. It uses ICU MessageFormat as its message syntax.
Setup
import { IntlProvider, FormattedMessage } from 'react-intl';
const messages = {
en: {
greeting: 'Hello, {name}!',
itemCount: '{count, plural, one {# item} other {# items}} in cart',
},
ja: {
greeting: 'こんにちは、{name}!',
itemCount: 'カートに{count, plural, other {#個の商品}}',
},
};
function App() {
const locale = 'en';
return (
<IntlProvider locale={locale} messages={messages[locale]}>
<MyComponent />
</IntlProvider>
);
}
FormattedMessage Component
<FormattedMessage
id="greeting"
defaultMessage="Hello, {name}!"
values={{ name: userName }}
/>
useIntl Hook
For programmatic formatting (tooltips, aria-labels, etc.):
import { useIntl } from 'react-intl';
function MyComponent() {
const intl = useIntl();
const label = intl.formatMessage(
{ id: 'itemCount', defaultMessage: '{count, plural, one {# item} other {# items}}' },
{ count: items.length }
);
return <span aria-label={label}>...</span>;
}
Rich Text with React Elements
react-intl supports React elements as variable values:
<FormattedMessage
id="tos"
defaultMessage="I agree to the <link>Terms of Service</link>"
values={{
link: (chunks) => <a href="/tos">{chunks}</a>,
}}
/>
The message file:
I agree to the <link>Terms of Service</link>
Plural and Select
<FormattedMessage
id="notification"
defaultMessage="{gender, select, male {He} female {She} other {They}} liked {count, plural, one {your post} other {# of your posts}}."
values={{ gender: user.gender, count: likeCount }}
/>
Message Extraction
FormatJS provides a CLI tool for extracting messages from source code:
npx formatjs extract 'src/**/*.tsx' --out-file lang/en.json
This scans all FormattedMessage usage and generates a JSON file for translators.
Best Practices
- Always provide
defaultMessage-- serves as the English source and fallback - Use message IDs like
page.section.messageKeyfor organization - Extract messages into separate JSON files, not inline
- Test with pseudo-localization to catch truncation and layout issues
- Use the
intl.formatMessagehook for non-JSX contexts (titles, ARIA labels)
Use Case
React developers setting up internationalization for the first time or migrating from string concatenation to ICU MessageFormat using the FormatJS / react-intl ecosystem.