alibaba/react-intl-universal

Name: react-intl-universal

Owner: Alibaba

Description: Internationalize React apps. Not only for React.Component but also for Vanilla JS.

Created: 2017-06-14 06:39:26.0

Updated: 2018-05-24 12:26:04.0

Pushed: 2018-05-13 13:33:53.0

Homepage:

Size: 138

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

react-intl-universal

react-intl-universal is a React internationalization package developed by Alibaba Group.

npm npm npm

Features
Live Demo

React Intl Universal Demo

Why Another Internationalization Solution for React?

In case of internationalizing React apps, react-intl is one of most popular package in industry. react-intl decorate your React.Component with wrapped component which is injected internationalized message dynamically so that the locale data is able to be loaded dynamically without reloading page. The following is the example code using react-intl.

rt { injectIntl } from 'react-intl';
s MyComponent extends Component {
nder() {
const intl = this.props;
const title = intl.formatMessage({ id: 'title' });
return (<div>{title}</div>);


rt default injectIntl(MyComponent);

However, this approach introduces two major issues.

Firstly, Internationalizing can be applied only in view layer such as React.Component. For Vanilla JS file, there's no way to internationalize it. For example, the following snippet is general form validator used by many React.Component in our apps. We definitely will not have such code separated in different React.Component in order to internationalize the warning message. Sadly, react-intl can't be used in Vanilla JS.

rt default const rules = {
Space(value) {
if (value.includes(' ')) {
  return 'Space is not allowed.';
}


Secondly, since your React.Component is wrapped by another class, the behavior is not as expected in many way. For example, to get the instance of React.Component, you can't use the normal way like:

s App {
nder() {
<MyComponent ref="my"/>

tMyInstance() {
console.log('getMyInstance', this.refs.my);


Instead, you need to use the method `getWrappedInstance()` to get that.

s MyComponent {...}
rt default injectIntl(MyComponent, {withRef: true});

s App {
nder() {
<MyComponent ref="my"/>

tMyInstance() {
console.log('getMyInstance', this.refs.my.getWrappedInstance());


Furthermore, your React.Component's properties are not inherited in subclass since component is injected by react-intl.

Due to the problem above, we create react-intl-universal to internationalize React app using simple but powerful API.

Get Started
Install
install react-intl-universal --save
Initialize

In the following example, we initialize intl with app locale data (locales) and determine which locale is used dynamically (currentLocale). Then use intl.get(...) to get the internationalized message. That's all. Pretty simple!

Note that you are not necessary to load all locale data, just load the current locale data on demand. Please refer the example for more detail.

rt intl from 'react-intl-universal';

ocale data
t locales = {
n-US": require('./locales/en-US.js'),
h-CN": require('./locales/zh-CN.js'),


s App extends Component {

ate = {initDone: false}

mponentDidMount() {
this.loadLocales();


adLocales() {
// init method will load CLDR locale data according to currentLocale
// react-intl-universal is singleton, so you should init it only once in your app
intl.init({
  currentLocale: 'en-US', // TODO: determine locale here
  locales,
})
.then(() => {
  // After loading CLDR locale data, start to render
  this.setState({initDone: true});
});


nder() {
return (
  this.state.initDone &&
  <div>
    {intl.get('SIMPLE')}
  </div>
);



HTML Message

As shown in above example, the get method returns string message. For HTML message, use getHTML instead. For example,

Locale data:

IP": "This is <span style='color:red'>HTML</span>" }

JS code:

.getHTML('TIP'); // {React.Element}
Default Message

When the specific key does't exist in current locale, you may want to make it return a default message. Use defaultMessage method after get method. For example,

JS code:

.get('not-exist-key').defaultMessage('default message') // "default message"

Or using d for short:

.get('not-exist-key').d('default message') // "default message"

And getHTML also supports default message.

.getHTML('not-exist-key').d(<div>hello</div>) // React.Element with "<div>hello</div>"
Message With Variables

If the message contains variables the {variable_name} is substituted directly into the string. In the example below, there are two variables {name} and {where}, the second argument representing the variables in get method are substituted into the string.

Locale data:

ELLO": "Hello, {name}. Welcome to {where}!" }

JS code:

.get('HELLO', {name:'Tony', where:'Alibaba'}) // "Hello, Tony. Welcome to Alibaba!"
Plural Form and Number Thousands Separators

Locale data:

HOTO": "You have {num, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}" }

JS code:

.get('PHOTO', {num:0}); // "You have no photos."
.get('PHOTO', {num:1}); // "You have one photo."
.get('PHOTO', {num:1000000}); // "You have 1,000,000 photos."

Plural label supports standard ICU Message syntax.

Number thousands separators also varies according to the user's locale. According to this document, United States use a period to indicate the decimal place. Many other countries use a comma instead.

Display Currency

Locale data:

ALE_PRICE": "The price is {price, number, USD}" }

JS code:

.get('SALE_PRICE', {price:123456.78}); // The price is $123,456.78

As mentioned, the locale data is in ICU Message format.

The syntax is {name, type, format}. Here is description:

if type is number and format is omitted, the result is formatted number with thousands separators. If format is one of currency code, it will show in corresponding currency format.

Display Dates

Locale data:


ALE_START": "Sale begins {start, date}",
ALE_END": "Sale ends {end, date, long}"

JS code:

.get('SALE_START', {start:new Date()}); // Sale begins 4/19/2017
.get('SALE_END', {end:new Date()}); // Sale ends April 19, 2017

If type is date, format has the following values:

Display Times

Locale data:


OUPON": "Coupon expires at {expires, time, medium}"

JS code:

.get('COUPON', {expires:new Date()}); // Coupon expires at 6:45:44 PM

if type is time, format has the following values:

Common Locale Data

For browser rendering, the common locale data such as date, currency, and number format are automatically loaded from CDN on demand.

For server-side rendering, they should be added in your application as shown in the example.

or Node.js
rt IntlPolyfill from "intl";
al.Intl = IntlPolyfill;
ire('intl/locale-data/jsonp/en.js');
ire('intl/locale-data/jsonp/zh.js');
ire('intl/locale-data/jsonp/fr.js');
ire('intl/locale-data/jsonp/ja.js');
Helper

react-intl-universal provides a utility helping developer determine the user's current locale. As the running examples, when user select a new locale, it redirect user new location like http://localhost:3000?lang=en-US. Then, we can use intl.determineLocale to get the locale from URL. It can also support determine user's locale via cookie and browser default language. Refer to the APIs section for more detail.

APIs Definition
*
 Initialize properties and load CLDR locale data according to currentLocale
 @param {Object} options
 @param {string} options.currentLocale Current locale such as 'en-US'
 @param {string} options.locales App locale data like {"en-US":{"key1":"value1"},"zh-CN":{"key1":"?1"}}
 @param {Object} options.warningHandler Ability to accumulate missing messages using third party services like Sentry. See https://github.com/alibaba/react-intl-universal/releases/tag/1.11.1
 @returns {Promise}
/
it(options)


*
 Load more locales after init
 @param {Object} locales App locale data 
/
ad(locales)


*
 Get the formatted message by key
 @param {string} key The string representing key in locale data file
 @param {Object} variables Variables in message
 @returns {string} message
/
t(key, variables)

*
 Get the formatted html message by key.
 @param {string} key The string representing key in locale data file
 @param {Object} variables Variables in message
 @returns {React.Element} message

tHTML(key, options)

*
 Helper: determine user's locale via URL, cookie, and browser's language.
 You may not this API, if you have other rules to determine user's locale.
 @param {string} options.urlLocaleKey URL's query Key to determine locale. Example: if URL=http://localhost?lang=en-US, then set it 'lang'
 @param {string} options.cookieLocaleKey Cookie's Key to determine locale. Example: if cookie=lang:en-US, then set it 'lang'
 @returns {string} determined locale such as 'en-US'
/
termineLocale(options)

*
 Get the inital options 
 @returns {Object} options includes currentLocale and locales
/
tInitOptions()
Compatibility with react-intl

As mentioned in the issue Mirror react-intl API, to make people switch their existing React projects from react-intl to react-intl-universal. We provide two compatible APIs as following.

*
 As same as get(...) API
 @param {Object} options 
 @param {string} options.id 
 @param {string} options.defaultMessage
 @param {Object} variables Variables in message
 @returns {string} message

rmatMessage(options, variables)
*
 As same as getHTML(...) API
 @param {Object} options 
 @param {string} options.id 
 @param {React.Element} options.defaultMessage
 @param {Object} variables Variables in message
 @returns {React.Element} message

rmatHTMLMessage(options, variables)

For example, the formatMessage API

t name = 'Tony';
.formatMessage({ id:'hello', defaultMessage: `Hello, ${name}`}, {name});

is equivalent to get API

t name = 'Tony';
.get('hello', {name}).d(`Hello, ${name}`);

And the formatHTMLMessage API

t name = 'Tony';
.formatHTMLMessage({ id:'hello', defaultMessage: <div>Hello</div>}, {name});

is equivalent to getHTML API

t name = 'Tony';
.getHTML('hello', {name}).d(<div>Hello</div>);
Browser Compatibility

Before using react-intl-universal, you need to include scripts below in HTML to support older browser.

[if lt IE 9]>
ipt src="//f.alicdn.com/es5-shim/4.5.7/es5-shim.min.js"></script>
ndif]-->
ipt>
ypeof Promise!=="function"){document.write('<script src="//f.alicdn.com/es6-shim/0.35.1/??es6-shim.min.js,es6-sham.min.js"><\/script>')}
ript>
Running Examples Locally
clone git@github.com:alibaba/react-intl-universal.git

For browser rendering,

eact-intl-universal/examples/browser-example
install
start

For server-side rendering,

eact-intl-universal/examples/node-js-example
install
start
Ask Question

Join the chat at https://gitter.im/react-intl-universal/Lobby

License

This software is free to use under the BSD license.

Work at Alibaba ????

Join us as a frontend engineer in Alibaba group!


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.