react-i18next vs react-intl
Which should you use for your React project?
Before we get started...
Let's ensure you're in the right place. If you are building a Next.js app, you should instead be reading the next-i18next vs react-intl comparison.
If you're looking for a full walkthrough on setting up react-i18next with a React app, check out this post. If you want a full walkthrough setting up react-intl, check out this post.
Introduction
There are countless React internationalization libraries out there, but far and above the rest in usage are react-i18next and react-intl. Both of these libraries have been around for quite a few years and have gained popularity in line with the popularity of React in general.
Background
Let’s first discuss some of the similarities between react-i18next and react-intl. Both libraries are open source and actively maintained. They have about the same length of history according to NPM, and both support TypeScript. It’s safe to say that regardless of your choice, either of these libraries will get the job done.
The react-i18next library is part of the i18next open source ecosystem. i18next has libraries for several frameworks, not just React. On the other hand, react-intl is part of the Format.js collection of internationalization libraries originally started by Yahoo.
As of the writing of this post (April 2023), react-i18next exceeds react-intl in the number of weekly NPM downloads. Currently react-intl is at 1.3 million weekly downloads and react-i18next is at about 2 million. react-i18next also appears to be increasing in popularity at a higher rate than react-intl.
Resources vs Messages
Let's clarify the terminology used in each library. react-i18next refers to translations as "resources" while react-intl refers to them as "messages". In effect, they are the same thing. But they do use different formatting and syntax.
One of the primary alluring aspects of react-intl over react-i18next is that it is built around the ICU Message Format syntax. ICU Message Format is a common standard for internationalization. It allows for more interoperability between all types of programming languages and platforms.
i18next uses its own syntax, so keep in mind that if you ever expand out of JavaScript into other programming environments, you will have to hope there is well-supported i18next library available. However it is worth mentioning that i18next does have an ICU add-on available, but it is used by a fraction of i18next users and is not the primary focus of the library. That isn’t to say that the add-on doesn’t work well, but if ICU is important to you then you probably want a library built around it.
Let's take a look at differences in how resources and messages are written:
react-i18next
{
"interpolation": "Hello {{name}}!",
"plural_one": "You have one notification.",
"plural_other": "You have {{count}} notifications."
}
react-intl
{
"interpolation": "Hello {name}!",
"plural": "{count, plural,
one {You have one notification.}
other {You have # notifications.}
}"
}
As you can see, interpolation in each library is pretty straightforward. The only difference being that react-i18next uses double curly braces while react-intl uses single.
Pluralization
Pluralization gets a little more complicated in each library since it's a rather complicated topic. Both libraries use the Intl API standard for pluralization. For a brief explanation, the Intl API uses 6 possible plural forms: "zero", "one", "two", "few", "many", and "other". Languages like English and Spanish have 2 plural forms (one and other), Chinese has 1 form (other), and Arabic has all six.
Nowadays if you are using an auto-translation tool like i18nexus, you're probably not worried about handling all the different types of plural forms since it's handled automatically for you. But if you are not using a tool like i18nexus and prefer to manage all of this yourself manually, read on.
react-i18next uses separate keys for each plural form. In the above example, "plural" is the root key and "_one" or "_other" is appended to each plural form's key. Since react-intl is using the ICU Message Format for its syntax, only one key is required and its value contains all plural forms.
Objectively react-i18next's pluralization formatting looks a lot cleaner and less prone to syntax errors. But as mentioned before, this syntax doesn't have as much interoperability with other programming environments since it is not using ICU.
Bundle Sizes
react-i18next is going to be a little more costly than react-intl when it comes to bundle size. The react-i18next library is about 7kb minified and gzipped, but requires the i18next library which is about 15kb for a total of 22kb. On the other hand, react-intl is about 17kb minified and gzipped. This is not a major difference for most apps, but its important to remember that react-i18next is more feature-packed than react-intl. So the tradeoff may be worth it depending on your needs.
Ease of Set Up
As you would expect, the less feature-packed library is more straightforward to understand. In this case, that is react-intl. Let’s take a look at the minimum of what it takes to set up react-intl in your React app:
react-intl
import { useState } from 'react';
import { FormattedMessage, IntlProvider } from 'react-intl';
const messages = {
en: {
welcome_msg: "Welcome to my app"
},
es: {
welcome_msg: "Bienvenido a mi aplicación"
}
}
export default function App() {
const { locale, setLocale } = useState('en');
return (
<IntlProvider locale={locale} messages={messages[locale]}>
<div className="App">
<p>
<FormattedMessage id="welcome_msg" />
</p>
</div>
</IntlProvider>
)
}
Any developer coming to this code will pretty easily understand how it works. A set of messages is defined and organized by language, while the current locale is set using React state. The IntlProvider
provides the messages to all child components, while the FormattedMessage
component renders a message.
Let’s take a look at the minimum set up for react-i18next:
react-i18next
import i18n from "i18next";
import { useTranslation, initReactI18next } from "react-i18next";
i18n
.use(initReactI18next)
.init({
resources: {
en: {
translation: {
welcome_msg: "Welcome to my app"
}
},
es: {
translation: {
welcome_msg: "Bienvenido a mi aplicación"
}
}
},
lng: "en",
fallbackLng: "en"
});
function App() {
const { t } = useTranslation();
return (
<div className="App">
<p>
{t('welcome_msg')}
</p>
</div>
)
}
The i18next set-up also appears quite simple, although it does require reading through documentation to understand the config. The main difference is that i18next creates an instance of itself that is globally accessible, not necessarily through a provider. The initialized i18next instance is accessed by the useTranslation
hook to provide translations within a component. While react-intl does have optional hooks available, react-i18next is more of a hooks-first library.
i18next’s myriad of add-ons and configuration options make it much more feature-rich than react-intl. For example, getting the current user’s preferred language is quite easy in react-i18next using the i18next-browser-languagedetector add-on. But react-intl provides no such feature, requiring developers to come up with their own implementation or use some other language detection library.
Wrapping it up
In the end, either library will suffice for your internationalization needs. While i18next is more feature packed, react-intl is simpler with a less opinionated integration. Based on the popularity of each, it appears that react-i18next is marching forward at a quicker pace and is the preferred React i18n library for new projects. But that isn’t to say that react-intl is on the decline. Its weekly download count also continues to rise, likely due to its usage of the standardized ICU Message Format. If you want more information, I recommend exploring the react-i18next docs and the react-intl docs. Happy internationalizing!
Level up your localization
It only takes a few minutes to streamline your translations forever.
Get started