react-intl 2.3.0 → 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 (263) hide show
  1. package/dist/react-intl.js +960 -948
  2. package/dist/react-intl.js.map +1 -1
  3. package/dist/react-intl.min.js +1 -2
  4. package/dist/react-intl.min.js.map +1 -1
  5. package/lib/index.es.js +971 -970
  6. package/lib/index.js +971 -970
  7. package/locale-data/af.js +1 -1
  8. package/locale-data/agq.js +1 -1
  9. package/locale-data/ak.js +1 -1
  10. package/locale-data/am.js +1 -1
  11. package/locale-data/ar.js +1 -1
  12. package/locale-data/ars.js +1 -0
  13. package/locale-data/as.js +1 -1
  14. package/locale-data/asa.js +1 -1
  15. package/locale-data/ast.js +1 -1
  16. package/locale-data/az.js +1 -1
  17. package/locale-data/bas.js +1 -1
  18. package/locale-data/be.js +1 -1
  19. package/locale-data/bem.js +1 -1
  20. package/locale-data/bez.js +1 -1
  21. package/locale-data/bg.js +1 -1
  22. package/locale-data/bh.js +1 -1
  23. package/locale-data/bm.js +1 -1
  24. package/locale-data/bn.js +1 -1
  25. package/locale-data/bo.js +1 -1
  26. package/locale-data/br.js +1 -1
  27. package/locale-data/brx.js +1 -1
  28. package/locale-data/bs.js +1 -1
  29. package/locale-data/ca.js +1 -1
  30. package/locale-data/ce.js +1 -1
  31. package/locale-data/cgg.js +1 -1
  32. package/locale-data/chr.js +1 -1
  33. package/locale-data/ckb.js +1 -1
  34. package/locale-data/cs.js +1 -1
  35. package/locale-data/cu.js +1 -1
  36. package/locale-data/cy.js +1 -1
  37. package/locale-data/da.js +1 -1
  38. package/locale-data/dav.js +1 -1
  39. package/locale-data/de.js +1 -1
  40. package/locale-data/dje.js +1 -1
  41. package/locale-data/dsb.js +1 -1
  42. package/locale-data/dua.js +1 -1
  43. package/locale-data/dv.js +1 -1
  44. package/locale-data/dyo.js +1 -1
  45. package/locale-data/dz.js +1 -1
  46. package/locale-data/ebu.js +1 -1
  47. package/locale-data/ee.js +1 -1
  48. package/locale-data/el.js +1 -1
  49. package/locale-data/en.js +1 -1
  50. package/locale-data/eo.js +1 -1
  51. package/locale-data/es.js +1 -1
  52. package/locale-data/et.js +1 -1
  53. package/locale-data/eu.js +1 -1
  54. package/locale-data/ewo.js +1 -1
  55. package/locale-data/fa.js +1 -1
  56. package/locale-data/ff.js +1 -1
  57. package/locale-data/fi.js +1 -1
  58. package/locale-data/fil.js +1 -1
  59. package/locale-data/fo.js +1 -1
  60. package/locale-data/fr.js +1 -1
  61. package/locale-data/fur.js +1 -1
  62. package/locale-data/fy.js +1 -1
  63. package/locale-data/ga.js +1 -1
  64. package/locale-data/gd.js +1 -1
  65. package/locale-data/gl.js +1 -1
  66. package/locale-data/gsw.js +1 -1
  67. package/locale-data/gu.js +1 -1
  68. package/locale-data/guw.js +1 -1
  69. package/locale-data/guz.js +1 -1
  70. package/locale-data/gv.js +1 -1
  71. package/locale-data/ha.js +1 -1
  72. package/locale-data/haw.js +1 -1
  73. package/locale-data/he.js +1 -1
  74. package/locale-data/hi.js +1 -1
  75. package/locale-data/hr.js +1 -1
  76. package/locale-data/hsb.js +1 -1
  77. package/locale-data/hu.js +1 -1
  78. package/locale-data/hy.js +1 -1
  79. package/locale-data/id.js +1 -1
  80. package/locale-data/ig.js +1 -1
  81. package/locale-data/ii.js +1 -1
  82. package/locale-data/in.js +1 -1
  83. package/locale-data/index.js +1 -10
  84. package/locale-data/is.js +1 -1
  85. package/locale-data/it.js +1 -1
  86. package/locale-data/iu.js +1 -1
  87. package/locale-data/iw.js +1 -1
  88. package/locale-data/ja.js +1 -1
  89. package/locale-data/jbo.js +1 -1
  90. package/locale-data/jgo.js +1 -1
  91. package/locale-data/ji.js +1 -1
  92. package/locale-data/jmc.js +1 -1
  93. package/locale-data/jv.js +1 -1
  94. package/locale-data/jw.js +1 -1
  95. package/locale-data/ka.js +1 -1
  96. package/locale-data/kab.js +1 -1
  97. package/locale-data/kaj.js +1 -1
  98. package/locale-data/kam.js +1 -1
  99. package/locale-data/kcg.js +1 -1
  100. package/locale-data/kde.js +1 -1
  101. package/locale-data/kea.js +1 -1
  102. package/locale-data/khq.js +1 -1
  103. package/locale-data/ki.js +1 -1
  104. package/locale-data/kk.js +1 -1
  105. package/locale-data/kkj.js +1 -1
  106. package/locale-data/kl.js +1 -1
  107. package/locale-data/kln.js +1 -1
  108. package/locale-data/km.js +1 -1
  109. package/locale-data/kn.js +1 -1
  110. package/locale-data/ko.js +1 -1
  111. package/locale-data/kok.js +1 -1
  112. package/locale-data/ks.js +1 -1
  113. package/locale-data/ksb.js +1 -1
  114. package/locale-data/ksf.js +1 -1
  115. package/locale-data/ksh.js +1 -1
  116. package/locale-data/ku.js +1 -1
  117. package/locale-data/kw.js +1 -1
  118. package/locale-data/ky.js +1 -1
  119. package/locale-data/lag.js +1 -1
  120. package/locale-data/lb.js +1 -1
  121. package/locale-data/lg.js +1 -1
  122. package/locale-data/lkt.js +1 -1
  123. package/locale-data/ln.js +1 -1
  124. package/locale-data/lo.js +1 -1
  125. package/locale-data/lrc.js +1 -1
  126. package/locale-data/lt.js +1 -1
  127. package/locale-data/lu.js +1 -1
  128. package/locale-data/luo.js +1 -1
  129. package/locale-data/luy.js +1 -1
  130. package/locale-data/lv.js +1 -1
  131. package/locale-data/mas.js +1 -1
  132. package/locale-data/mer.js +1 -1
  133. package/locale-data/mfe.js +1 -1
  134. package/locale-data/mg.js +1 -1
  135. package/locale-data/mgh.js +1 -1
  136. package/locale-data/mgo.js +1 -1
  137. package/locale-data/mk.js +1 -1
  138. package/locale-data/ml.js +1 -1
  139. package/locale-data/mn.js +1 -1
  140. package/locale-data/mo.js +1 -1
  141. package/locale-data/mr.js +1 -1
  142. package/locale-data/ms.js +1 -1
  143. package/locale-data/mt.js +1 -1
  144. package/locale-data/mua.js +1 -1
  145. package/locale-data/my.js +1 -1
  146. package/locale-data/mzn.js +1 -1
  147. package/locale-data/nah.js +1 -1
  148. package/locale-data/naq.js +1 -1
  149. package/locale-data/nb.js +1 -1
  150. package/locale-data/nd.js +1 -1
  151. package/locale-data/nds.js +1 -0
  152. package/locale-data/ne.js +1 -1
  153. package/locale-data/nl.js +1 -1
  154. package/locale-data/nmg.js +1 -1
  155. package/locale-data/nn.js +1 -1
  156. package/locale-data/nnh.js +1 -1
  157. package/locale-data/no.js +1 -1
  158. package/locale-data/nqo.js +1 -1
  159. package/locale-data/nr.js +1 -1
  160. package/locale-data/nso.js +1 -1
  161. package/locale-data/nus.js +1 -1
  162. package/locale-data/ny.js +1 -1
  163. package/locale-data/nyn.js +1 -1
  164. package/locale-data/om.js +1 -1
  165. package/locale-data/or.js +1 -1
  166. package/locale-data/os.js +1 -1
  167. package/locale-data/pa.js +1 -1
  168. package/locale-data/pap.js +1 -1
  169. package/locale-data/pl.js +1 -1
  170. package/locale-data/prg.js +1 -1
  171. package/locale-data/ps.js +1 -1
  172. package/locale-data/pt.js +1 -1
  173. package/locale-data/qu.js +1 -1
  174. package/locale-data/rm.js +1 -1
  175. package/locale-data/rn.js +1 -1
  176. package/locale-data/ro.js +1 -1
  177. package/locale-data/rof.js +1 -1
  178. package/locale-data/ru.js +1 -1
  179. package/locale-data/rw.js +1 -1
  180. package/locale-data/rwk.js +1 -1
  181. package/locale-data/sah.js +1 -1
  182. package/locale-data/saq.js +1 -1
  183. package/locale-data/sbp.js +1 -1
  184. package/locale-data/sdh.js +1 -1
  185. package/locale-data/se.js +1 -1
  186. package/locale-data/seh.js +1 -1
  187. package/locale-data/ses.js +1 -1
  188. package/locale-data/sg.js +1 -1
  189. package/locale-data/sh.js +1 -1
  190. package/locale-data/shi.js +1 -1
  191. package/locale-data/si.js +1 -1
  192. package/locale-data/sk.js +1 -1
  193. package/locale-data/sl.js +1 -1
  194. package/locale-data/sma.js +1 -1
  195. package/locale-data/smi.js +1 -1
  196. package/locale-data/smj.js +1 -1
  197. package/locale-data/smn.js +1 -1
  198. package/locale-data/sms.js +1 -1
  199. package/locale-data/sn.js +1 -1
  200. package/locale-data/so.js +1 -1
  201. package/locale-data/sq.js +1 -1
  202. package/locale-data/sr.js +1 -1
  203. package/locale-data/ss.js +1 -1
  204. package/locale-data/ssy.js +1 -1
  205. package/locale-data/st.js +1 -1
  206. package/locale-data/sv.js +1 -1
  207. package/locale-data/sw.js +1 -1
  208. package/locale-data/syr.js +1 -1
  209. package/locale-data/ta.js +1 -1
  210. package/locale-data/te.js +1 -1
  211. package/locale-data/teo.js +1 -1
  212. package/locale-data/th.js +1 -1
  213. package/locale-data/ti.js +1 -1
  214. package/locale-data/tig.js +1 -1
  215. package/locale-data/tk.js +1 -1
  216. package/locale-data/tl.js +1 -1
  217. package/locale-data/tn.js +1 -1
  218. package/locale-data/to.js +1 -1
  219. package/locale-data/tr.js +1 -1
  220. package/locale-data/ts.js +1 -1
  221. package/locale-data/twq.js +1 -1
  222. package/locale-data/tzm.js +1 -1
  223. package/locale-data/ug.js +1 -1
  224. package/locale-data/uk.js +1 -1
  225. package/locale-data/ur.js +1 -1
  226. package/locale-data/uz.js +1 -1
  227. package/locale-data/vai.js +1 -1
  228. package/locale-data/ve.js +1 -1
  229. package/locale-data/vi.js +1 -1
  230. package/locale-data/vo.js +1 -1
  231. package/locale-data/vun.js +1 -1
  232. package/locale-data/wa.js +1 -1
  233. package/locale-data/wae.js +1 -1
  234. package/locale-data/wo.js +1 -1
  235. package/locale-data/xh.js +1 -1
  236. package/locale-data/xog.js +1 -1
  237. package/locale-data/yav.js +1 -1
  238. package/locale-data/yi.js +1 -1
  239. package/locale-data/yo.js +1 -1
  240. package/locale-data/yue.js +1 -0
  241. package/locale-data/zgh.js +1 -1
  242. package/locale-data/zh.js +1 -1
  243. package/locale-data/zu.js +1 -1
  244. package/package.json +11 -9
  245. package/src/components/date.js +47 -0
  246. package/src/components/html-message.js +88 -0
  247. package/src/components/message.js +137 -0
  248. package/src/components/number.js +47 -0
  249. package/src/components/plural.js +59 -0
  250. package/src/components/provider.js +177 -0
  251. package/src/components/relative.js +171 -0
  252. package/src/components/time.js +47 -0
  253. package/src/define-messages.js +11 -0
  254. package/src/en.js +2 -0
  255. package/src/format.js +276 -0
  256. package/src/index.js +12 -0
  257. package/src/inject.js +59 -0
  258. package/src/locale-data-registry.js +42 -0
  259. package/src/plural.js +28 -0
  260. package/src/react-intl.js +24 -0
  261. package/src/types.js +98 -0
  262. package/src/utils.js +103 -0
  263. package/yarn.lock +87 -59
@@ -0,0 +1,137 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ import {Component, createElement, isValidElement} from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import {intlShape, messageDescriptorPropTypes} from '../types';
10
+ import {
11
+ invariantIntlContext,
12
+ shallowEquals,
13
+ shouldIntlComponentUpdate,
14
+ } from '../utils';
15
+
16
+ export default class FormattedMessage extends Component {
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;
45
+ }
46
+
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
+ }
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;
104
+ } else {
105
+ tokenizedValues[name] = value;
106
+ }
107
+ });
108
+ }
109
+
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
+ }
128
+
129
+ if (typeof children === 'function') {
130
+ return children(...nodes);
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
+ }
137
+ }
@@ -0,0 +1,47 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ import React, {Component} from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import {intlShape, numberFormatPropTypes} from '../types';
10
+ import {invariantIntlContext, shouldIntlComponentUpdate} from '../utils';
11
+
12
+ export default class FormattedNumber extends Component {
13
+ static displayName = 'FormattedNumber';
14
+
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
+ };
25
+
26
+ constructor(props, context) {
27
+ super(props, context);
28
+ invariantIntlContext(context);
29
+ }
30
+
31
+ shouldComponentUpdate(...next) {
32
+ return shouldIntlComponentUpdate(this, ...next);
33
+ }
34
+
35
+ render() {
36
+ const {formatNumber, textComponent: Text} = this.context.intl;
37
+ const {value, children} = this.props;
38
+
39
+ let formattedNumber = formatNumber(value, this.props);
40
+
41
+ if (typeof children === 'function') {
42
+ return children(formattedNumber);
43
+ }
44
+
45
+ return <Text>{formattedNumber}</Text>;
46
+ }
47
+ }
@@ -0,0 +1,59 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ import React, {Component} from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import {intlShape, pluralFormatPropTypes} from '../types';
10
+ import {invariantIntlContext, shouldIntlComponentUpdate} from '../utils';
11
+
12
+ export default class FormattedPlural extends Component {
13
+ static displayName = 'FormattedPlural';
14
+
15
+ static contextTypes = {
16
+ intl: intlShape,
17
+ };
18
+
19
+ static propTypes = {
20
+ ...pluralFormatPropTypes,
21
+ value: PropTypes.any.isRequired,
22
+
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,
29
+
30
+ children: PropTypes.func,
31
+ };
32
+
33
+ static defaultProps = {
34
+ style: 'cardinal',
35
+ };
36
+
37
+ constructor(props, context) {
38
+ super(props, context);
39
+ invariantIntlContext(context);
40
+ }
41
+
42
+ shouldComponentUpdate(...next) {
43
+ return shouldIntlComponentUpdate(this, ...next);
44
+ }
45
+
46
+ render() {
47
+ const {formatPlural, textComponent: Text} = this.context.intl;
48
+ const {value, other, children} = this.props;
49
+
50
+ let pluralCategory = formatPlural(value, this.props);
51
+ let formattedPlural = this.props[pluralCategory] || other;
52
+
53
+ if (typeof children === 'function') {
54
+ return children(formattedPlural);
55
+ }
56
+
57
+ return <Text>{formattedPlural}</Text>;
58
+ }
59
+ }
@@ -0,0 +1,177 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ import {Component, Children} from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import IntlMessageFormat from 'intl-messageformat';
10
+ import IntlRelativeFormat from 'intl-relativeformat';
11
+ import IntlPluralFormat from '../plural';
12
+ import memoizeIntlConstructor from 'intl-format-cache';
13
+ import invariant from 'invariant';
14
+ import {shouldIntlComponentUpdate, filterProps} from '../utils';
15
+ import {intlConfigPropTypes, intlFormatPropTypes, intlShape} from '../types';
16
+ import * as format from '../format';
17
+ import {hasLocaleData} from '../locale-data-registry';
18
+
19
+ const intlConfigPropNames = Object.keys(intlConfigPropTypes);
20
+ const intlFormatPropNames = Object.keys(intlFormatPropTypes);
21
+
22
+ // These are not a static property on the `IntlProvider` class so the intl
23
+ // config values can be inherited from an <IntlProvider> ancestor.
24
+ const defaultProps = {
25
+ formats: {},
26
+ messages: {},
27
+ textComponent: 'span',
28
+
29
+ defaultLocale: 'en',
30
+ defaultFormats: {},
31
+ };
32
+
33
+ export default class IntlProvider extends Component {
34
+ static displayName = 'IntlProvider';
35
+
36
+ static contextTypes = {
37
+ intl: intlShape,
38
+ };
39
+
40
+ static childContextTypes = {
41
+ intl: intlShape.isRequired,
42
+ };
43
+
44
+ static propTypes = {
45
+ ...intlConfigPropTypes,
46
+ children: PropTypes.element.isRequired,
47
+ initialNow: PropTypes.any,
48
+ };
49
+
50
+ constructor(props, context = {}) {
51
+ super(props, context);
52
+
53
+ invariant(
54
+ typeof Intl !== 'undefined',
55
+ '[React Intl] The `Intl` APIs must be available in the runtime, ' +
56
+ 'and do not appear to be built-in. An `Intl` polyfill should be loaded.\n' +
57
+ 'See: http://formatjs.io/guides/runtime-environments/'
58
+ );
59
+
60
+ const {intl: intlContext} = context;
61
+
62
+ // Used to stabilize time when performing an initial rendering so that
63
+ // all relative times use the same reference "now" time.
64
+ let initialNow;
65
+ if (isFinite(props.initialNow)) {
66
+ initialNow = Number(props.initialNow);
67
+ } else {
68
+ // When an `initialNow` isn't provided via `props`, look to see an
69
+ // <IntlProvider> exists in the ancestry and call its `now()`
70
+ // function to propagate its value for "now".
71
+ initialNow = intlContext ? intlContext.now() : Date.now();
72
+ }
73
+
74
+ // Creating `Intl*` formatters is expensive. If there's a parent
75
+ // `<IntlProvider>`, then its formatters will be used. Otherwise, this
76
+ // memoize the `Intl*` constructors and cache them for the lifecycle of
77
+ // this IntlProvider instance.
78
+ const {
79
+ formatters = {
80
+ getDateTimeFormat: memoizeIntlConstructor(Intl.DateTimeFormat),
81
+ getNumberFormat: memoizeIntlConstructor(Intl.NumberFormat),
82
+ getMessageFormat: memoizeIntlConstructor(IntlMessageFormat),
83
+ getRelativeFormat: memoizeIntlConstructor(IntlRelativeFormat),
84
+ getPluralFormat: memoizeIntlConstructor(IntlPluralFormat),
85
+ },
86
+ } =
87
+ intlContext || {};
88
+
89
+ this.state = {
90
+ ...formatters,
91
+
92
+ // Wrapper to provide stable "now" time for initial render.
93
+ now: () => {
94
+ return this._didDisplay ? Date.now() : initialNow;
95
+ },
96
+ };
97
+ }
98
+
99
+ getConfig() {
100
+ const {intl: intlContext} = this.context;
101
+
102
+ // Build a whitelisted config object from `props`, defaults, and
103
+ // `context.intl`, if an <IntlProvider> exists in the ancestry.
104
+ let config = filterProps(this.props, intlConfigPropNames, intlContext);
105
+
106
+ // Apply default props. This must be applied last after the props have
107
+ // been resolved and inherited from any <IntlProvider> in the ancestry.
108
+ // This matches how React resolves `defaultProps`.
109
+ for (let propName in defaultProps) {
110
+ if (config[propName] === undefined) {
111
+ config[propName] = defaultProps[propName];
112
+ }
113
+ }
114
+
115
+ if (!hasLocaleData(config.locale)) {
116
+ const {locale, defaultLocale, defaultFormats} = config;
117
+
118
+ if (process.env.NODE_ENV !== 'production') {
119
+ console.error(
120
+ `[React Intl] Missing locale data for locale: "${locale}". ` +
121
+ `Using default locale: "${defaultLocale}" as fallback.`
122
+ );
123
+ }
124
+
125
+ // Since there's no registered locale data for `locale`, this will
126
+ // fallback to the `defaultLocale` to make sure things can render.
127
+ // The `messages` are overridden to the `defaultProps` empty object
128
+ // to maintain referential equality across re-renders. It's assumed
129
+ // each <FormattedMessage> contains a `defaultMessage` prop.
130
+ config = {
131
+ ...config,
132
+ locale: defaultLocale,
133
+ formats: defaultFormats,
134
+ messages: defaultProps.messages,
135
+ };
136
+ }
137
+
138
+ return config;
139
+ }
140
+
141
+ getBoundFormatFns(config, state) {
142
+ return intlFormatPropNames.reduce((boundFormatFns, name) => {
143
+ boundFormatFns[name] = format[name].bind(null, config, state);
144
+ return boundFormatFns;
145
+ }, {});
146
+ }
147
+
148
+ getChildContext() {
149
+ const config = this.getConfig();
150
+
151
+ // Bind intl factories and current config to the format functions.
152
+ const boundFormatFns = this.getBoundFormatFns(config, this.state);
153
+
154
+ const {now, ...formatters} = this.state;
155
+
156
+ return {
157
+ intl: {
158
+ ...config,
159
+ ...boundFormatFns,
160
+ formatters,
161
+ now,
162
+ },
163
+ };
164
+ }
165
+
166
+ shouldComponentUpdate(...next) {
167
+ return shouldIntlComponentUpdate(this, ...next);
168
+ }
169
+
170
+ componentDidMount() {
171
+ this._didDisplay = true;
172
+ }
173
+
174
+ render() {
175
+ return Children.only(this.props.children);
176
+ }
177
+ }
@@ -0,0 +1,171 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ import React, {Component} from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import {intlShape, relativeFormatPropTypes} from '../types';
10
+ import {invariantIntlContext, shouldIntlComponentUpdate} from '../utils';
11
+
12
+ const SECOND = 1000;
13
+ const MINUTE = 1000 * 60;
14
+ const HOUR = 1000 * 60 * 60;
15
+ const DAY = 1000 * 60 * 60 * 24;
16
+
17
+ // The maximum timer delay value is a 32-bit signed integer.
18
+ // See: https://mdn.io/setTimeout
19
+ const MAX_TIMER_DELAY = 2147483647;
20
+
21
+ function selectUnits(delta) {
22
+ let absDelta = Math.abs(delta);
23
+
24
+ if (absDelta < MINUTE) {
25
+ return 'second';
26
+ }
27
+
28
+ if (absDelta < HOUR) {
29
+ return 'minute';
30
+ }
31
+
32
+ if (absDelta < DAY) {
33
+ return 'hour';
34
+ }
35
+
36
+ // The maximum scheduled delay will be measured in days since the maximum
37
+ // timer delay is less than the number of milliseconds in 25 days.
38
+ return 'day';
39
+ }
40
+
41
+ function getUnitDelay(units) {
42
+ switch (units) {
43
+ case 'second':
44
+ return SECOND;
45
+ case 'minute':
46
+ return MINUTE;
47
+ case 'hour':
48
+ return HOUR;
49
+ case 'day':
50
+ return DAY;
51
+ default:
52
+ return MAX_TIMER_DELAY;
53
+ }
54
+ }
55
+
56
+ function isSameDate(a, b) {
57
+ if (a === b) {
58
+ return true;
59
+ }
60
+
61
+ let aTime = new Date(a).getTime();
62
+ let bTime = new Date(b).getTime();
63
+
64
+ return isFinite(aTime) && isFinite(bTime) && aTime === bTime;
65
+ }
66
+
67
+ export default class FormattedRelative extends Component {
68
+ static displayName = 'FormattedRelative';
69
+
70
+ static contextTypes = {
71
+ intl: intlShape,
72
+ };
73
+
74
+ static propTypes = {
75
+ ...relativeFormatPropTypes,
76
+ value: PropTypes.any.isRequired,
77
+ format: PropTypes.string,
78
+ updateInterval: PropTypes.number,
79
+ initialNow: PropTypes.any,
80
+ children: PropTypes.func,
81
+ };
82
+
83
+ static defaultProps = {
84
+ updateInterval: 1000 * 10,
85
+ };
86
+
87
+ constructor(props, context) {
88
+ super(props, context);
89
+ invariantIntlContext(context);
90
+
91
+ let now = isFinite(props.initialNow)
92
+ ? Number(props.initialNow)
93
+ : context.intl.now();
94
+
95
+ // `now` is stored as state so that `render()` remains a function of
96
+ // props + state, instead of accessing `Date.now()` inside `render()`.
97
+ this.state = {now};
98
+ }
99
+
100
+ scheduleNextUpdate(props, state) {
101
+ // Cancel and pending update because we're scheduling a new update.
102
+ clearTimeout(this._timer);
103
+
104
+ const {value, units, updateInterval} = props;
105
+ const time = new Date(value).getTime();
106
+
107
+ // If the `updateInterval` is falsy, including `0` or we don't have a
108
+ // valid date, then auto updates have been turned off, so we bail and
109
+ // skip scheduling an update.
110
+ if (!updateInterval || !isFinite(time)) {
111
+ return;
112
+ }
113
+
114
+ const delta = time - state.now;
115
+ const unitDelay = getUnitDelay(units || selectUnits(delta));
116
+ const unitRemainder = Math.abs(delta % unitDelay);
117
+
118
+ // We want the largest possible timer delay which will still display
119
+ // accurate information while reducing unnecessary re-renders. The delay
120
+ // should be until the next "interesting" moment, like a tick from
121
+ // "1 minute ago" to "2 minutes ago" when the delta is 120,000ms.
122
+ const delay =
123
+ delta < 0
124
+ ? Math.max(updateInterval, unitDelay - unitRemainder)
125
+ : Math.max(updateInterval, unitRemainder);
126
+
127
+ this._timer = setTimeout(() => {
128
+ this.setState({now: this.context.intl.now()});
129
+ }, delay);
130
+ }
131
+
132
+ componentDidMount() {
133
+ this.scheduleNextUpdate(this.props, this.state);
134
+ }
135
+
136
+ componentWillReceiveProps({value: nextValue}) {
137
+ // When the `props.value` date changes, `state.now` needs to be updated,
138
+ // and the next update can be rescheduled.
139
+ if (!isSameDate(nextValue, this.props.value)) {
140
+ this.setState({now: this.context.intl.now()});
141
+ }
142
+ }
143
+
144
+ shouldComponentUpdate(...next) {
145
+ return shouldIntlComponentUpdate(this, ...next);
146
+ }
147
+
148
+ componentWillUpdate(nextProps, nextState) {
149
+ this.scheduleNextUpdate(nextProps, nextState);
150
+ }
151
+
152
+ componentWillUnmount() {
153
+ clearTimeout(this._timer);
154
+ }
155
+
156
+ render() {
157
+ const {formatRelative, textComponent: Text} = this.context.intl;
158
+ const {value, children} = this.props;
159
+
160
+ let formattedRelative = formatRelative(value, {
161
+ ...this.props,
162
+ ...this.state,
163
+ });
164
+
165
+ if (typeof children === 'function') {
166
+ return children(formattedRelative);
167
+ }
168
+
169
+ return <Text>{formattedRelative}</Text>;
170
+ }
171
+ }
@@ -0,0 +1,47 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ import React, {Component} from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import {intlShape, dateTimeFormatPropTypes} from '../types';
10
+ import {invariantIntlContext, shouldIntlComponentUpdate} from '../utils';
11
+
12
+ export default class FormattedTime extends Component {
13
+ static displayName = 'FormattedTime';
14
+
15
+ static contextTypes = {
16
+ intl: intlShape,
17
+ };
18
+
19
+ static propTypes = {
20
+ ...dateTimeFormatPropTypes,
21
+ value: PropTypes.any.isRequired,
22
+ format: PropTypes.string,
23
+ children: PropTypes.func,
24
+ };
25
+
26
+ constructor(props, context) {
27
+ super(props, context);
28
+ invariantIntlContext(context);
29
+ }
30
+
31
+ shouldComponentUpdate(...next) {
32
+ return shouldIntlComponentUpdate(this, ...next);
33
+ }
34
+
35
+ render() {
36
+ const {formatTime, textComponent: Text} = this.context.intl;
37
+ const {value, children} = this.props;
38
+
39
+ let formattedTime = formatTime(value, this.props);
40
+
41
+ if (typeof children === 'function') {
42
+ return children(formattedTime);
43
+ }
44
+
45
+ return <Text>{formattedTime}</Text>;
46
+ }
47
+ }
@@ -0,0 +1,11 @@
1
+ /*
2
+ * Copyright 2015, Yahoo Inc.
3
+ * Copyrights licensed under the New BSD License.
4
+ * See the accompanying LICENSE file for terms.
5
+ */
6
+
7
+ export default function defineMessages(messageDescriptors) {
8
+ // This simply returns what's passed-in because it's meant to be a hook for
9
+ // babel-plugin-react-intl.
10
+ return messageDescriptors;
11
+ }
package/src/en.js ADDED
@@ -0,0 +1,2 @@
1
+ // GENERATED FILE
2
+ export default {"locale":"en","pluralRuleFunction":function (n,ord){var s=String(n).split("."),v0=!s[1],t0=Number(s[0])==n,n10=t0&&s[0].slice(-1),n100=t0&&s[0].slice(-2);if(ord)return n10==1&&n100!=11?"one":n10==2&&n100!=12?"two":n10==3&&n100!=13?"few":"other";return n==1&&v0?"one":"other"},"fields":{"year":{"displayName":"year","relative":{"0":"this year","1":"next year","-1":"last year"},"relativeTime":{"future":{"one":"in {0} year","other":"in {0} years"},"past":{"one":"{0} year ago","other":"{0} years ago"}}},"month":{"displayName":"month","relative":{"0":"this month","1":"next month","-1":"last month"},"relativeTime":{"future":{"one":"in {0} month","other":"in {0} months"},"past":{"one":"{0} month ago","other":"{0} months ago"}}},"day":{"displayName":"day","relative":{"0":"today","1":"tomorrow","-1":"yesterday"},"relativeTime":{"future":{"one":"in {0} day","other":"in {0} days"},"past":{"one":"{0} day ago","other":"{0} days ago"}}},"hour":{"displayName":"hour","relative":{"0":"this hour"},"relativeTime":{"future":{"one":"in {0} hour","other":"in {0} hours"},"past":{"one":"{0} hour ago","other":"{0} hours ago"}}},"minute":{"displayName":"minute","relative":{"0":"this minute"},"relativeTime":{"future":{"one":"in {0} minute","other":"in {0} minutes"},"past":{"one":"{0} minute ago","other":"{0} minutes ago"}}},"second":{"displayName":"second","relative":{"0":"now"},"relativeTime":{"future":{"one":"in {0} second","other":"in {0} seconds"},"past":{"one":"{0} second ago","other":"{0} seconds ago"}}}}};