react-intl 3.7.0 → 3.9.2

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/dist/types.d.ts CHANGED
@@ -37,6 +37,7 @@ export interface IntlFormatters {
37
37
  formatMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType>): string;
38
38
  formatMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType | React.ReactElement | FormatXMLElementFn>): string | React.ReactNodeArray;
39
39
  formatHTMLMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType>): React.ReactNode;
40
+ formatList(values: Array<string>, opts?: FormatListOptions): string;
40
41
  formatList(values: Array<string | React.ReactNode>, opts?: FormatListOptions): React.ReactNode;
41
42
  }
42
43
  export interface Formatters {
@@ -11,7 +11,7 @@ interface Props extends FormatPluralOptions {
11
11
  many?: React.ReactNode;
12
12
  children?(value: React.ReactNode): React.ReactElement | null;
13
13
  }
14
- declare const _default: React.ForwardRefExoticComponent<Pick<Props, "children" | "other" | "zero" | "one" | "two" | "few" | "many" | "type" | "localeMatcher" | "format" | "value"> & {
14
+ declare const _default: React.ForwardRefExoticComponent<Pick<Props, "children" | "other" | "zero" | "one" | "two" | "few" | "many" | "localeMatcher" | "type" | "format" | "value"> & {
15
15
  forwardedRef?: ((instance: any) => void) | React.RefObject<any> | null | undefined;
16
16
  } & React.RefAttributes<any>> & {
17
17
  WrappedComponent: React.ComponentType<Props>;
@@ -1,3 +1,2 @@
1
- import * as React from 'react';
2
1
  import { IntlConfig, Formatters, IntlFormatters } from '../types';
3
- export declare function formatList({ locale, onError }: Pick<IntlConfig, 'locale' | 'onError'>, getListFormat: Formatters['getListFormat'], values: Parameters<IntlFormatters['formatList']>[0], options?: Parameters<IntlFormatters['formatList']>[1]): string | React.ReactNode;
2
+ export declare function formatList({ locale, onError }: Pick<IntlConfig, 'locale' | 'onError'>, getListFormat: Formatters['getListFormat'], values: Array<string>, options: Parameters<IntlFormatters['formatList']>[1]): string;
@@ -82,7 +82,7 @@ export declare const FormattedNumberParts: React.FC<Formatter['formatNumber'] &
82
82
  children(val: Intl.NumberFormatPart[]): React.ReactElement | null;
83
83
  }>;
84
84
 
85
- export declare const FormattedPlural: React.ForwardRefExoticComponent<Pick<Props_2, "children" | "other" | "zero" | "one" | "two" | "few" | "many" | "type" | "localeMatcher" | "format" | "value"> & {
85
+ export declare const FormattedPlural: React.ForwardRefExoticComponent<Pick<Props_2, "children" | "other" | "zero" | "one" | "two" | "few" | "many" | "localeMatcher" | "type" | "format" | "value"> & {
86
86
  forwardedRef?: ((instance: any) => void) | React.RefObject<any> | null | undefined;
87
87
  } & React.RefAttributes<any>> & {
88
88
  WrappedComponent: React.ComponentType<Props_2>;
@@ -169,6 +169,7 @@ export declare interface IntlFormatters {
169
169
  formatMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType>): string;
170
170
  formatMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType | React.ReactElement | FormatXMLElementFn>): string | React.ReactNodeArray;
171
171
  formatHTMLMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType>): React.ReactNode;
172
+ formatList(values: Array<string>, opts?: FormatListOptions): string;
172
173
  formatList(values: Array<string | React.ReactNode>, opts?: FormatListOptions): React.ReactNode;
173
174
  }
174
175
 
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.6.2"
8
+ "packageVersion": "7.7.0"
9
9
  }
10
10
  ]
11
11
  }
package/lib/types.d.ts CHANGED
@@ -37,6 +37,7 @@ export interface IntlFormatters {
37
37
  formatMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType>): string;
