Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: migration path from react-intl to js-lingui? #154

Closed
tinynumbers opened this issue Jan 30, 2018 · 11 comments
Closed

Question: migration path from react-intl to js-lingui? #154

tinynumbers opened this issue Jan 30, 2018 · 11 comments

Comments

@tinynumbers
Copy link

Has there been any documented approach (or tooling) around ways to migrate an existing code-base from react-intl translations to js-lingui translations?

I have a rather large codebase that was originally built around react-intl but I'd like to change it to use js-lingui everywhere... but not finding much direction on tackling this project.

Any suggestions?

@tricoder42
Copy link
Contributor

Hello @tinynumbers,
so the question has been finally asked. I was thinking about it from the beginning, but fortunately I started working on fresh new project when I come up with jsLingui.

This would be great to document and once the instructions are clear, we could turn them into codemod.

TLDR: There's no clear migration path at the moment.

Components

I think the migration should could be done in two stages:

  1. Replace all FormatMessage components with Trans. These component are almost compatible, you just need to rename defaultMessage to defaults. Also, jsLingui doesn't support message descriptions at the moment.

  2. Replace default message (or ID if natural language is used as an ID) with children. This step requires babel plugins/presets so it's optional if you can't use them for whatever reason.

// react-intl
<FormattedMessage
    id='app.greeting'
    description='Greeting to welcome the user to the app'
    defaultMessage='Hello, {name}!'
    values={{
        name: 'Eric'
    }}
/>

// @lingui/react - first phase
<Trans
    id='app.greeting'
    // description='Greeting to welcome the user to the app'
    defaults='Hello, {name}!'
    values={{
        name: 'Eric'
    }}
/>

// @lingui/react - final phase, using babel presets
<Trans id='app.greeting'>Hello {name}</Trans>

Functions (aka Injection API)

injectIntl should be replaced with withI18n

// react-intl
injectIntl(Component, options)

// @lingui/react
withI18n(options)(Component)

// without options
withI18n()(Component)

Now, all intl prop is replaced with i18n and functions are renamed as well:

react-intl @lingui/react
intl.formatMessage i18n.t
intl.formatPlural i18n.plural
intl.formatNumber i18n.number
intl.formatDate i18n.date
intl.formatTime i18n.date

This is the area with the biggest documentation dept :/

Missing pieces

There's no FormatDateRelative at the moment. FormatHTMLMessage isn't required, because it's handled by Trans component itself.

What components do you use in your project?

@tinynumbers
Copy link
Author

The only things we're importing from react-intl are:

  • addLocaleData (only a couple of times)
  • defineMessages
  • FormattedMessage
  • FormattedRelative (only a couple of times)
  • injectIntl
  • IntlProvider (only once)
  • messageDescriptorPropTypes

What you've written is a big help - thank you!

@tricoder42
Copy link
Contributor

tricoder42 commented Jan 30, 2018

Ah, I forgot. IntlProvider is the same as I18nProvider (probably different props).

Instead of defineMessages, jsLingui has i18nMark:

const languages = {
  'english': i18nMark('English'),
  'french': i18nMark('French')
}

// now you can feed `Trans` id prop with marked string.
<Trans id={languages['english']} /> 

Under the hood, i18nMark is used just for extraction, so the marked strings appear in message catalog. jsLingui doesn't have "runtime" catalog, because everything is precompiled.

I'll take a look at addLocaleData, but you must define & translate FormattedMessage by yourself (EDIT: I mean, you need to create custom Component which implements @lingui/react and produces relative time strings)

@tinynumbers
Copy link
Author

Follow-up question: have you ever seen projects that use react-intl and js-lingui in parallel - both within the same application - with some UI components doing their translations via react-intl and others (again, within the same application) translating via js-lingui?

@tricoder42
Copy link
Contributor

No, I haven't heard about any. Theoretically this should be possible, because IntlProvider and I18nProvider use different props in context. However, it also means you need to load two versions of message catalog, because jsLingui uses compiled version ({ [key: string]: Function | string }), while react-intl compiles messages on-the-fly.

@tume
Copy link

tume commented Jan 31, 2018

I recently migrated small codebase from react-intl to js-lingui (by hand) and only hiccup was with DateFormat component.

It seems that react-intl's FormattedDate was internally calling new Date() so you could pass in just strings instead of just Date objects in js-lingui.

@tricoder42
Copy link
Contributor

@tume That's actually good suggestion for improvement. I've just created an issue for it #155

@tinynumbers
Copy link
Author

Is it possible to use i18n.t as a direct replacement for react-intl's intl.formatMessage? Since i18n.t expects a template string, it seems unusable if your message is stored in a variable? Let me know if I'm misunderstanding the capabilities of i18n.t...

For now, in my proof-of-concept spike on this migration, I've been using i18n._ instead of .t. Example:

A bunch of messages:

const messages = {
  foo: i18nMark('Foo'),
  bar: i18nMark('Bar Baz {extra} Stuff'),
};

The translation:

const subsituteMe = 'some text';
// 1.x:
i18n._(messages.bar, { values: { extra: substituteMe } });
// or, 2.x:
i18n._(messages.bar, { extra: substituteMe });

@tricoder42
Copy link
Contributor

Yes, you're correct. If you use i18nMark, you need to feed the variable to i18n._. i18n.t could be used directly like:

const extra = 'some text'
i18n.t`Bar Baz {extra} Stuff`

@azangru
Copy link

azangru commented Feb 21, 2018

@tricoder42 You mentioned above that react-intl's intl.formatDate corresponds to i18n.date in jsLingui, but I am having trouble accessing the date function on the injected i18n object. When I inspect the i18n object, I see that it has plural, select, selectOrdinal and t methods, but no date method. I have @lingui/react package version 2.0.2. Is there a convenient way to access the date function apart from using the DateFormat react element?

@tricoder42
Copy link
Contributor

I'm closing this issue as it becomes a bit obsolete. If anyone recentl migrated from react-into to LinguiJS, feel free to send a PR with migration guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants