Socket
Socket
Sign inDemoInstall

lingui-react

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

lingui-react


Version published
Maintainers
1
Install size
680 kB
Created

Readme

Source

Tutorial | API reference

lingui-react

React Components for I18n using ICU message format

lingui-react provides React components for writing texts using ICU message format

Install

npm install lingui-react
# or
yarn add lingui-react

:warning: It's possible to use this library without babel-preset-lingui-react. However, it's highly encouraged to use this preset. All examples below assume that it's installed.

Quickstart

This is an introductory tutorial. For API reference, scroll below.

lingui-react provides several components which represent ICU MessageFormat in React. It's useful to understand this format, but certainly not required.

Please make sure that you have babel-preset-lingui-react installed and added to your babel config.

:bulb: All components are exported from the top of lingui-react package.

1. Setup I18nProvider

Translations are loaded from message catalogs for given language. The top component which adds messages and language to React component tree is I18nProvider:

import React from 'react'
import { render } from 'react-dom'
import { I18nProvider } from 'lingui-react'

import messages from './locales/en.json'
import App from './App'

render(
    <I18nProvider language="en" messages={{ en: messages }}>
       <App />
    </I18nProvider>,
    document.getElementById("app")
)

:hammer_and_wrench: Under the hood this component adds messages and language to context. All lingui-react components access messages from this context, so it's not necessary to pass them in props manually.

:bulb: Active language can be managed for example in redux store. In that case, add I18nProvider in main component (e.g. App) and read language from redux store. Loading of messages dynamically can be achieved in several ways depending on bundler (e.g. webpack) and it's not covered in this tutorial.

2. Wrap messages in i18n components

The simplest i18n component is Trans. It's used for simple, singular translations. It supports both variables and inline components.

Suppose we have a Header component, which displays the name of currently logged in user:

import React from 'react'

const Header = ({ username }) => (
  <div>
    <h1>Documentation</h1>
    
    <div className="UserProfile">
        Welcome, <Link to="/profile">{username}</Link>
    </div>
  </div>
)

If we want to translate this component, just wrap all messages in Trans component:

import React from 'react'
import { Trans } from 'lingui-react'

const Header = ({ username }) => (
  <div>
    <h1><Trans>Documentation</Trans></h1>
    
    <div className="UserProfile">
        <Trans>Welcome, <Link to="/profile">{username}</Link></Trans>
    </div>
  </div>
)

This process is completely seamless and doesn't require any other changes to Header component.

:warning: Only simple variables are supported inside Trans component. It's not possible to use constants, function calls or object properties. Any of following examples won't work:

const messages = [
  "Hello",
  "World"
]

<Trans>{messages.length}</Trans>
<Trans>{isEmpty(messages)}</Trans>
<Trans>The answer is {42}</Trans>
:hammer_and_wrench: How the extracted messages are going to look like

Extraction of messages into external message catalogs isn't covered in this tutorial because it's a responsibility of lingui-cli package. However, now it's probably the best and only time to mention, how extracted messages are going to look like.

Given the example above, we'll get two messages:

// example of empty message catalog
export default {
  "Documentation": "",
  "Welcome, <0>{username}</0>": ""
}

Anything wrapped inside Trans component is considered as a source message.

Names of variables are preserved. This is the reason why only simple variables are allowed - variable names are used as placeholders.

Inline components are replaced with dummy <0>, <1>, etc. tags. Using anonymous tags has several advantages:

  • Both built-in (e.g: span) and custom (e.g: Link) components are supported
  • Change of props doesn't require update of translations (e.g. when class name is changed)

3. Translations of attributes

Sometimes it's necessary to translate element attributes, like link title or aria-label. Trans is a React component, but in this case we need just a text.

It's possible to access low-level i18n API using WithI18n high-order component. All we need to do is wrap our component and then we can use lingui-i18n API from i18n prop:

import React from 'react'
import { Trans, WithI18n } from 'lingui-react'

// WithI18n injects `i18n` prop 
const LogoutIcon = WithI18n()(
    ({ i18n }) => <Icon name="turn-off" aria-label={i18n.t`Log out`}/>
)

:warning: Watch the empty braces after WithI18n. WithI18n expects options as a first argument and return configured HOC.

i18n.t returns plain text. It's the same API which can be used outside React context and it's described in lingui-i18n

4. Plurals, categories, and formats

ICU MessageFormat is very powerful and extensible format. It supports plurals, categories and date/number formats.

Plurals

