Navigation Menu

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

Upgrade guide from v1.x to v2.x? #193

Closed
liuyanghejerry opened this issue Oct 26, 2015 · 11 comments
Closed

Upgrade guide from v1.x to v2.x? #193

liuyanghejerry opened this issue Oct 26, 2015 · 11 comments

Comments

@liuyanghejerry
Copy link

Hi all,

It seems that react-intl v2.x is very different from v1.x. I've seen some of the breaking changes when browsing the release logs, but is there a more complete upgrade guide out there?

@ericf
Copy link
Collaborator

ericf commented Oct 27, 2015

Not yet, but this will be something that will be created along with the updated docs. Is there something in particular that you're curious about?

@liuyanghejerry
Copy link
Author

My projects now have some language files, written as JSON-like Object, such as:

var zh_CN = {
  "locales": "zh",
  "messages": {
    "GENERAL": {
      "UNKNOWN_DEVICE": "未知设备",
      "MAP_LOAD_ERROR": "地图加载失败",
    },
}

And one of the files is loaded, which depends on current locale, and be set via props:

<MainFrame {...intlData} />

And finally, these translations will be used like:

var someText = 
      <FormattedMessage
        message={this.getIntlMessage('GENERAL.UNKNOWN_DEVICE')} />;

I believe this is the "standard" usage in react-intl v1.x. Of course, mixin is used.

Now I'm little confused about the new v2.x way, which looks like I need to re-write(correct me if im wrong) every language file in a "defineMessages" way:

const messages = defineMessages({
    label: {
        id: 'send_button.label',
        defaultMessage: 'Send',
    },
    tooltip: {
        id: 'send_button.tooltip',
        defaultMessage: 'Send the message'
    }
});

So is there any guide on this change? Do I have to re-write ALL translations? Or how to use those old translations with the brand new API? I think I need little help here.

@ericf
Copy link
Collaborator

ericf commented Oct 28, 2015

@liuyanghejerry One change is that messages is expected to be a flat object. Before feeding the translated messages into <IntlProvider> you'll want to flatten the object. You could do something like:

function flattenMessages(nestedMessages, prefix = '') {
    return Object.keys(nestedMessages).reduce((messages, key) => {
        let value       = nestedMessages[key];
        let prefixedKey = prefix ? `${prefix}.${key}` : key;

        if (typeof value === 'string') {
            messages[prefixedKey] = value;
        } else {
            Object.assign(messages, flattenMessages(value, prefixedKey));
        }

        return messages;
    }, {});
}

let messages = flattenMessages(nestedMessages);

A new feature of v2 is being able to define your default messages inside your source code where they are used and use The Babel plugin: babel-plugin-react-intl to extract these default messages for translation. But this is optional; you don't have to use this, and you can migrate to it overtime. You can keep your default messages being defined in a separate, single file, if you wish. That'll still work.

var someText = 
      <FormattedMessage
        message={this.getIntlMessage('GENERAL.UNKNOWN_DEVICE')} />;

The above example you provided in not correct. First, React components return elements, not a text string; second, getIntlMessage() is gone, and third there is no message prop. Instead it would look like this (assuming you've flattened the messages:

<p>
    <FormattedMessage id="GENERAL.UNKNOWN_DEVICE" />
</p>

If you wanted to move to the defining your default messages right where you're formatting them and use the Babel plugin to extract them, then you could do this (assuming English as the source/default language):

<p>
    <FormattedMessage
        id="GENERAL.UNKNOWN_DEVICE" 
        defaultMessage="Unknown Device"
    />
</p>

And finally, if you wanted to format the message to a string and use it in a non-element way, like a title or aria attribute value, then you could do this:

const messages = defineMessages({
    unknown_device: {
        id: 'GENERAL.UNKNOWN_DEVICE',
        defaultMessage: 'Unknown Device'
    }
});

const SomeComponent = injectIntl(({intl}) => {
    const message = intl.formatMessage(messages.unknown_device);
    return <div title={message}>{message}</div>;
});

export default SomeComponent;

In this example I used a stateless component and passed it into injectIntl() which will provide the intl prop which has the imperative API on it which includes the formatMessage() function. This is the same API that's used internally in all of the React Intl <Formatted*> components. These format*() functions will always return a string instead of a React element.


The major upgrade steps:

  1. Use a flat messages object.
  2. Wrap the app's root component with <IntlProvider>.
  3. Use <FormattedMessage id="some_message_id" />
  4. Use injectIntl() HOC function to access imperative API via intl prop.

@liuyanghejerry
Copy link
Author

Thanks! Now it seems much clearer and easier when upgrade.

@jopdeklein
Copy link

I know I am a little late to the party, but I am wondering what is the main driver behind flattening the objects is? My main concern is that it will increase our payload as there will be a lot of duplicated strings being sent over the line in the messages data structure. Would it be possible to keep the string identifiers with the dot notation like they are in the example above, but still keep the underlying messages structure nested?

@ericf
Copy link
Collaborator

ericf commented Jan 21, 2016

Would it be possible to keep the string identifiers with the dot notation like they are in the example above, but still keep the underlying messages structure nested?

Yes. The strings are no longer interpreted so you can use whatever schema you want.

@jopdeklein
Copy link

Thanks Eric, I will give it a go.

@jopdeklein
Copy link

@ericf I think I didn't communicate my question properly, from all documentation I read I get the impression only the fully flattened message structure is supported: https://github.com/yahoo/react-intl/blob/bcef0973868e7e284ab1f90deaa16c2933cf6d70/UPGRADE.md#flatten-messages-object, is this correct?

@liuyanghejerry
Copy link
Author

@jopdeklein You can still use non-flatten structures to store your messages, since you can transform any non-flattened structures into a flattened ones. The idea is that you can store and transfer nested structures, and flatten them when you feed them to react-intl.

@jopdeklein
Copy link

@liuyanghejerry @ericf OK that makes sense - I'll balance the performance between sending more data vs the CPU cycles used for flattening the structure during run-time. Thanks for your suggestions, appreciated.

@ericf
Copy link
Collaborator

ericf commented Jan 22, 2016

the CPU cycles used for flattening the structure during run-time.

Likely to be imperceptible.

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

No branches or pull requests

3 participants