Type-checked and intuitive way to internationalize applications in Javascript and ReactJS using ICU message format.
Internationalization is the design and development of a product, application or document content that enables easy localization for target audiences that vary in culture, region, or language.
Building applications and products for international audiences involves internationalization (i.e: preparing app for translation) and localization (i.e: adoption of application to meet language and cultural requirements). Lingui provides tools to make i18n of JS applications using ICU message format as easy as possible.
Internationalization consists of three steps:
- Specify parts for localization - This is required even for monolingual apps, because it allows to use pluralization, polite forms, date/time formatting, etc.
- Build a message catalog - Message catalogs are passed to translators
- Load translated messages and switch application language
The first part of i18n process is wrapping all texts with component or function, which replaces source text with translation during runtime. js-lingui
uses ICU Message Format which allows using of variables and plural forms.
lingui-js
provides i18n.t
template tag for translation, i18n.plural
, i18n.select
, i18n.selectOrdinal
methods for pluralization and custom forms:
import i18n from 'lingui-i18n'
i18n.t`January`
const name = "Fred"
i18n.t`Hello, my name is ${name}`
i18n.plural({
value: count,
one: `# Book`,
other: `# Books`
})
lingui-react
provides several component for React applications: Trans
is the main component for general translation, Plural
and Select
for pluralization and custom forms (e.g: polite forms):
import React from 'react'
import { Trans, Plural } from 'lingui-react'
class App extends React.Component {
render() {
const name = "Fred"
const count = 42
return (
<div>
// Static text
<Trans>January</Trans>
// Variables
<Trans>Hello, my name is {name}</Trans>
// Components
<Trans>See the <a href="/more">description</a> below.</Trans>
// Plurals
<Plural
value={count}
zero={<strong>No books</strong>}
one="# book"
other="# books"
/>
</div>
)
}
}
Sometimes it's necessary to translate also a text attributes, which doesn't accept React components. lingui-react
has WithReact
decorator, which injects i18n
object from lingui-i18n
.
import React from 'react'
import { WithI18n } from 'lingui-react'
// Translating text attributes
class LinkWithTooltip extends React.Component {
render() {
const { articleName, i18n } = this.props
return (
<a
href="/more"
title={i18n.t`Link to ${articleName}`}
>
<Trans>Link</Trans>
</a>
)
}
}
// Decorate component. WithReact actually expects options as a first
// argument and return customized decorator:
// Signature: WithReact = (options) => (WrappedComponent)
LinkWithTooltip = WithReact()(LinkWithTooltip)
At this point, application is available only in one language (English). When no translations are available the default texts are used.
Translators are working with message catalogs which are mapping of messages from source to target language. The simplest form is a dictionary, where key is source message and value is translated one:
const fr = {
"January": "Janvier",
"Hello, my name is {name}": "Salut, je m'appelle {name}",
"See the <0>description</0> below.": "...",
"{count, plural, zero {<0>No books</0>} one {# book} other {# books}": "..."
}
The key is also called message ID. It must be unique across application. It should also include all parameters so translators can change the order of words and parameters if required.
js-lingui
provides three babel plugins and CLI for building message catalogs:
npm install --save-dev lingui-i18n
# or
yarn add --dev lingui-i18n
# add directories for target languages
lingui add-locale en fr
# extract messages from source to message catalogs
lingui extract
Target directory for locales is configured in package.json
:
{
"lingui": {
"localeDir": "./locale"
}
}
Under the hood, there're three babel plugins responsible for creating message catalogs:
-
babel-plugin-lingui-transform-js
This plugin transforms methods and template tag from
lingui-i18n
into ICU message format which becomes message ID.i18n.t`Hello, my name is ${name}` /* becomes this entry in source language file: * { * "Hello, my name is {name}": "" * } */
-
babel-plugin-lingui-transform-react
This plugin transforms components from
lingui-react
(e.g:Trans
) into ICU message format which becomes message ID.Note: It's also possible to use custom message IDs. Simply pass
id
attribute toTrans
component and children's going to be used as a default message only.<Trans id="month.january">January</Trans> /* becomes this entry in source language file: * { * "month.january": "January", * ... * } */
-
babel-plugin-lingui-extract-messages
- It extracts all message IDs into temporary catalogs, one catalog per file.
The result is one message catalog per language (e.g: locale/fr/messages.json
).
Translated message catalogs must be loaded back to application. The process depends on type of application.
lingui-i18n
uses .load(messages)
method to load message catalog and
.use(language)
to select a language:
import i18n from 'lingui-i18n'
import messagesEn from './locales/en/messages.json'
import messagesFr from './locales/fr/messages.json'
// load message catalogs
i18n.load({
en: messagesEn,
fr: messagesFr
})
// activate english language
i18n.activate('en')
lingui-react
provides ProvideI18n
component which receives active language and messages for that language:
import React from 'react'
import { ProvideI18n } from 'lingui-react'
import App from './App'
import messages from './locales/fr/messages.json'
render(
<ProvideI18n language="fr" messages={messages}>
<App />
</ProvideI18n>,
document.getElementById('app')
)
When we render messages from the first part, we get them translated in French:
<Trans>January</Trans>
// becomes: Janvier
const name = "Fred"
<Trans>Hello, my name is {name}</Trans>
// becomes: Salut, je m'appelle Fred
<Trans>See the <a href="/more">description</a> below.</Trans>
// becomes: Voir <a href="/more">la description</a> ci-dessous
const count = 42
<Plural
value={count}
zero={<strong>No books</strong>}
one="# book"
other="# books"
/>
// becomes: 42 livres
See wiki for more info or example-usecase for detailed example.
lingui-cli
Docs
Command line interface for working with message catalogs.
lingui-i18n
Docs
Functions for I18n in Javascript.
lingui-react
Docs
Components for I18n in React.
I18nProvider
– context provider of all i18n data (messages, current language, etc.)WithI18n
- HOC for passing i18n data from context to props of wrapped component. Also takes care of updates when context data changes, but some ancestor skip update (shouldComponentUpdate
returnsfalse
).Trans
– component for message formating and translationSelect
– select message based on variablePlural
– select plural based on numberSelectOrdinal
– select ordinal number
babel-plugin-lingui-transform-js
Docs
Transform function from lingui-i18n
into ICU message format.
babel-plugin-lingui-transform-react
Docs
Transform components from lingui-react
into ICU message format.
Extract all messages for translation to external files.