react-intl 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +20 -0
- package/dist/react-intl.js +1017 -1475
- package/dist/react-intl.js.map +1 -1
- package/dist/react-intl.min.js +1 -2
- package/dist/react-intl.min.js.map +1 -1
- package/lib/index.es.js +983 -1428
- package/lib/index.js +990 -1435
- package/locale-data/af.js +1 -1
- package/locale-data/agq.js +1 -1
- package/locale-data/ak.js +1 -1
- package/locale-data/am.js +1 -1
- package/locale-data/ar.js +1 -1
- package/locale-data/ars.js +1 -0
- package/locale-data/as.js +1 -1
- package/locale-data/asa.js +1 -1
- package/locale-data/ast.js +1 -1
- package/locale-data/az.js +1 -1
- package/locale-data/bas.js +1 -1
- package/locale-data/be.js +1 -1
- package/locale-data/bem.js +1 -1
- package/locale-data/bez.js +1 -1
- package/locale-data/bg.js +1 -1
- package/locale-data/bh.js +1 -1
- package/locale-data/bm.js +1 -1
- package/locale-data/bn.js +1 -1
- package/locale-data/bo.js +1 -1
- package/locale-data/br.js +1 -1
- package/locale-data/brx.js +1 -1
- package/locale-data/bs.js +1 -1
- package/locale-data/ca.js +1 -1
- package/locale-data/ce.js +1 -1
- package/locale-data/cgg.js +1 -1
- package/locale-data/chr.js +1 -1
- package/locale-data/ckb.js +1 -1
- package/locale-data/cs.js +1 -1
- package/locale-data/cu.js +1 -1
- package/locale-data/cy.js +1 -1
- package/locale-data/da.js +1 -1
- package/locale-data/dav.js +1 -1
- package/locale-data/de.js +1 -1
- package/locale-data/dje.js +1 -1
- package/locale-data/dsb.js +1 -1
- package/locale-data/dua.js +1 -1
- package/locale-data/dv.js +1 -1
- package/locale-data/dyo.js +1 -1
- package/locale-data/dz.js +1 -1
- package/locale-data/ebu.js +1 -1
- package/locale-data/ee.js +1 -1
- package/locale-data/el.js +1 -1
- package/locale-data/en.js +1 -1
- package/locale-data/eo.js +1 -1
- package/locale-data/es.js +1 -1
- package/locale-data/et.js +1 -1
- package/locale-data/eu.js +1 -1
- package/locale-data/ewo.js +1 -1
- package/locale-data/fa.js +1 -1
- package/locale-data/ff.js +1 -1
- package/locale-data/fi.js +1 -1
- package/locale-data/fil.js +1 -1
- package/locale-data/fo.js +1 -1
- package/locale-data/fr.js +1 -1
- package/locale-data/fur.js +1 -1
- package/locale-data/fy.js +1 -1
- package/locale-data/ga.js +1 -1
- package/locale-data/gd.js +1 -1
- package/locale-data/gl.js +1 -1
- package/locale-data/gsw.js +1 -1
- package/locale-data/gu.js +1 -1
- package/locale-data/guw.js +1 -1
- package/locale-data/guz.js +1 -1
- package/locale-data/gv.js +1 -1
- package/locale-data/ha.js +1 -1
- package/locale-data/haw.js +1 -1
- package/locale-data/he.js +1 -1
- package/locale-data/hi.js +1 -1
- package/locale-data/hr.js +1 -1
- package/locale-data/hsb.js +1 -1
- package/locale-data/hu.js +1 -1
- package/locale-data/hy.js +1 -1
- package/locale-data/id.js +1 -1
- package/locale-data/ig.js +1 -1
- package/locale-data/ii.js +1 -1
- package/locale-data/in.js +1 -1
- package/locale-data/index.js +1 -10
- package/locale-data/is.js +1 -1
- package/locale-data/it.js +1 -1
- package/locale-data/iu.js +1 -1
- package/locale-data/iw.js +1 -1
- package/locale-data/ja.js +1 -1
- package/locale-data/jbo.js +1 -1
- package/locale-data/jgo.js +1 -1
- package/locale-data/ji.js +1 -1
- package/locale-data/jmc.js +1 -1
- package/locale-data/jv.js +1 -1
- package/locale-data/jw.js +1 -1
- package/locale-data/ka.js +1 -1
- package/locale-data/kab.js +1 -1
- package/locale-data/kaj.js +1 -1
- package/locale-data/kam.js +1 -1
- package/locale-data/kcg.js +1 -1
- package/locale-data/kde.js +1 -1
- package/locale-data/kea.js +1 -1
- package/locale-data/khq.js +1 -1
- package/locale-data/ki.js +1 -1
- package/locale-data/kk.js +1 -1
- package/locale-data/kkj.js +1 -1
- package/locale-data/kl.js +1 -1
- package/locale-data/kln.js +1 -1
- package/locale-data/km.js +1 -1
- package/locale-data/kn.js +1 -1
- package/locale-data/ko.js +1 -1
- package/locale-data/kok.js +1 -1
- package/locale-data/ks.js +1 -1
- package/locale-data/ksb.js +1 -1
- package/locale-data/ksf.js +1 -1
- package/locale-data/ksh.js +1 -1
- package/locale-data/ku.js +1 -1
- package/locale-data/kw.js +1 -1
- package/locale-data/ky.js +1 -1
- package/locale-data/lag.js +1 -1
- package/locale-data/lb.js +1 -1
- package/locale-data/lg.js +1 -1
- package/locale-data/lkt.js +1 -1
- package/locale-data/ln.js +1 -1
- package/locale-data/lo.js +1 -1
- package/locale-data/lrc.js +1 -1
- package/locale-data/lt.js +1 -1
- package/locale-data/lu.js +1 -1
- package/locale-data/luo.js +1 -1
- package/locale-data/luy.js +1 -1
- package/locale-data/lv.js +1 -1
- package/locale-data/mas.js +1 -1
- package/locale-data/mer.js +1 -1
- package/locale-data/mfe.js +1 -1
- package/locale-data/mg.js +1 -1
- package/locale-data/mgh.js +1 -1
- package/locale-data/mgo.js +1 -1
- package/locale-data/mk.js +1 -1
- package/locale-data/ml.js +1 -1
- package/locale-data/mn.js +1 -1
- package/locale-data/mo.js +1 -1
- package/locale-data/mr.js +1 -1
- package/locale-data/ms.js +1 -1
- package/locale-data/mt.js +1 -1
- package/locale-data/mua.js +1 -1
- package/locale-data/my.js +1 -1
- package/locale-data/mzn.js +1 -1
- package/locale-data/nah.js +1 -1
- package/locale-data/naq.js +1 -1
- package/locale-data/nb.js +1 -1
- package/locale-data/nd.js +1 -1
- package/locale-data/nds.js +1 -0
- package/locale-data/ne.js +1 -1
- package/locale-data/nl.js +1 -1
- package/locale-data/nmg.js +1 -1
- package/locale-data/nn.js +1 -1
- package/locale-data/nnh.js +1 -1
- package/locale-data/no.js +1 -1
- package/locale-data/nqo.js +1 -1
- package/locale-data/nr.js +1 -1
- package/locale-data/nso.js +1 -1
- package/locale-data/nus.js +1 -1
- package/locale-data/ny.js +1 -1
- package/locale-data/nyn.js +1 -1
- package/locale-data/om.js +1 -1
- package/locale-data/or.js +1 -1
- package/locale-data/os.js +1 -1
- package/locale-data/pa.js +1 -1
- package/locale-data/pap.js +1 -1
- package/locale-data/pl.js +1 -1
- package/locale-data/prg.js +1 -1
- package/locale-data/ps.js +1 -1
- package/locale-data/pt.js +1 -1
- package/locale-data/qu.js +1 -1
- package/locale-data/rm.js +1 -1
- package/locale-data/rn.js +1 -1
- package/locale-data/ro.js +1 -1
- package/locale-data/rof.js +1 -1
- package/locale-data/ru.js +1 -1
- package/locale-data/rw.js +1 -1
- package/locale-data/rwk.js +1 -1
- package/locale-data/sah.js +1 -1
- package/locale-data/saq.js +1 -1
- package/locale-data/sbp.js +1 -1
- package/locale-data/sdh.js +1 -1
- package/locale-data/se.js +1 -1
- package/locale-data/seh.js +1 -1
- package/locale-data/ses.js +1 -1
- package/locale-data/sg.js +1 -1
- package/locale-data/sh.js +1 -1
- package/locale-data/shi.js +1 -1
- package/locale-data/si.js +1 -1
- package/locale-data/sk.js +1 -1
- package/locale-data/sl.js +1 -1
- package/locale-data/sma.js +1 -1
- package/locale-data/smi.js +1 -1
- package/locale-data/smj.js +1 -1
- package/locale-data/smn.js +1 -1
- package/locale-data/sms.js +1 -1
- package/locale-data/sn.js +1 -1
- package/locale-data/so.js +1 -1
- package/locale-data/sq.js +1 -1
- package/locale-data/sr.js +1 -1
- package/locale-data/ss.js +1 -1
- package/locale-data/ssy.js +1 -1
- package/locale-data/st.js +1 -1
- package/locale-data/sv.js +1 -1
- package/locale-data/sw.js +1 -1
- package/locale-data/syr.js +1 -1
- package/locale-data/ta.js +1 -1
- package/locale-data/te.js +1 -1
- package/locale-data/teo.js +1 -1
- package/locale-data/th.js +1 -1
- package/locale-data/ti.js +1 -1
- package/locale-data/tig.js +1 -1
- package/locale-data/tk.js +1 -1
- package/locale-data/tl.js +1 -1
- package/locale-data/tn.js +1 -1
- package/locale-data/to.js +1 -1
- package/locale-data/tr.js +1 -1
- package/locale-data/ts.js +1 -1
- package/locale-data/twq.js +1 -1
- package/locale-data/tzm.js +1 -1
- package/locale-data/ug.js +1 -1
- package/locale-data/uk.js +1 -1
- package/locale-data/ur.js +1 -1
- package/locale-data/uz.js +1 -1
- package/locale-data/vai.js +1 -1
- package/locale-data/ve.js +1 -1
- package/locale-data/vi.js +1 -1
- package/locale-data/vo.js +1 -1
- package/locale-data/vun.js +1 -1
- package/locale-data/wa.js +1 -1
- package/locale-data/wae.js +1 -1
- package/locale-data/wo.js +1 -1
- package/locale-data/xh.js +1 -1
- package/locale-data/xog.js +1 -1
- package/locale-data/yav.js +1 -1
- package/locale-data/yi.js +1 -1
- package/locale-data/yo.js +1 -1
- package/locale-data/yue.js +1 -0
- package/locale-data/zgh.js +1 -1
- package/locale-data/zh.js +1 -1
- package/locale-data/zu.js +1 -1
- package/package.json +33 -27
- package/src/components/date.js +30 -29
- package/src/components/html-message.js +63 -62
- package/src/components/message.js +117 -114
- package/src/components/number.js +30 -29
- package/src/components/plural.js +37 -36
- package/src/components/provider.js +135 -134
- package/src/components/relative.js +128 -120
- package/src/components/time.js +30 -29
- package/src/define-messages.js +3 -3
- package/src/en.js +1 -1
- package/src/format.js +209 -208
- package/src/inject.js +38 -40
- package/src/locale-data-registry.js +21 -21
- package/src/plural.js +9 -9
- package/src/types.js +58 -48
- package/src/utils.js +65 -55
- package/yarn.lock +4832 -0
|
@@ -4,84 +4,85 @@
|
|
|
4
4
|
* See the accompanying LICENSE file for terms.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, {Component
|
|
7
|
+
import React, {Component} from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
8
9
|
import {intlShape, messageDescriptorPropTypes} from '../types';
|
|
9
10
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
invariantIntlContext,
|
|
12
|
+
shallowEquals,
|
|
13
|
+
shouldIntlComponentUpdate,
|
|
13
14
|
} from '../utils';
|
|
14
15
|
|
|
15
16
|
export default class FormattedHTMLMessage extends Component {
|
|
16
|
-
|
|
17
|
+
static displayName = 'FormattedHTMLMessage';
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
static contextTypes = {
|
|
20
|
+
intl: intlShape,
|
|
21
|
+
};
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
static propTypes = {
|
|
24
|
+
...messageDescriptorPropTypes,
|
|
25
|
+
values: PropTypes.object,
|
|
26
|
+
tagName: PropTypes.string,
|
|
27
|
+
children: PropTypes.func,
|
|
28
|
+
};
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
constructor(props, context) {
|
|
34
|
-
super(props, context);
|
|
35
|
-
invariantIntlContext(context);
|
|
36
|
-
}
|
|
30
|
+
static defaultProps = {
|
|
31
|
+
values: {},
|
|
32
|
+
};
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
constructor(props, context) {
|
|
35
|
+
super(props, context);
|
|
36
|
+
invariantIntlContext(context);
|
|
37
|
+
}
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
shouldComponentUpdate(nextProps, ...next) {
|
|
40
|
+
const {values} = this.props;
|
|
41
|
+
const {values: nextValues} = nextProps;
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// equals comparison on the other props isn't affected by the `values`.
|
|
49
|
-
let nextPropsToCheck = {
|
|
50
|
-
...nextProps,
|
|
51
|
-
values,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return shouldIntlComponentUpdate(this, nextPropsToCheck, ...next);
|
|
43
|
+
if (!shallowEquals(nextValues, values)) {
|
|
44
|
+
return true;
|
|
55
45
|
}
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
// Since `values` has already been checked, we know they're not
|
|
48
|
+
// different, so the current `values` are carried over so the shallow
|
|
49
|
+
// equals comparison on the other props isn't affected by the `values`.
|
|
50
|
+
let nextPropsToCheck = {
|
|
51
|
+
...nextProps,
|
|
52
|
+
values,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return shouldIntlComponentUpdate(this, nextPropsToCheck, ...next);
|
|
56
|
+
}
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
description,
|
|
63
|
-
defaultMessage,
|
|
64
|
-
values: rawValues,
|
|
65
|
-
tagName: Component = Text,
|
|
66
|
-
children,
|
|
67
|
-
} = this.props;
|
|
58
|
+
render() {
|
|
59
|
+
const {formatHTMLMessage, textComponent: Text} = this.context.intl;
|
|
68
60
|
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const {
|
|
62
|
+
id,
|
|
63
|
+
description,
|
|
64
|
+
defaultMessage,
|
|
65
|
+
values: rawValues,
|
|
66
|
+
tagName: Component = Text,
|
|
67
|
+
children,
|
|
68
|
+
} = this.props;
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
70
|
+
let descriptor = {id, description, defaultMessage};
|
|
71
|
+
let formattedHTMLMessage = formatHTMLMessage(descriptor, rawValues);
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// To be safe, all string prop values were escaped when formatting the
|
|
79
|
-
// message. It is assumed that the message is not UGC, and came from the
|
|
80
|
-
// developer making it more like a template.
|
|
81
|
-
//
|
|
82
|
-
// Note: There's a perf impact of using this component since there's no
|
|
83
|
-
// way for React to do its virtual DOM diffing.
|
|
84
|
-
const html = {__html: formattedHTMLMessage};
|
|
85
|
-
return <Component dangerouslySetInnerHTML={html}/>;
|
|
73
|
+
if (typeof children === 'function') {
|
|
74
|
+
return children(formattedHTMLMessage);
|
|
86
75
|
}
|
|
76
|
+
|
|
77
|
+
// Since the message presumably has HTML in it, we need to set
|
|
78
|
+
// `innerHTML` in order for it to be rendered and not escaped by React.
|
|
79
|
+
// To be safe, all string prop values were escaped when formatting the
|
|
80
|
+
// message. It is assumed that the message is not UGC, and came from the
|
|
81
|
+
// developer making it more like a template.
|
|
82
|
+
//
|
|
83
|
+
// Note: There's a perf impact of using this component since there's no
|
|
84
|
+
// way for React to do its virtual DOM diffing.
|
|
85
|
+
const html = {__html: formattedHTMLMessage};
|
|
86
|
+
return <Component dangerouslySetInnerHTML={html} />;
|
|
87
|
+
}
|
|
87
88
|
}
|
|
@@ -4,131 +4,134 @@
|
|
|
4
4
|
* See the accompanying LICENSE file for terms.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import {Component, createElement, isValidElement} from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
8
9
|
import {intlShape, messageDescriptorPropTypes} from '../types';
|
|
9
10
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
invariantIntlContext,
|
|
12
|
+
shallowEquals,
|
|
13
|
+
shouldIntlComponentUpdate,
|
|
13
14
|
} from '../utils';
|
|
14
15
|
|
|
15
16
|
export default class FormattedMessage extends Component {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
static displayName = 'FormattedMessage';
|
|
18
|
+
|
|
19
|
+
static contextTypes = {
|
|
20
|
+
intl: intlShape,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
static propTypes = {
|
|
24
|
+
...messageDescriptorPropTypes,
|
|
25
|
+
values: PropTypes.object,
|
|
26
|
+
tagName: PropTypes.string,
|
|
27
|
+
children: PropTypes.func,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static defaultProps = {
|
|
31
|
+
values: {},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
constructor(props, context) {
|
|
35
|
+
super(props, context);
|
|
36
|
+
invariantIntlContext(context);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
shouldComponentUpdate(nextProps, ...next) {
|
|
40
|
+
const {values} = this.props;
|
|
41
|
+
const {values: nextValues} = nextProps;
|
|
42
|
+
|
|
43
|
+
if (!shallowEquals(nextValues, values)) {
|
|
44
|
+
return true;
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Since `values` has already been checked, we know they're not
|
|
47
|
-
// different, so the current `values` are carried over so the shallow
|
|
48
|
-
// equals comparison on the other props isn't affected by the `values`.
|
|
49
|
-
let nextPropsToCheck = {
|
|
50
|
-
...nextProps,
|
|
51
|
-
values,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return shouldIntlComponentUpdate(this, nextPropsToCheck, ...next);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
render() {
|
|
58
|
-
const {formatMessage, textComponent: Text} = this.context.intl;
|
|
59
|
-
|
|
60
|
-
const {
|
|
61
|
-
id,
|
|
62
|
-
description,
|
|
63
|
-
defaultMessage,
|
|
64
|
-
values,
|
|
65
|
-
tagName: Component = Text,
|
|
66
|
-
children,
|
|
67
|
-
} = this.props;
|
|
68
|
-
|
|
69
|
-
let tokenDelimiter;
|
|
70
|
-
let tokenizedValues;
|
|
71
|
-
let elements;
|
|
72
|
-
|
|
73
|
-
let hasValues = values && Object.keys(values).length > 0;
|
|
74
|
-
if (hasValues) {
|
|
75
|
-
// Creates a token with a random UID that should not be guessable or
|
|
76
|
-
// conflict with other parts of the `message` string.
|
|
77
|
-
let uid = Math.floor(Math.random() * 0x10000000000).toString(16);
|
|
78
|
-
|
|
79
|
-
let generateToken = (() => {
|
|
80
|
-
let counter = 0;
|
|
81
|
-
return () => `ELEMENT-${uid}-${counter += 1}`;
|
|
82
|
-
})();
|
|
83
|
-
|
|
84
|
-
// Splitting with a delimiter to support IE8. When using a regex
|
|
85
|
-
// with a capture group IE8 does not include the capture group in
|
|
86
|
-
// the resulting array.
|
|
87
|
-
tokenDelimiter = `@__${uid}__@`;
|
|
88
|
-
tokenizedValues = {};
|
|
89
|
-
elements = {};
|
|
90
|
-
|
|
91
|
-
// Iterates over the `props` to keep track of any React Element
|
|
92
|
-
// values so they can be represented by the `token` as a placeholder
|
|
93
|
-
// when the `message` is formatted. This allows the formatted
|
|
94
|
-
// message to then be broken-up into parts with references to the
|
|
95
|
-
// React Elements inserted back in.
|
|
96
|
-
Object.keys(values).forEach((name) => {
|
|
97
|
-
let value = values[name];
|
|
98
|
-
|
|
99
|
-
if (isValidElement(value)) {
|
|
100
|
-
let token = generateToken();
|
|
101
|
-
tokenizedValues[name] = tokenDelimiter + token + tokenDelimiter;
|
|
102
|
-
elements[token] = value;
|
|
103
|
-
} else {
|
|
104
|
-
tokenizedValues[name] = value;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
47
|
+
// Since `values` has already been checked, we know they're not
|
|
48
|
+
// different, so the current `values` are carried over so the shallow
|
|
49
|
+
// equals comparison on the other props isn't affected by the `values`.
|
|
50
|
+
let nextPropsToCheck = {
|
|
51
|
+
...nextProps,
|
|
52
|
+
values,
|
|
53
|
+
};
|
|
108
54
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
55
|
+
return shouldIntlComponentUpdate(this, nextPropsToCheck, ...next);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
render() {
|
|
59
|
+
const {formatMessage, textComponent: Text} = this.context.intl;
|
|
60
|
+
|
|
61
|
+
const {
|
|
62
|
+
id,
|
|
63
|
+
description,
|
|
64
|
+
defaultMessage,
|
|
65
|
+
values,
|
|
66
|
+
tagName: Component = Text,
|
|
67
|
+
children,
|
|
68
|
+
} = this.props;
|
|
69
|
+
|
|
70
|
+
let tokenDelimiter;
|
|
71
|
+
let tokenizedValues;
|
|
72
|
+
let elements;
|
|
73
|
+
|
|
74
|
+
let hasValues = values && Object.keys(values).length > 0;
|
|
75
|
+
if (hasValues) {
|
|
76
|
+
// Creates a token with a random UID that should not be guessable or
|
|
77
|
+
// conflict with other parts of the `message` string.
|
|
78
|
+
let uid = Math.floor(Math.random() * 0x10000000000).toString(16);
|
|
79
|
+
|
|
80
|
+
let generateToken = (() => {
|
|
81
|
+
let counter = 0;
|
|
82
|
+
return () => `ELEMENT-${uid}-${(counter += 1)}`;
|
|
83
|
+
})();
|
|
84
|
+
|
|
85
|
+
// Splitting with a delimiter to support IE8. When using a regex
|
|
86
|
+
// with a capture group IE8 does not include the capture group in
|
|
87
|
+
// the resulting array.
|
|
88
|
+
tokenDelimiter = `@__${uid}__@`;
|
|
89
|
+
tokenizedValues = {};
|
|
90
|
+
elements = {};
|
|
91
|
+
|
|
92
|
+
// Iterates over the `props` to keep track of any React Element
|
|
93
|
+
// values so they can be represented by the `token` as a placeholder
|
|
94
|
+
// when the `message` is formatted. This allows the formatted
|
|
95
|
+
// message to then be broken-up into parts with references to the
|
|
96
|
+
// React Elements inserted back in.
|
|
97
|
+
Object.keys(values).forEach(name => {
|
|
98
|
+
let value = values[name];
|
|
99
|
+
|
|
100
|
+
if (isValidElement(value)) {
|
|
101
|
+
let token = generateToken();
|
|
102
|
+
tokenizedValues[name] = tokenDelimiter + token + tokenDelimiter;
|
|
103
|
+
elements[token] = value;
|
|
124
104
|
} else {
|
|
125
|
-
|
|
105
|
+
tokenizedValues[name] = value;
|
|
126
106
|
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
127
109
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
110
|
+
let descriptor = {id, description, defaultMessage};
|
|
111
|
+
let formattedMessage = formatMessage(descriptor, tokenizedValues || values);
|
|
112
|
+
|
|
113
|
+
let nodes;
|
|
114
|
+
|
|
115
|
+
let hasElements = elements && Object.keys(elements).length > 0;
|
|
116
|
+
if (hasElements) {
|
|
117
|
+
// Split the message into parts so the React Element values captured
|
|
118
|
+
// above can be inserted back into the rendered message. This
|
|
119
|
+
// approach allows messages to render with React Elements while
|
|
120
|
+
// keeping React's virtual diffing working properly.
|
|
121
|
+
nodes = formattedMessage
|
|
122
|
+
.split(tokenDelimiter)
|
|
123
|
+
.filter(part => !!part)
|
|
124
|
+
.map(part => elements[part] || part);
|
|
125
|
+
} else {
|
|
126
|
+
nodes = [formattedMessage];
|
|
127
|
+
}
|
|
131
128
|
|
|
132
|
-
|
|
129
|
+
if (typeof children === 'function') {
|
|
130
|
+
return children(...nodes);
|
|
133
131
|
}
|
|
132
|
+
|
|
133
|
+
// Needs to use `createElement()` instead of JSX, otherwise React will
|
|
134
|
+
// warn about a missing `key` prop with rich-text message formatting.
|
|
135
|
+
return createElement(Component, null, ...nodes);
|
|
136
|
+
}
|
|
134
137
|
}
|
package/src/components/number.js
CHANGED
|
@@ -4,43 +4,44 @@
|
|
|
4
4
|
* See the accompanying LICENSE file for terms.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, {Component
|
|
7
|
+
import React, {Component} from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
8
9
|
import {intlShape, numberFormatPropTypes} from '../types';
|
|
9
10
|
import {invariantIntlContext, shouldIntlComponentUpdate} from '../utils';
|
|
10
11
|
|
|
11
12
|
export default class FormattedNumber extends Component {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
static contextTypes = {
|
|
15
|
-
intl: intlShape,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
static propTypes = {
|
|
19
|
-
...numberFormatPropTypes,
|
|
20
|
-
value : PropTypes.any.isRequired,
|
|
21
|
-
format : PropTypes.string,
|
|
22
|
-
children: PropTypes.func,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
constructor(props, context) {
|
|
26
|
-
super(props, context);
|
|
27
|
-
invariantIntlContext(context);
|
|
28
|
-
}
|
|
13
|
+
static displayName = 'FormattedNumber';
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
static contextTypes = {
|
|
16
|
+
intl: intlShape,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
static propTypes = {
|
|
20
|
+
...numberFormatPropTypes,
|
|
21
|
+
value: PropTypes.any.isRequired,
|
|
22
|
+
format: PropTypes.string,
|
|
23
|
+
children: PropTypes.func,
|
|
24
|
+
};
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
constructor(props, context) {
|
|
27
|
+
super(props, context);
|
|
28
|
+
invariantIntlContext(context);
|
|
29
|
+
}
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
shouldComponentUpdate(...next) {
|
|
32
|
+
return shouldIntlComponentUpdate(this, ...next);
|
|
33
|
+
}
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
render() {
|
|
36
|
+
const {formatNumber, textComponent: Text} = this.context.intl;
|
|
37
|
+
const {value, children} = this.props;
|
|
43
38
|
|
|
44
|
-
|
|
39
|
+
let formattedNumber = formatNumber(value, this.props);
|
|
40
|
+
|
|
41
|
+
if (typeof children === 'function') {
|
|
42
|
+
return children(formattedNumber);
|
|
45
43
|
}
|
|
44
|
+
|
|
45
|
+
return <Text>{formattedNumber}</Text>;
|
|
46
|
+
}
|
|
46
47
|
}
|
package/src/components/plural.js
CHANGED
|
@@ -4,55 +4,56 @@
|
|
|
4
4
|
* See the accompanying LICENSE file for terms.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, {Component
|
|
7
|
+
import React, {Component} from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
8
9
|
import {intlShape, pluralFormatPropTypes} from '../types';
|
|
9
10
|
import {invariantIntlContext, shouldIntlComponentUpdate} from '../utils';
|
|
10
11
|
|
|
11
12
|
export default class FormattedPlural extends Component {
|
|
12
|
-
|
|
13
|
+
static displayName = 'FormattedPlural';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
static contextTypes = {
|
|
16
|
+
intl: intlShape,
|
|
17
|
+
};
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
static propTypes = {
|
|
20
|
+
...pluralFormatPropTypes,
|
|
21
|
+
value: PropTypes.any.isRequired,
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
other: PropTypes.node.isRequired,
|
|
24
|
+
zero: PropTypes.node,
|
|
25
|
+
one: PropTypes.node,
|
|
26
|
+
two: PropTypes.node,
|
|
27
|
+
few: PropTypes.node,
|
|
28
|
+
many: PropTypes.node,
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
children: PropTypes.func,
|
|
31
|
+
};
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
static defaultProps = {
|
|
34
|
+
style: 'cardinal',
|
|
35
|
+
};
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
shouldComponentUpdate(...next) {
|
|
42
|
-
return shouldIntlComponentUpdate(this, ...next);
|
|
43
|
-
}
|
|
37
|
+
constructor(props, context) {
|
|
38
|
+
super(props, context);
|
|
39
|
+
invariantIntlContext(context);
|
|
40
|
+
}
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
shouldComponentUpdate(...next) {
|
|
43
|
+
return shouldIntlComponentUpdate(this, ...next);
|
|
44
|
+
}
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
render() {
|
|
47
|
+
const {formatPlural, textComponent: Text} = this.context.intl;
|
|
48
|
+
const {value, other, children} = this.props;
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
50
|
+
let pluralCategory = formatPlural(value, this.props);
|
|
51
|
+
let formattedPlural = this.props[pluralCategory] || other;
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
if (typeof children === 'function') {
|
|
54
|
+
return children(formattedPlural);
|
|
57
55
|
}
|
|
56
|
+
|
|
57
|
+
return <Text>{formattedPlural}</Text>;
|
|
58
|
+
}
|
|
58
59
|
}
|