Comparison with react-intl¶
react-intl is definitely the most popular and widely-used i18n library for React. LinguiJS is in many ways very similar: both libraries use the same syntax for messages (ICU MessageFormat) and they also have very similar API.
Here’s an example from react-intl docs:
<FormattedMessage
id="welcome"
defaultMessage={`Hello {name}, you have {unreadCount, number} {unreadCount, plural,
one {message}
other {messages}
}`}
values={{name: <b>{name}</b>, unreadCount}}
/>
Looking at the low-level API of LinguiJS, there isn’t much difference:
<Trans
id="welcome"
defaults={`Hello {name}, you have {unreadCount, number} {unreadCount, plural,
one {message}
other {messages}
}`}
values={{name: <b>{name}</b>, unreadCount}}
/>
There’s really no reason to reinvent the wheel when both libs are build on top of the same message syntax. The story doesn’t end here, though.
Translations with rich-text markup¶
Suppose we have the following text:
<p>Read the <a href="/docs">documentation</a>.</p>
In react-intl, this would be translated as:
<FormattedMessage
id='msg.docs'
defaultMessage='Read the <link>documentation</link>.'
values={{
link: (...chunks) => <a href="/docs">{chunks}</a>
}}
/>
LinguiJS extends ICU MessageFormat with tags. The example above would be:
<Trans
id='msg.docs'
defaults='Read the <0>documentation</0>.'
components={[
<a href="/docs" />
]}
/>
and the translator gets the message in one piece: Read the <0>documentation</0>
.
However, let’s go yet another level deeper.
Macros for component-based message syntax¶
LinguiJS provides macros @lingui/macro
which automatically generates a message
syntax.
Let’s go back to the previous example:
<p>
Read the <a href="/docs">documentation</a>.
</p>
All we need to do is to wrap the message in a <Trans> macro:
<p>
<Trans id="msg.docs">Read the <a href="/docs">documentation</a>.</Trans>
</p>
The macro then parses the <Trans> macro children and generates
defaults
and components
props automatically in the form described in the previous section.
This is extremely useful when adding i18n to an existing project. All we need is to wrap all messages in <Trans> macro.
Let’s compare it with react-intl solution to see the difference:
<p>
<FormattedMessage
id='msg.docs'
defaultMessage='Read the <link>documentation</link>.'
values={{
link: (...chunks) => <a href="/docs">{chunks}</a>
}}
/>
</p>
Note
It’ also worth mentioning that the message IDs are completely optional. LinguiJS is unopinionated in this way and perfectly works with messages as IDs as well:
<p>
<Trans>Read the <a href="/docs">documentation</a>.</Trans>
</p>
The message ID is Read the <0>documentation</0>.
instead of msg.id
. Both
solutions have pros and cons and the library lets you choose the one which works best for you.
Plurals¶
Another very common linguistic feature is pluralization.
Let’s take a look at the original example from react-intl docs:
<FormattedMessage
id="welcome"
defaultMessage={`Hello {name}, you have {unreadCount, number} {unreadCount, plural,
one {message}
other {messages}
}`}
values={{name: <b>{name}</b>, unreadCount}}
/>
Using LinguiJS macros, we could combine <Trans>, <Plural> components and number macro:
<Trans id="welcome">
Hello <b>{name}</b>, you have {number(undreadCount)} <Plural
one="message"
other="messages"
/>
</Trans>
and the final message would be very similar:
<Trans
id="welcome"
defaults={`Hello <0>{name}</0>, you have {unreadCount, number} {unreadCount, plural,
one {message}
other {messages}
}`}
values={{name, unreadCount}}
/>
The only difference is the <0> tag included in the message, as LinguiJS can handle components in both variables and the message itself.
Note
It’s good to mention here that this isn’t the best example of using plurals. Make your translators happy and move plurals to the top of the message:
<Plural
id="welcome"
value={number}
one={<>Hello <b>{name}</b>, you have {number(undreadMessages)} message.</>}
other={<>Hello <b>{name}</b>, you have {number(undreadMessages)} messages.</>}
/>
Even though both variants are syntactically valid in ICU MessageFormat, the second one is easier for translating, because (again) the translator gets the phrase in one piece.
Text attributes¶
Components can’t be used in some contexts, e.g. to translate text attributes.
Whereas react-intl provides JS methods (e.g: formatMessage
) which return plain
strings, LinguiJS offers its core library for such translations. And it also provides
macros for these usecases!
Here are a few short examples:
<a title={i18n._(t`The title of ${name}`)}>{name}</a>
<img alt={i18n._(plural({ value: count, one: "flag", other: "flags" }))} src="..." />
Custom IDs are supported as well:
<a title={i18n._(t("link.title")`The title of ${name}`}>{name}</a>
<img alt={i18n._(plural("img.alt", { value: count, one: "flag", other: "flags" }))} src="..." />
Note
To inject i18n
object into props, you need to use HOC withI18n()
. It’s
very similar to injectIntl
from react-intl. Alternatively, you can also use
<I18n> render prop component.
External message catalog¶
Let’s say our app has been internationalized and we now want to send the messages to the translator.
react-intl comes with the Babel plugin which extracts messages from individual files, but it’s up to you to merge them into one file which you can send to translators.
LinguiJS provides handy CLI which extracts messages and merges them with any existing translations. Again, the story doesn’t end here.
Compiling messages¶
The biggest and slowest part of i18n libraries are message parsers and formatters. LinguiJS compiles messages from MessageFormat syntax into JS functions which only accept values for interpolation (e.g. components, variables, etc). This makes the final bundle smaller and makes the library faster. The compiled catalogs are also bundled with locale data like plurals, so it’s not necessary to load them manually.
Disadvantages of LinguiJS¶
react-intl has been around for some time and it’s definitely more popular, more used and a lot of production sites are running it. The community is larger and it’s much easier to find help on StackOverflow and other sites.
LinguiJS is a very new library and the community is very small at the moment. It’s not tested on many production sites. On the other hand, LinguiJS’s testing suite is very large and all examples are incorporated into the integration testing suite to make sure everything is working fine.
Last but not least, LinguiJS is actively maintained. Bugs are fixed regularly and new features are constantly coming in. Work is currently progressing on webpack code splitting, so that only messages related to the code in the chunk are loaded.
Summary¶
both libraries use the same MessageFormat syntax
similar API (easy to port from one to the other)
On top of that, LinguiJS:
supports rich-text messages
provides macros to simplify writing messages using MessageFormat syntax
provides a CLI for extracting and compiling messages
is very small (<5kb gzipped) and fast
works also in vanilla JS (without React)
is actively maintained
On the other hand, react-intl:
is the most popular and used i18n lib in React
is used in many production websites (stability)
has lots of resources available online