38
38
  formatMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType | React.ReactElement | FormatXMLElementFn>): string | React.ReactNodeArray;
39
39
  formatHTMLMessage(descriptor: MessageDescriptor, values?: Record<string, PrimitiveType>): React.ReactNode;
40
+ formatList(values: Array<string>, opts?: FormatListOptions): string;
40
41
  formatList(values: Array<string | React.ReactNode>, opts?: FormatListOptions): React.ReactNode;
41
42
  }
42
43
  export interface Formatters {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-intl",
3
- "version": "3.7.0",
3
+ "version": "3.9.2",
4
4
  "description": "Internationalize React apps. This library provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.",
5
5
  "keywords": [
6
6
  "intl",
@@ -19,7 +19,8 @@
19
19
  "author": "Eric Ferraiuolo <edf@ericf.me>",
20
20
  "files": [
21
21
  "dist",
22
- "lib"
22
+ "lib",
23
+ "src"
23
24
  ],
24
25
  "contributors": [
25
26
  "Aarni Koskela <akx@iki.fi>",
@@ -130,50 +131,50 @@
130
131
  "types": "./lib/react-intl.d.ts",
131
132
  "sideEffects": false,
132
133
  "dependencies": {
133
- "@formatjs/intl-listformat": "^1.2.6",
134
- "@formatjs/intl-relativetimeformat": "^4.4.5",
135
- "@formatjs/intl-unified-numberformat": "^2.1.6",
136
- "@formatjs/macro": "^0.2.5",
134
+ "@formatjs/intl-listformat": "^1.3.1",
135
+ "@formatjs/intl-relativetimeformat": "^4.5.1",
136
+ "@formatjs/intl-unified-numberformat": "^2.2.0",
137
+ "@formatjs/macro": "^0.2.6",
137
138
  "@types/hoist-non-react-statics": "^3.3.1",
138
139
  "@types/invariant": "^2.2.30",
139
140
  "hoist-non-react-statics": "^3.3.1",
140
- "intl-format-cache": "^4.2.10",
141
- "intl-locales-supported": "^1.8.3",
142
- "intl-messageformat": "^7.6.0",
143
- "intl-messageformat-parser": "^3.3.0",
141
+ "intl-format-cache": "^4.2.13",
142
+ "intl-locales-supported": "^1.8.4",
143
+ "intl-messageformat": "^7.7.2",
144
+ "intl-messageformat-parser": "^3.5.1",
144
145
  "invariant": "^2.1.1",
145
- "shallow-equal": "^1.1.0"
146
+ "shallow-equal": "^1.2.1"
146
147
  },
147
148
  "peerDependencies": {
148
149
  "react": "^16.3.0"
149
150
  },
150
151
  "devDependencies": {
151
- "@babel/core": "^7.7.4",
152
+ "@babel/core": "^7.7.5",
152
153
  "@babel/node": "^7.7.4",
153
154
  "@babel/plugin-proposal-class-properties": "^7.7.4",
154
- "@babel/plugin-transform-modules-commonjs": "^7.7.4",
155
- "@babel/preset-env": "^7.7.4",
155
+ "@babel/plugin-transform-modules-commonjs": "^7.7.5",
156
+ "@babel/preset-env": "^7.7.6",
156
157
  "@babel/preset-react": "^7.7.4",
157
- "@formatjs/intl-pluralrules": "^1.3.6",
158
- "@microsoft/api-documenter": "^7.7.0",
159
- "@microsoft/api-extractor": "^7.6.2",
158
+ "@formatjs/intl-pluralrules": "^1.3.9",
159
+ "@microsoft/api-documenter": "^7.7.2",
160
+ "@microsoft/api-extractor": "^7.7.0",
160
161
  "@types/benchmark": "^1.0.31",
161
- "@types/enzyme": "^3.10.3",
162
+ "@types/enzyme": "^3.10.4",
162
163
  "@types/jest": "^24.0.23",
163
164
  "@types/prop-types": "^15.7.3",
164
- "@types/react": "^16.9.13",
165
+ "@types/react": "^16.9.16",
165
166
  "@types/react-dom": "^16.9.4",
166
- "@typescript-eslint/eslint-plugin": "^2.9.0",
167
- "@typescript-eslint/parser": "^2.9.0",
167
+ "@typescript-eslint/eslint-plugin": "^2.11.0",
168
+ "@typescript-eslint/parser": "^2.11.0",
168
169
  "babel-jest": "^24.9.0",
169
170
  "benchmark": "^2.1.0",
170
- "core-js": "^3.4.2",
171
+ "core-js": "^3.4.8",
171
172
  "cross-env": "^6.0.3",
172
173
  "enzyme": "^3.6.0",
173
174
  "enzyme-adapter-react-16": "^1.15.1",
174
175
  "enzyme-to-json": "^3.4.3",
175
- "eslint": "^6.7.1",
176
- "eslint-plugin-react": "^7.16.0",
176
+ "eslint": "^6.7.2",
177
+ "eslint-plugin-react": "^7.17.0",
177
178
  "fs-extra": "^8.1.0",
178
179
  "full-icu": "^1.3.0",
179
180
  "glob": "^7.1.6",
@@ -186,16 +187,16 @@
186
187
  "react": "^16.12.0",
187
188
  "react-dom": "^16.12.0",
188
189
  "rimraf": "^3.0.0",
189
- "rollup": "^1.27.5",
190
+ "rollup": "^1.27.9",
190
191
  "rollup-plugin-babel": "^4.3.3",
191
192
  "rollup-plugin-commonjs": "^10.1.0",
192
193
  "rollup-plugin-node-resolve": "^5.2.0",
193
194
  "rollup-plugin-replace": "^2.0.0",
194
- "rollup-plugin-typescript2": "^0.25.0",
195
+ "rollup-plugin-typescript2": "^0.25.3",
195
196
  "rollup-plugin-uglify": "^6.0.3",
196
197
  "standard-version": "^7.0.1",
197
198
  "ts-jest": "^24.2.0",
198
- "ts-node": "^8.5.2",
199
+ "ts-node": "^8.5.4",
199
200
  "tslib": "^1.9.3",
200
201
  "typescript": "~3.3.0"
201
202
  },
@@ -0,0 +1,114 @@
1
+ import * as React from 'react';
2
+ import {invariantIntlContext} from '../utils';
3
+ import {
4
+ IntlShape,
5
+ FormatDateOptions,
6
+ FormatNumberOptions,
7
+ FormatListOptions,
8
+ } from '../types';
9
+ import {Context} from './injectIntl';
10
+
11
+ enum DisplayName {
12
+ formatDate = 'FormattedDate',
13
+ formatTime = 'FormattedTime',
14
+ formatNumber = 'FormattedNumber',
15
+ formatList = 'FormattedList',
16
+ }
17
+
18
+ enum DisplayNameParts {
19
+ formatDate = 'FormattedDateParts',
20
+ formatTime = 'FormattedTimeParts',
21
+ formatNumber = 'FormattedNumberParts',
22
+ formatList = 'FormattedListParts',
23
+ }
24
+
25
+ type Formatter = {
26
+ formatDate: FormatDateOptions;
27
+ formatTime: FormatDateOptions;
28
+ formatNumber: FormatNumberOptions;
29
+ formatList: FormatListOptions;
30
+ };
31
+
32
+ export const FormattedNumberParts: React.FC<Formatter['formatNumber'] & {
33
+ value: Parameters<IntlShape['formatNumber']>[0];
34
+
35
+ children(val: Intl.NumberFormatPart[]): React.ReactElement | null;
36
+ }> = props => (
37
+ <Context.Consumer>
38
+ {(intl): React.ReactElement | null => {
39
+ invariantIntlContext(intl);
40
+ const {value, children, ...formatProps} = props;
41
+ return children(intl.formatNumberToParts(value, formatProps));
42
+ }}
43
+ </Context.Consumer>
44
+ );
45
+ FormattedNumberParts.displayName = 'FormattedNumberParts';
46
+
47
+ export function createFormattedDateTimePartsComponent<
48
+ Name extends 'formatDate' | 'formatTime'
49
+ >(
50
+ name: Name
51
+ ): React.FC<
52
+ Formatter[Name] & {
53
+ value: Parameters<IntlShape[Name]>[0];
54
+ children(val: Intl.DateTimeFormatPart[]): React.ReactElement | null;
55
+ }
56
+ > {
57
+ type FormatFn = IntlShape[Name];
58
+ type Props = Formatter[Name] & {
59
+ value: Parameters<FormatFn>[0];
60
+ children(val: Intl.DateTimeFormatPart[]): React.ReactElement | null;
61
+ };
62
+
63
+ const ComponentParts: React.FC<Props> = props => (
64
+ <Context.Consumer>
65
+ {(intl): React.ReactElement | null => {
66
+ invariantIntlContext(intl);
67
+ const {value, children, ...formatProps} = props;
68
+ const date = typeof value === 'string' ? new Date(value || 0) : value;
69
+ const formattedParts: Intl.DateTimeFormatPart[] =
70
+ name === 'formatDate'
71
+ ? intl.formatDateToParts(date, formatProps)
72
+ : intl.formatTimeToParts(date, formatProps);
73
+
74
+ return children(formattedParts);
75
+ }}
76
+ </Context.Consumer>
77
+ );
78
+ ComponentParts.displayName = DisplayNameParts[name];
79
+ return ComponentParts;
80
+ }
81
+
82
+ export function createFormattedComponent<Name extends keyof Formatter>(
83
+ name: Name
84
+ ): React.FC<
85
+ Formatter[Name] & {
86
+ value: Parameters<IntlShape[Name]>[0];
87
+ children?(val: string): React.ReactElement | null;
88
+ }
89
+ > {
90
+ type FormatFn = IntlShape[Name];
91
+ type Props = Formatter[Name] & {
92
+ value: Parameters<FormatFn>[0];
93
+ children?(val: string): React.ReactElement | null;
94
+ };
95
+
96
+ const Component: React.FC<Props> = props => (
97
+ <Context.Consumer>
98
+ {(intl): JSX.Element | null => {
99
+ invariantIntlContext(intl);
100
+ const {value, children, ...formatProps} = props;
101
+ // TODO: fix TS type definition for localeMatcher upstream
102
+ const formattedValue = intl[name](value as any, formatProps as any);
103
+
104
+ if (typeof children === 'function') {
105
+ return children(formattedValue as any);
106
+ }
107
+ const Text = intl.textComponent || React.Fragment;
108
+ return <Text>{formattedValue}</Text>;
109
+ }}
110
+ </Context.Consumer>
111
+ );
112
+ Component.displayName = DisplayName[name];
113
+ return Component;
114
+ }
@@ -0,0 +1,68 @@
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 * as React from 'react';
8
+ import {PrimitiveType} from 'intl-messageformat';
9
+ import FormattedMessage from './message';
10
+ import {Context} from './injectIntl';
11
+ import {invariantIntlContext} from '../utils';
12
+
13
+ class FormattedHTMLMessage extends FormattedMessage<
14
+ Record<string, PrimitiveType>
15
+ > {
16
+ static displayName = 'FormattedHTMLMessage';
17
+ static defaultProps = {
18
+ ...FormattedMessage.defaultProps,
19
+ tagName: 'span' as 'span',
20
+ };
21
+ render(): JSX.Element {
22
+ return (
23
+ <Context.Consumer>
24
+ {(intl): React.ReactNode => {
25
+ if (!this.props.defaultMessage) {
26
+ invariantIntlContext(intl);
27
+ }
28
+
29
+ const {formatHTMLMessage, textComponent} = intl;
30
+ const {
31
+ id,
32
+ description,
33
+ defaultMessage,
34
+ values: rawValues,
35
+ children,
36
+ } = this.props;
37
+
38
+ let {tagName: Component} = this.props;
39
+
40
+ // This is bc of TS3.3 doesn't recognize `defaultProps`
41
+ if (!Component) {
42
+ Component = textComponent || 'span';
43
+ }
44
+
45
+ const descriptor = {id, description, defaultMessage};
46
+ const formattedHTMLMessage = formatHTMLMessage(descriptor, rawValues);
47
+
48
+ if (typeof children === 'function') {
49
+ return children(formattedHTMLMessage);
50
+ }
51
+
52
+ // Since the message presumably has HTML in it, we need to set
53
+ // `innerHTML` in order for it to be rendered and not escaped by React.
54
+ // To be safe, all string prop values were escaped when formatting the
55
+ // message. It is assumed that the message is not UGC, and came from the
56
+ // developer making it more like a template.
57
+ //
58
+ // Note: There's a perf impact of using this component since there's no
59
+ // way for React to do its virtual DOM diffing.
60
+ const html = {__html: formattedHTMLMessage};
61
+ return <Component dangerouslySetInnerHTML={html} />;
62
+ }}
63
+ </Context.Consumer>
64
+ );
65
+ }
66
+ }
67
+
68
+ export default FormattedHTMLMessage;
@@ -0,0 +1,111 @@
1
+ import * as React from 'react';
2
+ import * as hoistNonReactStatics_ from 'hoist-non-react-statics';
3
+ // Since rollup cannot deal with namespace being a function,
4
+ // this is to interop with TypeScript since `invariant`
5
+ // does not export a default
6
+ // https://github.com/rollup/rollup/issues/1267
7
+ const hoistNonReactStatics: typeof hoistNonReactStatics_ =
8
+ (hoistNonReactStatics_ as any).default || hoistNonReactStatics_;
9
+ import {invariantIntlContext} from '../utils';
10
+ import {IntlShape, Omit} from '../types';
11
+
12
+ function getDisplayName(Component: React.ComponentType<any>): string {
13
+ return Component.displayName || Component.name || 'Component';
14
+ }
15
+
16
+ // TODO: We should provide initial value here
17
+ const IntlContext = React.createContext<IntlShape>(null as any);
18
+ const {Consumer: IntlConsumer, Provider: IntlProvider} = IntlContext;
19
+
20
+ export const Provider = IntlProvider;
21
+ export const Context = IntlContext;
22
+
23
+ export interface Opts<
24
+ IntlPropName extends string = 'intl',
25
+ ForwardRef extends boolean = false
26
+ > {
27
+ intlPropName?: IntlPropName;
28
+ forwardRef?: ForwardRef;
29
+ enforceContext?: boolean;
30
+ }
31
+
32
+ export type WrappedComponentProps<IntlPropName extends string = 'intl'> = {
33
+ [k in IntlPropName]: IntlShape;
34
+ };
35
+
36
+ export type WithIntlProps<P> = Omit<P, keyof WrappedComponentProps> & {
37
+ forwardedRef?: React.Ref<any>;
38
+ };
39
+
40
+ export default function injectIntl<
41
+ IntlPropName extends string,
42
+ P extends WrappedComponentProps<IntlPropName> = WrappedComponentProps<any>
43
+ >(
44
+ WrappedComponent: React.ComponentType<P>,
45
+ options?: Opts<IntlPropName, false>
46
+ ): React.FC<WithIntlProps<P>> & {
47
+ WrappedComponent: React.ComponentType<P>;
48
+ };
49
+ export default function injectIntl<
50
+ IntlPropName extends string = 'intl',
51
+ P extends WrappedComponentProps<IntlPropName> = WrappedComponentProps<any>,
52
+ T extends React.ComponentType<P> = any
53
+ >(
54
+ WrappedComponent: React.ComponentType<P>,
55
+ options?: Opts<IntlPropName, true>
56
+ ): React.ForwardRefExoticComponent<
57
+ React.PropsWithoutRef<WithIntlProps<P>> & React.RefAttributes<T>
58
+ > & {
59
+ WrappedComponent: React.ComponentType<P>;
60
+ };
61
+ export default function injectIntl<
62
+ IntlPropName extends string = 'intl',
63
+ P extends WrappedComponentProps<IntlPropName> = WrappedComponentProps<any>,
64
+ ForwardRef extends boolean = false,
65
+ T extends React.ComponentType<P> = any
66
+ >(
67
+ WrappedComponent: React.ComponentType<P>,
68
+ options?: Opts<IntlPropName, ForwardRef>
69
+ ): React.ForwardRefExoticComponent<
70
+ React.PropsWithoutRef<WithIntlProps<P>> & React.RefAttributes<T>
71
+ > & {
72
+ WrappedComponent: React.ComponentType<P>;
73
+ } {
74
+ const {intlPropName = 'intl', forwardRef = false, enforceContext = true} =
75
+ options || {};
76
+
77
+ const WithIntl: React.FC<P & {forwardedRef?: React.Ref<any>}> & {
78
+ WrappedComponent: React.ComponentType<P>;
79
+ } = props => (
80
+ <IntlConsumer>
81
+ {(intl): React.ReactNode => {
82
+ if (enforceContext) {
83
+ invariantIntlContext(intl);
84
+ }
85
+
86
+ return (
87
+ <WrappedComponent
88
+ {...props}
89
+ {...{
90
+ [intlPropName]: intl,
91
+ }}
92
+ ref={forwardRef ? props.forwardedRef : null}
93
+ />
94
+ );
95
+ }}
96
+ </IntlConsumer>
97
+ );
98
+ WithIntl.displayName = `injectIntl(${getDisplayName(WrappedComponent)})`;
99
+ WithIntl.WrappedComponent = WrappedComponent;
100
+
101
+ if (forwardRef) {
102
+ return hoistNonReactStatics(
103
+ React.forwardRef<T, P>((props: P, ref) => (
104
+ <WithIntl {...props} forwardedRef={ref} />
105
+ )),
106
+ WrappedComponent
107
+ ) as any;
108
+ }
109
+
110
+ return hoistNonReactStatics(WithIntl, WrappedComponent) as any;
111
+ }
@@ -0,0 +1,120 @@
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 * as React from 'react';
8
+ import {PrimitiveType, FormatXMLElementFn} from 'intl-messageformat';
9
+ import {Context} from './injectIntl';
10
+ import {MessageDescriptor} from '../types';
11
+ import {formatMessage} from '../formatters/message';
12
+ import {
13
+ invariantIntlContext,
14
+ DEFAULT_INTL_CONFIG,
15
+ createFormatters,
16
+ } from '../utils';
17
+ import * as shallowEquals_ from 'shallow-equal/objects';
18
+ const shallowEquals: typeof shallowEquals_ =
19
+ (shallowEquals_ as any).default || shallowEquals_;
20
+
21
+ const defaultFormatMessage = (
22
+ descriptor: MessageDescriptor,
23
+ values?: Record<
24
+ string,
25
+ PrimitiveType | React.ReactElement | FormatXMLElementFn
26
+ >
27
+ ): string => {
28
+ if (process.env.NODE_ENV !== 'production') {
29
+ console.error(
30
+ '[React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry. Using default message as fallback.'
31
+ );
32
+ }
33
+
34
+ return formatMessage(
35
+ {
36
+ ...DEFAULT_INTL_CONFIG,
37
+ locale: 'en',
38
+ },
39
+ createFormatters(),
40
+ descriptor,
41
+ values as any
42
+ );
43
+ };
44
+
45
+ export interface Props<
46
+ V extends Record<string, any> = Record<string, React.ReactNode>
47
+ > extends MessageDescriptor {
48
+ values?: V;
49
+ tagName?: React.ElementType<any>;
50
+ children?(...nodes: React.ReactNodeArray): React.ReactNode;
51
+ }
52
+
53
+ class FormattedMessage<
54
+ V extends Record<string, any> = Record<
55
+ string,
56
+ PrimitiveType | React.ReactElement | FormatXMLElementFn
57
+ >
58
+ > extends React.Component<Props<V>> {
59
+ static displayName = 'FormattedMessage';
60
+ static defaultProps = {
61
+ values: {},
62
+ };
63
+
64
+ shouldComponentUpdate(nextProps: Props<V>): boolean {
65
+ const {values, ...otherProps} = this.props;
66
+ const {values: nextValues, ...nextOtherProps} = nextProps;
67
+ return (
68
+ !shallowEquals(nextValues, values) ||
69
+ !shallowEquals(otherProps, nextOtherProps)
70
+ );
71
+ }
72
+
73
+ render(): JSX.Element {
74
+ return (
75
+ <Context.Consumer>
76
+ {(intl): React.ReactNode => {
77
+ if (!this.props.defaultMessage) {
78
+ invariantIntlContext(intl);
79
+ }
80
+
81
+ const {
82
+ formatMessage = defaultFormatMessage,
83
+ textComponent: Text = React.Fragment,
84
+ } = intl || {};
85
+ const {
86
+ id,
87
+ description,
88
+ defaultMessage,
89
+ values,
90
+ children,
91
+ tagName: Component = Text,
92
+ } = this.props;
93
+
94
+ const descriptor = {id, description, defaultMessage};
95
+ let nodes: string | React.ReactNodeArray = formatMessage(
96
+ descriptor,
97
+ values
98
+ );
99
+
100
+ if (!Array.isArray(nodes)) {
101
+ nodes = [nodes];
102
+ }
103
+
104
+ if (typeof children === 'function') {
105
+ return children(...nodes);
106
+ }
107
+
108
+ if (Component) {
109
+ // Needs to use `createElement()` instead of JSX, otherwise React will
110
+ // warn about a missing `key` prop with rich-text message formatting.
111
+ return React.createElement(Component, null, ...nodes);
112
+ }
113
+ return nodes;
114
+ }}
115
+ </Context.Consumer>
116
+ );
117
+ }
118
+ }
119
+
120
+ export default FormattedMessage;
@@ -0,0 +1,50 @@
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 * as React from 'react';
8
+ import withIntl from './injectIntl';
9
+ import {IntlShape, FormatPluralOptions} from '../types';
10
+
11
+ interface Props extends FormatPluralOptions {
12
+ value: number;
13
+ intl: IntlShape;
14
+ other: React.ReactNode;
15
+ zero?: React.ReactNode;
16
+ one?: React.ReactNode;
17
+ two?: React.ReactNode;
18
+ few?: React.ReactNode;
19
+ many?: React.ReactNode;
20
+ children?(value: React.ReactNode): React.ReactElement | null;
21
+ }
22
+
23
+ const FormattedPlural: React.FC<Props> = props => {
24
+ const {
25
+ value,
26
+ other,
27
+ children,
28
+ intl: {formatPlural, textComponent: Text},
29
+ } = props;
30
+
31
+ const pluralCategory = formatPlural(value, props);
32
+ const formattedPlural = props[pluralCategory as 'one'] || other;
33
+
34
+ if (typeof children === 'function') {
35
+ return children(formattedPlural);
36
+ }
37
+ if (Text) {
38
+ return <Text>{formattedPlural}</Text>;
39
+ }
40
+ // Work around @types/react where React.FC cannot return string
41
+ return formattedPlural as any;
42
+ };
43
+
44
+ FormattedPlural.defaultProps = {
45
+ type: 'cardinal',
46
+ };
47
+
48
+ FormattedPlural.displayName = 'FormattedPlural';
49
+
50
+ export default withIntl(FormattedPlural);