Migration guide from 1.x to 2.x

The biggest change in 2.x is a new build & release process which allowed simplify public API. Library is also taking advantage of latest React features while providing escape hatch to be used also in older React versions.

Backward incompatible changes

New scope of NPM packages

Packages are now released under @lingui organization. Some package names were renamed:

New package name

Previous package name





















Default message catalog format

LinguiJS used comprehensive lingui format which contains additional info like location of message in source and default translation (if any). Since most 3rd party tools work with simple JSON only, the default format will be minimal.

  • If you want to keep lingui format, add "format": "lingui" to your lingui config in package.json.

  • If you want to migrate lingui format to minimal, remove format from configuration (if any) and run lingui extract --convert-from lingui.

  • If you used minimal format before, simply remove "format": "minimal" from lingui configuration.

unpackCatalog is deprecated

unpackCatalog was a useless optimization, which probably saved less bytes that was a size of unpackCatalog function. lingui compile now produce unpacked file, which can be used directly in <I18nProvider>:

import { I18nProvider } from 'lingui-react'
import catalogEn from './locale/en/messages.js'

function App() {
   return (
      <I18nProvider language="en" catalogs={{ en: catalogEn }}>
         <App />

development prop of I18nProvider is deprecated

LinguiJS doesn’t include message parser in production bundle, because messages are compiled to functions during build. However, in development it’s convenient to see parsed and formatted messages right away without need to run lingui extract and lingui compile everytime we add new text to an app.

Message parser is included in development package along with plural rules for all languages. This package is now included automatically in development build and the content is scoped under if (process.env.NODE_ENV === 'production') so it’s removed using dead code elimination techniques. To remove it, simply build your app with NODE_ENV=production. This change is inspired by React.

Default wrapping components removed

In React < 16.2, components had to return single children which had to be either React Element or null. For this reason, <I18nProvider> wrapped children in div and <Trans> wrapped translations in span.

React 16.2 allows multiple children and text children, so default wrapping components are removed.

If you’re using React < 16.2 or you want to keep the previous behavior:

  1. Set defaultRender prop of <I18nProvider> to span.

  2. Wrap children of <I18nProvider> into div explicitly.

import * as React from 'react'
import { I18nProvider } from 'lingui-react'

function App() {
   return (
      <I18nProvider defaultRender="span">
            // original children of I18nProvider

Package lingui-formats merged to lingui-i18n

lingui-formats package was used for date/number formatting and was a wrapper around Intl module. It only exported two functions: date and number, so it was merged to lingui-i18n. It’s unlikely that you imported from it directly but if you did, simply import date and number functions from lingui-i18n:

// Before
import { date, number } from 'lingui-formats'

// After
import { date, number } from 'lingui-i18n'

Signature of i18n._ changed

i18n._ is low-level API for message translation and formatting. The function signature has changed from:

i18n._(messageId: string, { values: Object, defaults: string, formats: Object })


i18n._(messageId: string, values: Object, { defaults: string, formats: Object })

This change makes usage easier, because values are commonly used parameter.

Since this is a low-level API, you probably haven’t used it directly and it’s enough to upgrade your lingui babel plugin. Otherwise simple refactoring is required:

// before
i18n._('Hello {name}', { values: { name: "Fred" } })

// after
i18n._('Hello {name}', { name: "Fred" })

New features

Custom IDs for @lingui/core methods

Most i18n methods in core library (i18n.t, i18n.select, i18n.plural) accept custom message ID as the first argument. Generated message is used as a default one:

i18n.t('id')`Default message`

i18n.plural('id', {
   one: 'Book',
   other: 'Books'

ID is valid only for root calls of i18n methods:

// this doesn't work, `id` is set in nested i18n method
i18n.t`This is their ${i18n.selectOrdinal(`id`, { count: count, ... })}`