Plural component selects the appropriate plural form based on given value. For example in English, there are only two plural forms: singular and plural. If we want to translate message with plural support, we need to define both:

import React from 'react'
import { Trans } from 'lingui-react'

const Notifications = ({ newMessageCount }) => {
  return (
    <div>
      <Plural 
         value={newMessageCount}
         one="# message"
         other="# messages"
      />
    </div>
  )
}

In English, Plural will render 1 message, but 3 messages depending on value. It means that one prop expresses singular while other is for plural. Plural forms for other than source language are defined in message catalogs. If we use English as a source language, we'll always use only one and other props in Plural.

Just to clarify, the message above is extracted as:

{newMessageCount, plural, one {# message} other {# messages}}

In Czech, it would be translated as:

{newMessageCount, plural, one {# zpráva} few {# zprávy} other {# zpráv}}

This is different compared to gettext, which maps singular/plural to language specific plural forms. In ICU message format, we always have only one message with all plural forms per language.

:hammer_and_wrench: All possible plural forms are zero, one, two, few, many, other.

:bulb: Plural forms for all languages are listed at CLDR page - Language Plural Rules.

Select

Some languages have different forms based on gender or level of politeness. Such cases are handled using Select component, which works like a switch statement:

import React from 'react'
import { Trans } from 'lingui-react'

const Book = ({ gender }) => {
  return (
    <div>
      <Select 
         value={gender}
         male="His book"
         female="Her book"
         other="Their book"
      />
    </div>
  )
}

There's no magic here. Select uses the form based on value. other prop defines default form.

DateFormat and NumberFormat

Localization isn't just about text, but also about different number and date formats. NumberFormat is used for formatting numbers (decimals, percents, currencies), while DateFormat is for dates:

import React from 'react'
import { DateFormat, NumberFormat } from 'lingui-react'

const Invoice = ({ invoiceNumber, datePaid, amount }) => {
  return (
    <div>
      <h1><Trans>Invoice {invoiceNumber}</Trans></h1>

      <Trans>
        Amount <NumberFormat 
          value={amount} 
          format={{ style: 'currency', currency: 'EUR' }} 
        /> paid on <DateFormat value={datePaid} />
      </Trans>
    </div>
  )
}

6. What's next

Once the app is internationalized, it's ready to be localized. Localization process includes extracting messages into message catalog, translating message catalogs and importing them in the application.

lingui-cli provides extract command, which creates new or updates existing message catalogs.

Finally, after the message catalog is translated, it can be loaded back to the application. I18nProvider expects a dictionary of messages, where the key is a message in source language and the value is a message in target langauge.

Other useful links:

API

ComponentExample MessageFormat
TransMy name is {name}
Categories
Plural{count, plural, one {# book} other {# books}}
Select{gender, select, male {His} female {Her} other {Their}}
SelectOrdinal{count, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}
Formats
DateFormat{value, date, short}
NumberFormat{value, number, percent}

General concepts

Rendering in components

All i18n components support two optional props:

Prop nameTypeDescription
classNamestringClass name to be added to <span> element
renderReact.ElementReact.Class

Setting className is enough in most cases. If we need to replace <span> with a custom element, we can pass it to render prop.

When render is React.Element, it is cloned with translation as children:

<Trans render={<h1 />}>Heading</Trans>

Using React.Component (or stateless component) in render prop is useful to get more control over the rendering of translation. Component passed to render receive translation/value in translation prop.

<Trans render={({ translation }) => <Icon label={translation} />}>
    Sign in
</Trans>
Variables inside components

:warning: Only simple variables are supported inside Trans component and as a value prop in other i18n components. It's not possible to use constants, function calls or object properties. Any of following examples won't work:

const messages = [
  "Hello",
  "World"
]

<Trans>{messages.length}</Trans>
<Trans>{isEmpty(messages)}</Trans>
<Trans>The answer is {42}</Trans>
<DateFormat value={new Date()}

Trans

This is the main component for translation. It supports variables and components inside messages. It's also possible to override generated message ID using id prop.

Prop nameTypeDescription
idstringOverride auto-generated message ID
Examples:
// basic use case
<Trans>Hello World</Trans>
// custom message ID
<Trans id="msg.hello">Hello World</Trans>
// variable interpolation
const name = "Fred"
<Trans>My name is {name}</Trans>
// inline components
<Trans>See the <Link to="/more">description</Link> below.</Trans>

Plural

MessageFormat: {arg, plural, ...forms}

Prop nameTypeDescription
valuenumberOverride auto-generated message ID
offsetnumberOffsets plural forms but doesn't affect exact matches
zerostringForm for empty value
onestringSingular form
twostringDual form
fewstringPaucal form
manystringPlural form
otherstring(required) general plural form
_0, _1, ...stringExact match form, correspond to =N rule

See Language Plural Rules overview.

Based on language, the value is mapped to specific plural form. If such plural form isn't defined, the other form is used. # character inside message is used as a placeholder for value:

const count = 42
// renders as '42 books'
<Plural 
    value={count}
    one="# book"
    other="# books"
/>

It's also possible to use exact matches. This is common used in combination with offset prop. offset doesn't affect value for exact matches, only plural forms:

const count = 42
<Plural 
    value={count}
    offset={1}
    // when value = 0
    _0="Nobody arrived"
    
    // when value = 1
    _1="Only you arrived"
    
    // when value = 2
    // value - offset = 1 -> `one` plural form
    one="You and # other guest"
    
    // when value >= 3
    other="You and # other guests"
/>

Select

MessageFormat: {arg, select, ...forms}

Prop nameTypeDescription
valuenumberOverride auto-generated message ID
othernumber(required) Default, catch-all form

This component selects appropriate form based on content of value. It behaves like an switch statement. other prop is used when no prop matches value:

// gender = "female" -> `Her book`
// gender = "male" -> `His book`
// gender = "unspecified" -> `Their book`
<Select 
    value={gender}
    male="His book"
    female="Her book"
    other="Their books"
/>

SelectOrdinal

MessageFormat: {arg, selectordinal, ...forms}

Prop nameTypeDescription
valuenumberOverride auto-generated message ID
offsetnumberOffsets plural forms but doesn't affect exact matches
zerostringForm for empty value
onestringSingular form
twostringDual form
fewstringPaucal form
manystringPlural form
otherstring(required) general plural form
_0, _1, ...stringExact match form, correspond to =N rule

See Language Plural Rules overview.

This component is equivalent to Plural. The only difference is that it uses ordinal plural forms, instead of cardinal ones.

<SelectOrdinal
    value={count}
    one="#st"
    two="#nd"
    few="#rd"
    other="#th"
/>

DateFormat

MessageFormat: {arg, date, format}

Prop nameTypeDescription
valuenumberDate to be formatted
formatstring or ObjectDate format passed as options to Intl.DateTimeFormat

This component is a wrapper around Intl.DateTimeFormat.

const now = new Date()
// default language format
<DateFormat value={now} />
const now = new Date()
// custom format
<DateFormat value={now} format={{
    year: "numberic",
    month: "long",
    day: "numeric"
}}/>

NumberFormat

MessageFormat: {arg, number, format}

Prop nameTypeDescription
valuenumberNumber to be formatted
formatstring or ObjectNumber format passed as options to Intl.NumberFormat

This component is a wrapper around Intl.NumberFormat.

const num = 0.42
// default language format
<NumberFormat value={num} />
const amount = 3.14
// custom format
<NumberFormat value={amount} format={{
    style: "currency",
    currency: 'EUR',
    minimumFractionDigits: 2
}}/>

Providers

Message catalogs and active language are passed to the context in I18nProvider. However, context should never be accessed directly. WithI18n hoc passes i18n prop to wrapped component and shadows all implementation details:

I18nProvider

Prop nameTypeDescription
languagestringActive language
messagesobjectMessage catalog

messages must be in format: { [language]: messageCatalog }. This component should live above all i18n components. The good place is top-level application component. However, if the language is stored in the redux store, this component should be inserted below react-redux/Provider.

import React from 'react'
import { Provider, connect } from 'react-redux'
import { I18nProvider } from 'lingui-react'

const App = connect(state => ({ language: state.language }))(
    ({ language} ) => {
        const messages = require(`locales/${language}.json`)
        return (
            <I18nProvider language={language} messages={{ [language]: messages }}>
               // the rest of app
            </I18nProvider>,
        )
    }
)

WithI18n

WithI18n([ options ])

OptionsDescription
withRefReturns reference to wrapped instance in getWrappedInstance

This HOC injects i18n prop to wrapped component. It's useful when wrapped component needs to access lingui-i18n API for plain text translations.

import React from 'react'
import { Trans, WithI18n } from 'lingui-react'

// WithI18n injects `i18n` prop 
const LogoutIcon = WithI18n()(
    ({ i18n }) => <Icon name="turn-off" aria-label={i18n.t`Log out`}/>
)

License

MIT

Keywords

FAQs

Last updated on 29 Jun 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc