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.
Files changed (262) hide show
  1. package/CONTRIBUTING.md +20 -0
  2. package/dist/react-intl.js +1017 -1475
  3. package/dist/react-intl.js.map +1 -1
  4. package/dist/react-intl.min.js +1 -2
  5. package/dist/react-intl.min.js.map +1 -1
  6. package/lib/index.es.js +983 -1428
  7. package/lib/index.js +990 -1435
  8. package/locale-data/af.js +1 -1
  9. package/locale-data/agq.js +1 -1
  10. package/locale-data/ak.js +1 -1
  11. package/locale-data/am.js +1 -1
  12. package/locale-data/ar.js +1 -1
  13. package/locale-data/ars.js +1 -0
  14. package/locale-data/as.js +1 -1
  15. package/locale-data/asa.js +1 -1
  16. package/locale-data/ast.js +1 -1
  17. package/locale-data/az.js +1 -1
  18. package/locale-data/bas.js +1 -1
  19. package/locale-data/be.js +1 -1
  20. package/locale-data/bem.js +1 -1
  21. package/locale-data/bez.js +1 -1
  22. package/locale-data/bg.js +1 -1
  23. package/locale-data/bh.js +1 -1
  24. package/locale-data/bm.js +1 -1
  25. package/locale-data/bn.js +1 -1
  26. package/locale-data/bo.js +1 -1
  27. package/locale-data/br.js +1 -1
  28. package/locale-data/brx.js +1 -1
  29. package/locale-data/bs.js +1 -1
  30. package/locale-data/ca.js +1 -1
  31. package/locale-data/ce.js +1 -1
  32. package/locale-data/cgg.js +1 -1
  33. package/locale-data/chr.js +1 -1
  34. package/locale-data/ckb.js +1 -1
  35. package/locale-data/cs.js +1 -1
  36. package/locale-data/cu.js +1 -1
  37. package/locale-data/cy.js +1 -1
  38. package/locale-data/da.js +1 -1
  39. package/locale-data/dav.js +1 -1
  40. package/locale-data/de.js +1 -1
  41. package/locale-data/dje.js +1 -1
  42. package/locale-data/dsb.js +1 -1
  43. package/locale-data/dua.js +1 -1
  44. package/locale-data/dv.js +1 -1
  45. package/locale-data/dyo.js +1 -1
  46. package/locale-data/dz.js +1 -1
  47. package/locale-data/ebu.js +1 -1
  48. package/locale-data/ee.js +1 -1
  49. package/locale-data/el.js +1 -1
  50. package/locale-data/en.js +1 -1
  51. package/locale-data/eo.js +1 -1
  52. package/locale-data/es.js +1 -1
  53. package/locale-data/et.js +1 -1
  54. package/locale-data/eu.js +1 -1
  55. package/locale-data/ewo.js +1 -1
  56. package/locale-data/fa.js +1 -1
  57. package/locale-data/ff.js +1 -1
  58. package/locale-data/fi.js +1 -1
  59. package/locale-data/fil.js +1 -1
  60. package/locale-data/fo.js +1 -1
  61. package/locale-data/fr.js +1 -1
  62. package/locale-data/fur.js +1 -1
  63. package/locale-data/fy.js +1 -1
  64. package/locale-data/ga.js +1 -1
  65. package/locale-data/gd.js +1 -1
  66. package/locale-data/gl.js +1 -1
  67. package/locale-data/gsw.js +1 -1
  68. package/locale-data/gu.js +1 -1
  69. package/locale-data/guw.js +1 -1
  70. package/locale-data/guz.js +1 -1
  71. package/locale-data/gv.js +1 -1
  72. package/locale-data/ha.js +1 -1
  73. package/locale-data/haw.js +1 -1
  74. package/locale-data/he.js +1 -1
  75. package/locale-data/hi.js +1 -1
  76. package/locale-data/hr.js +1 -1
  77. package/locale-data/hsb.js +1 -1
  78. package/locale-data/hu.js +1 -1
  79. package/locale-data/hy.js +1 -1
  80. package/locale-data/id.js +1 -1
  81. package/locale-data/ig.js +1 -1
  82. package/locale-data/ii.js +1 -1
  83. package/locale-data/in.js +1 -1
  84. package/locale-data/index.js +1 -10
  85. package/locale-data/is.js +1 -1
  86. package/locale-data/it.js +1 -1
  87. package/locale-data/iu.js +1 -1
  88. package/locale-data/iw.js +1 -1
  89. package/locale-data/ja.js +1 -1
  90. package/locale-data/jbo.js +1 -1
  91. package/locale-data/jgo.js +1 -1
  92. package/locale-data/ji.js +1 -1
  93. package/locale-data/jmc.js +1 -1
  94. package/locale-data/jv.js +1 -1
  95. package/locale-data/jw.js +1 -1
  96. package/locale-data/ka.js +1 -1
  97. package/locale-data/kab.js +1 -1
  98. package/locale-data/kaj.js +1 -1
  99. package/locale-data/kam.js +1 -1
  100. package/locale-data/kcg.js +1 -1
  101. package/locale-data/kde.js +1 -1
  102. package/locale-data/kea.js +1 -1
  103. package/locale-data/khq.js +1 -1
  104. package/locale-data/ki.js +1 -1
  105. package/locale-data/kk.js +1 -1
  106. package/locale-data/kkj.js +1 -1
  107. package/locale-data/kl.js +1 -1
  108. package/locale-data/kln.js +1 -1
  109. package/locale-data/km.js +1 -1
  110. package/locale-data/kn.js +1 -1
  111. package/locale-data/ko.js +1 -1
  112. package/locale-data/kok.js +1 -1
  113. package/locale-data/ks.js +1 -1
  114. package/locale-data/ksb.js +1 -1
  115. package/locale-data/ksf.js +1 -1
  116. package/locale-data/ksh.js +1 -1
  117. package/locale-data/ku.js +1 -1
  118. package/locale-data/kw.js +1 -1
  119. package/locale-data/ky.js +1 -1
  120. package/locale-data/lag.js +1 -1
  121. package/locale-data/lb.js +1 -1
  122. package/locale-data/lg.js +1 -1
  123. package/locale-data/lkt.js +1 -1
  124. package/locale-data/ln.js +1 -1
  125. package/locale-data/lo.js +1 -1
  126. package/locale-data/lrc.js +1 -1
  127. package/locale-data/lt.js +1 -1
  128. package/locale-data/lu.js +1 -1
  129. package/locale-data/luo.js +1 -1
  130. package/locale-data/luy.js +1 -1
  131. package/locale-data/lv.js +1 -1
  132. package/locale-data/mas.js +1 -1
  133. package/locale-data/mer.js +1 -1
  134. package/locale-data/mfe.js +1 -1
  135. package/locale-data/mg.js +1 -1
  136. package/locale-data/mgh.js +1 -1
  137. package/locale-data/mgo.js +1 -1
  138. package/locale-data/mk.js +1 -1
  139. package/locale-data/ml.js +1 -1
  140. package/locale-data/mn.js +1 -1
  141. package/locale-data/mo.js +1 -1
  142. package/locale-data/mr.js +1 -1
  143. package/locale-data/ms.js +1 -1
  144. package/locale-data/mt.js +1 -1
  145. package/locale-data/mua.js +1 -1
  146. package/locale-data/my.js +1 -1
  147. package/locale-data/mzn.js +1 -1
  148. package/locale-data/nah.js +1 -1
  149. package/locale-data/naq.js +1 -1
  150. package/locale-data/nb.js +1 -1
  151. package/locale-data/nd.js +1 -1
  152. package/locale-data/nds.js +1 -0
  153. package/locale-data/ne.js +1 -1
  154. package/locale-data/nl.js +1 -1
  155. package/locale-data/nmg.js +1 -1
  156. package/locale-data/nn.js +1 -1
  157. package/locale-data/nnh.js +1 -1
  158. package/locale-data/no.js +1 -1
  159. package/locale-data/nqo.js +1 -1
  160. package/locale-data/nr.js +1 -1
  161. package/locale-data/nso.js +1 -1
  162. package/locale-data/nus.js +1 -1
  163. package/locale-data/ny.js +1 -1
  164. package/locale-data/nyn.js +1 -1
  165. package/locale-data/om.js +1 -1
  166. package/locale-data/or.js +1 -1
  167. package/locale-data/os.js +1 -1
  168. package/locale-data/pa.js +1 -1
  169. package/locale-data/pap.js +1 -1
  170. package/locale-data/pl.js +1 -1
  171. package/locale-data/prg.js +1 -1
  172. package/locale-data/ps.js +1 -1
  173. package/locale-data/pt.js +1 -1
  174. package/locale-data/qu.js +1 -1
  175. package/locale-data/rm.js +1 -1
  176. package/locale-data/rn.js +1 -1
  177. package/locale-data/ro.js +1 -1
  178. package/locale-data/rof.js +1 -1
  179. package/locale-data/ru.js +1 -1
  180. package/locale-data/rw.js +1 -1
  181. package/locale-data/rwk.js +1 -1
  182. package/locale-data/sah.js +1 -1
  183. package/locale-data/saq.js +1 -1
  184. package/locale-data/sbp.js +1 -1
  185. package/locale-data/sdh.js +1 -1
  186. package/locale-data/se.js +1 -1
  187. package/locale-data/seh.js +1 -1
  188. package/locale-data/ses.js +1 -1
  189. package/locale-data/sg.js +1 -1
  190. package/locale-data/sh.js +1 -1
  191. package/locale-data/shi.js +1 -1
  192. package/locale-data/si.js +1 -1
  193. package/locale-data/sk.js +1 -1
  194. package/locale-data/sl.js +1 -1
  195. package/locale-data/sma.js +1 -1
  196. package/locale-data/smi.js +1 -1
  197. package/locale-data/smj.js +1 -1
  198. package/locale-data/smn.js +1 -1
  199. package/locale-data/sms.js +1 -1
  200. package/locale-data/sn.js +1 -1
  201. package/locale-data/so.js +1 -1
  202. package/locale-data/sq.js +1 -1
  203. package/locale-data/sr.js +1 -1
  204. package/locale-data/ss.js +1 -1
  205. package/locale-data/ssy.js +1 -1
  206. package/locale-data/st.js +1 -1
  207. package/locale-data/sv.js +1 -1
  208. package/locale-data/sw.js +1 -1
  209. package/locale-data/syr.js +1 -1
  210. package/locale-data/ta.js +1 -1
  211. package/locale-data/te.js +1 -1
  212. package/locale-data/teo.js +1 -1
  213. package/locale-data/th.js +1 -1
  214. package/locale-data/ti.js +1 -1
  215. package/locale-data/tig.js +1 -1
  216. package/locale-data/tk.js +1 -1
  217. package/locale-data/tl.js +1 -1
  218. package/locale-data/tn.js +1 -1
  219. package/locale-data/to.js +1 -1
  220. package/locale-data/tr.js +1 -1
  221. package/locale-data/ts.js +1 -1
  222. package/locale-data/twq.js +1 -1
  223. package/locale-data/tzm.js +1 -1
  224. package/locale-data/ug.js +1 -1
  225. package/locale-data/uk.js +1 -1
  226. package/locale-data/ur.js +1 -1
  227. package/locale-data/uz.js +1 -1
  228. package/locale-data/vai.js +1 -1
  229. package/locale-data/ve.js +1 -1
  230. package/locale-data/vi.js +1 -1
  231. package/locale-data/vo.js +1 -1
  232. package/locale-data/vun.js +1 -1
  233. package/locale-data/wa.js +1 -1
  234. package/locale-data/wae.js +1 -1
  235. package/locale-data/wo.js +1 -1
  236. package/locale-data/xh.js +1 -1
  237. package/locale-data/xog.js +1 -1
  238. package/locale-data/yav.js +1 -1
  239. package/locale-data/yi.js +1 -1
  240. package/locale-data/yo.js +1 -1
  241. package/locale-data/yue.js +1 -0
  242. package/locale-data/zgh.js +1 -1
  243. package/locale-data/zh.js +1 -1
  244. package/locale-data/zu.js +1 -1
  245. package/package.json +33 -27
  246. package/src/components/date.js +30 -29
  247. package/src/components/html-message.js +63 -62
  248. package/src/components/message.js +117 -114
  249. package/src/components/number.js +30 -29
  250. package/src/components/plural.js +37 -36
  251. package/src/components/provider.js +135 -134
  252. package/src/components/relative.js +128 -120
  253. package/src/components/time.js +30 -29
  254. package/src/define-messages.js +3 -3
  255. package/src/en.js +1 -1
  256. package/src/format.js +209 -208
  257. package/src/inject.js +38 -40
  258. package/src/locale-data-registry.js +21 -21
  259. package/src/plural.js +9 -9
  260. package/src/types.js +58 -48
  261. package/src/utils.js +65 -55
  262. 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, PropTypes} from 'react';
7
+ import React, {Component} from 'react';
8
+ import PropTypes from 'prop-types';
8
9
  import {intlShape, messageDescriptorPropTypes} from '../types';
9
10
  import {
10
- invariantIntlContext,
11
- shallowEquals,
12
- shouldIntlComponentUpdate,
11
+ invariantIntlContext,
12
+ shallowEquals,
13
+ shouldIntlComponentUpdate,
13
14
  } from '../utils';
14
15
 
15
16
  export default class FormattedHTMLMessage extends Component {
16
- static displayName = 'FormattedHTMLMessage';
17
+ static displayName = 'FormattedHTMLMessage';
17
18
 
18
- static contextTypes = {
19
- intl: intlShape,
20
- };
19
+ static contextTypes = {
20
+ intl: intlShape,
21
+ };
21
22
 
22
- static propTypes = {
23
- ...messageDescriptorPropTypes,
24
- values : PropTypes.object,
25
- tagName : PropTypes.string,
26
- children: PropTypes.func,
27
- };
23
+ static propTypes = {
24
+ ...messageDescriptorPropTypes,
25
+ values: PropTypes.object,
26
+ tagName: PropTypes.string,
27
+ children: PropTypes.func,
28
+ };
28
29
 
29
- static defaultProps = {
30
- values: {},
31
- };
32
-
33
- constructor(props, context) {
34
- super(props, context);
35
- invariantIntlContext(context);
36
- }
30
+ static defaultProps = {
31
+ values: {},
32
+ };
37
33
 
38
- shouldComponentUpdate(nextProps, ...next) {
39
- const {values} = this.props;
40
- const {values: nextValues} = nextProps;
34
+ constructor(props, context) {
35
+ super(props, context);
36
+ invariantIntlContext(context);
37
+ }
41
38
 
42
- if (!shallowEquals(nextValues, values)) {
43
- return true;
44
- }
39
+ shouldComponentUpdate(nextProps, ...next) {
40
+ const {values} = this.props;
41
+ const {values: nextValues} = nextProps;
45
42
 
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);
43
+ if (!shallowEquals(nextValues, values)) {
44
+ return true;
55
45
  }
56
46
 
57
- render() {
58
- const {formatHTMLMessage, textComponent: Text} = this.context.intl;
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
- const {
61
- id,
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
- let descriptor = {id, description, defaultMessage};
70
- let formattedHTMLMessage = formatHTMLMessage(descriptor, rawValues);
61
+ const {
62
+ id,
63
+ description,
64
+ defaultMessage,
65
+ values: rawValues,
66
+ tagName: Component = Text,
67
+ children,
68
+ } = this.props;
71
69
 
72
- if (typeof children === 'function') {
73
- return children(formattedHTMLMessage);
74
- }
70
+ let descriptor = {id, description, defaultMessage};
71
+ let formattedHTMLMessage = formatHTMLMessage(descriptor, rawValues);
75
72
 
76
- // Since the message presumably has HTML in it, we need to set
77
- // `innerHTML` in order for it to be rendered and not escaped by React.
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 React, {Component, PropTypes, isValidElement} from 'react';
7
+ import {Component, createElement, isValidElement} from 'react';
8
+ import PropTypes from 'prop-types';
8
9
  import {intlShape, messageDescriptorPropTypes} from '../types';
9
10
  import {
10
- invariantIntlContext,
11
- shallowEquals,
12
- shouldIntlComponentUpdate,
11
+ invariantIntlContext,
12
+ shallowEquals,
13
+ shouldIntlComponentUpdate,
13
14
  } from '../utils';
14
15
 
15
16
  export default class FormattedMessage extends Component {
16
- static displayName = 'FormattedMessage';
17
-
18
- static contextTypes = {
19
- intl: intlShape,
20
- };
21
-
22
- static propTypes = {
23
- ...messageDescriptorPropTypes,
24
- values : PropTypes.object,
25
- tagName : PropTypes.string,
26
- children: PropTypes.func,
27
- };
28
-
29
- static defaultProps = {
30
- values : {},
31
- };
32
-
33
- constructor(props, context) {
34
- super(props, context);
35
- invariantIntlContext(context);
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
- shouldComponentUpdate(nextProps, ...next) {
39
- const {values} = this.props;
40
- const {values: nextValues} = nextProps;
41
-
42
- if (!shallowEquals(nextValues, values)) {
43
- return true;
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
- let descriptor = {id, description, defaultMessage};
110
- let formattedMessage = formatMessage(descriptor, tokenizedValues || values);
111
-
112
- let nodes;
113
-
114
- let hasElements = elements && Object.keys(elements).length > 0;
115
- if (hasElements) {
116
- // Split the message into parts so the React Element values captured
117
- // above can be inserted back into the rendered message. This
118
- // approach allows messages to render with React Elements while
119
- // keeping React's virtual diffing working properly.
120
- nodes = formattedMessage
121
- .split(tokenDelimiter)
122
- .filter((part) => !!part)
123
- .map((part) => elements[part] || part);
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
- nodes = [formattedMessage];
105
+ tokenizedValues[name] = value;
126
106
  }
107
+ });
108
+ }
127
109
 
128
- if (typeof children === 'function') {
129
- return children(...nodes);
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
- return <Component>{nodes}</Component>;
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
  }
@@ -4,43 +4,44 @@
4
4
  * See the accompanying LICENSE file for terms.
5
5
  */
6
6
 
7
- import React, {Component, PropTypes} from 'react';
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
- static displayName = 'FormattedNumber';
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
- shouldComponentUpdate(...next) {
31
- return shouldIntlComponentUpdate(this, ...next);
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
- render() {
35
- const {formatNumber, textComponent: Text} = this.context.intl;
36
- const {value, children} = this.props;
26
+ constructor(props, context) {
27
+ super(props, context);
28
+ invariantIntlContext(context);
29
+ }
37
30
 
38
- let formattedNumber = formatNumber(value, this.props);
31
+ shouldComponentUpdate(...next) {
32
+ return shouldIntlComponentUpdate(this, ...next);
33
+ }
39
34
 
40
- if (typeof children === 'function') {
41
- return children(formattedNumber);
42
- }
35
+ render() {
36
+ const {formatNumber, textComponent: Text} = this.context.intl;
37
+ const {value, children} = this.props;
43
38
 
44
- return <Text>{formattedNumber}</Text>;
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
  }
@@ -4,55 +4,56 @@
4
4
  * See the accompanying LICENSE file for terms.
5
5
  */
6
6
 
7
- import React, {Component, PropTypes} from 'react';
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
- static displayName = 'FormattedPlural';
13
+ static displayName = 'FormattedPlural';
13
14
 
14
- static contextTypes = {
15
- intl: intlShape,
16
- };
15
+ static contextTypes = {
16
+ intl: intlShape,
17
+ };
17
18
 
18
- static propTypes = {
19
- ...pluralFormatPropTypes,
20
- value: PropTypes.any.isRequired,
19
+ static propTypes = {
20
+ ...pluralFormatPropTypes,
21
+ value: PropTypes.any.isRequired,
21
22
 
22
- other: PropTypes.node.isRequired,
23
- zero : PropTypes.node,
24
- one : PropTypes.node,
25
- two : PropTypes.node,
26
- few : PropTypes.node,
27
- many : PropTypes.node,
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
- children: PropTypes.func,
30
- };
30
+ children: PropTypes.func,
31
+ };
31
32
 
32
- static defaultProps = {
33
- style: 'cardinal',
34
- };
33
+ static defaultProps = {
34
+ style: 'cardinal',
35
+ };
35
36
 
36
- constructor(props, context) {
37
- super(props, context);
38
- invariantIntlContext(context);
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
- render() {
46
- const {formatPlural, textComponent: Text} = this.context.intl;
47
- const {value, other, children} = this.props;
42
+ shouldComponentUpdate(...next) {
43
+ return shouldIntlComponentUpdate(this, ...next);
44
+ }
48
45
 
49
- let pluralCategory = formatPlural(value, this.props);
50
- let formattedPlural = this.props[pluralCategory] || other;
46
+ render() {
47
+ const {formatPlural, textComponent: Text} = this.context.intl;
48
+ const {value, other, children} = this.props;
51
49
 
52
- if (typeof children === 'function') {
53
- return children(formattedPlural);
54
- }
50
+ let pluralCategory = formatPlural(value, this.props);
51
+ let formattedPlural = this.props[pluralCategory] || other;
55
52
 
56
- return <Text>{formattedPlural}</Text>;
53
+ if (typeof children === 'function') {
54
+ return children(formattedPlural);
57
55
  }
56
+
57
+ return <Text>{formattedPlural}</Text>;
58
+ }
58
59
  }