Skip to content
This repository was archived by the owner on May 4, 2020. It is now read-only.

Commit 3605693

Browse files
authored
feat(intl-messageformat): allow passing in formatters (#107)
1 parent e4c0c0f commit 3605693

File tree

16 files changed

+306
-218
lines changed

16 files changed

+306
-218
lines changed

packages/babel-plugin-react-intl/CHANGELOG.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
77

88
**Note:** Version bump only for package babel-plugin-react-intl
99

10-
11-
12-
13-
1410
# [3.4.0](https://github.com/formatjs/formatjs/compare/[email protected]@3.4.0) (2019-06-27)
1511

1612
### Features

packages/formatjs-extract-cldr-data/CHANGELOG.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
77

88
**Note:** Version bump only for package formatjs-extract-cldr-data
99

10-
11-
12-
13-
1410
# [9.1.0](https://github.com/formatjs/formatjs/compare/[email protected]@9.1.0) (2019-06-27)
1511

1612
### Features

packages/intl-format-cache/CHANGELOG.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
77

88
**Note:** Version bump only for package intl-format-cache
99

10-
11-
12-
13-
1410
# [3.2.0](https://github.com/formatjs/formatjs/compare/[email protected]@3.2.0) (2019-06-27)
1511

1612
### Features

packages/intl-locales-supported/CHANGELOG.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
77

88
**Note:** Version bump only for package intl-locales-supported
99

10-
11-
12-
13-
1410
# [1.2.0](https://github.com/formatjs/formatjs/compare/[email protected]@1.2.0) (2019-06-27)
1511

1612
### Features

packages/intl-messageformat-parser/CHANGELOG.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
55

66
## [1.7.1](https://github.com/formatjs/formatjs/compare/[email protected]@1.7.1) (2019-06-26)
77

8-
98
### Bug Fixes
109

11-
* **intl-messageformat-parser:** Escape double-' to a single ' ([#103](https://github.com/formatjs/formatjs/issues/103)) ([4d0cd1f](https://github.com/formatjs/formatjs/commit/4d0cd1f))
12-
13-
14-
15-
10+
- **intl-messageformat-parser:** Escape double-' to a single ' ([#103](https://github.com/formatjs/formatjs/issues/103)) ([4d0cd1f](https://github.com/formatjs/formatjs/commit/4d0cd1f))
1611

1712
# [1.7.0](https://github.com/formatjs/formatjs/compare/[email protected]@1.7.0) (2019-06-27)
1813

packages/intl-messageformat/CHANGELOG.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
77

88
**Note:** Version bump only for package intl-messageformat
99

10-
11-
12-
13-
1410
# [4.2.0](https://github.com/formatjs/formatjs/compare/[email protected]@4.2.0) (2019-06-27)
1511

1612
### Features

packages/intl-messageformat/README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ _Note: This `IntlMessageFormat` API may change to stay in sync with ECMA-402, bu
2323
Messages are provided into the constructor as a `String` message, or a [pre-parsed AST][parser] object.
2424

2525
```js
26-
var msg = new IntlMessageFormat(message, locales, [formats]);
26+
var msg = new IntlMessageFormat(message, locales, [formats], [opts]);
2727
```
2828

2929
The string `message` is parsed, then stored internally in a compiled form that is optimized for the `format()` method to produce the formatted string for displaying to the user.
@@ -122,6 +122,9 @@ To create a message to format, use the `IntlMessageFormat` constructor. The cons
122122

123123
- **[formats]** - _{Object}_ - Optional object with user defined options for format styles.
124124

125+
- **[opts]** - `{ formatters?: Formatters }`: Optional options.
126+
- `formatters`: Map containing memoized formatters for performance.
127+
125128
```js
126129
var msg = new IntlMessageFormat('My name is {name}.', 'en-US');
127130
```
@@ -178,6 +181,8 @@ In this example, we're defining a `USD` number format style which is passed to t
178181

179182
## Advanced Usage
180183

184+
### Core entry point
185+
181186
We also expose another entry point via `intl-messageformat/core` that does not contain the parser from `intl-messageformat-parser`. This is significantly smaller than the regular package but expects the message passed in to be in `AST` form instead of string. E.g:
182187

183188
```ts
@@ -193,6 +198,25 @@ new IntlMessageFormat(parser.parse('hello')).format(); // prints out hello
193198

194199
This helps performance for cases like SSR or preload/precompilation-supported platforms since `AST` can be cached.
195200

201+
### Formatters
202+
203+
For complex messages, initializing `Intl.*` constructors can be expensive. Therefore, we allow user to pass in `formatters` to provide memoized instances of these `Intl` objects. This opts combines with passing in AST + using [core entry point](#core-entry-point) and `intl-format-cache` can speed things up by 30x per the benchmark down below.
204+
205+
For example:
206+
207+
```ts
208+
import IntlMessageFormat from 'intl-messageformat';
209+
import memoizeIntlConstructor from 'intl-format-cache';
210+
const formatters = {
211+
getNumberFormat: memoizeIntlConstructor(Intl.NumberFormat),
212+
getDateTimeFormat: memoizeIntlConstructor(Intl.DateTimeFormat),
213+
getPluralRules: memoizeIntlConstructor(Intl.PluralRules)
214+
};
215+
new IntlMessageFormat('hello {number, number}', 'en', undefined, {
216+
formatters
217+
}).format({ number: 3 }); // prints out `hello, 3`
218+
```
219+
196220
## Examples
197221

198222
### Plural Label
@@ -222,6 +246,19 @@ console.log(msg.format({numPhotos: 1000})); // => "You have 1,000 photos."
222246

223247
_Note: how when `numPhotos` was `1000`, the number is formatted with the correct thousands separator._
224248

249+
## Benchmark
250+
251+
```
252+
format_cached_complex_msg x 539,674 ops/sec ±1.87% (87 runs sampled)
253+
format_cached_string_msg x 99,311,640 ops/sec ±2.15% (87 runs sampled)
254+
new_complex_msg_preparsed x 1,490 ops/sec ±8.37% (54 runs sampled)
255+
new_complex_msg x 836 ops/sec ±31.96% (67 runs sampled)
256+
new_string_msg x 27,752 ops/sec ±8.25% (65 runs sampled)
257+
complex msg format x 799 ops/sec ±9.38% (55 runs sampled)
258+
complex msg w/ formatters format x 1,878 ops/sec ±16.63% (64 runs sampled)
259+
complex preparsed msg w/ formatters format x 26,482 ops/sec ±2.55% (84 runs sampled)
260+
```
261+
225262
## License
226263

227264
This software is free to use under the Yahoo! Inc. BSD license.

packages/intl-messageformat/src/compiler.ts

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ export interface Formats {
1818
time: Record<string, Intl.DateTimeFormatOptions>;
1919
}
2020

21+
export interface Formatters {
22+
getNumberFormat(
23+
...args: ConstructorParameters<typeof Intl.NumberFormat>
24+
): Intl.NumberFormat;
25+
getDateTimeFormat(
26+
...args: ConstructorParameters<typeof Intl.DateTimeFormat>
27+
): Intl.DateTimeFormat;
28+
getPluralRules(
29+
...args: ConstructorParameters<typeof Intl.PluralRules>
30+
): Intl.PluralRules;
31+
}
32+
2133
export type Pattern =
2234
| string
2335
| PluralOffsetString
@@ -35,10 +47,16 @@ export default class Compiler {
3547
private pluralNumberFormat: Intl.NumberFormat | null = null;
3648
private currentPlural: ArgumentElement | null | undefined = null;
3749
private pluralStack: Array<ArgumentElement | null | undefined> = [];
50+
private formatters: Formatters;
3851

39-
constructor(locales: string | string[], formats: Formats) {
52+
constructor(
53+
locales: string | string[],
54+
formats: Formats,
55+
formatters: Formatters
56+
) {
4057
this.locales = locales;
4158
this.formats = formats;
59+
this.formatters = formatters;
4260
}
4361

4462
compile(ast: MessageFormatPattern): Pattern[] {
@@ -96,6 +114,7 @@ export default class Compiler {
96114

97115
compileArgument(element: ArgumentElement) {
98116
const { format, id } = element;
117+
const { formatters } = this;
99118

100119
if (!format) {
101120
return new StringFormat(id);
@@ -106,31 +125,38 @@ export default class Compiler {
106125
case 'numberFormat':
107126
return {
108127
id,
109-
format: new Intl.NumberFormat(locales, formats.number[format.style])
110-
.format
128+
format: formatters.getNumberFormat(
129+
locales,
130+
formats.number[format.style]
131+
).format
111132
};
112133

113134
case 'dateFormat':
114135
return {
115136
id,
116-
format: new Intl.DateTimeFormat(locales, formats.date[format.style])
117-
.format
137+
format: formatters.getDateTimeFormat(
138+
locales,
139+
formats.date[format.style]
140+
).format
118141
};
119142

120143
case 'timeFormat':
121144
return {
122145
id,
123-
format: new Intl.DateTimeFormat(locales, formats.time[format.style])
124-
.format
146+
format: formatters.getDateTimeFormat(
147+
locales,
148+
formats.time[format.style]
149+
).format
125150
};
126151

127152
case 'pluralFormat':
128153
return new PluralFormat(
129154
id,
130-
format.ordinal,
131155
format.offset,
132156
this.compileOptions(element),
133-
locales
157+
formatters.getPluralRules(locales, {
158+
type: format.ordinal ? 'ordinal' : 'cardinal'
159+
})
134160
);
135161

136162
case 'selectFormat':
@@ -176,7 +202,7 @@ abstract class Formatter {
176202
abstract format(value: string | number): string;
177203
}
178204

179-
export class StringFormat extends Formatter {
205+
class StringFormat extends Formatter {
180206
format(value: number | string) {
181207
if (!value && typeof value !== 'number') {
182208
return '';
@@ -186,24 +212,21 @@ export class StringFormat extends Formatter {
186212
}
187213
}
188214

189-
export class PluralFormat {
215+
class PluralFormat {
190216
public id: string;
191217
private offset: number;
192218
private options: Record<string, Pattern[]>;
193219
private pluralRules: Intl.PluralRules;
194220
constructor(
195221
id: string,
196-
useOrdinal: boolean,
197222
offset: number,
198223
options: Record<string, Pattern[]>,
199-
locales: string | string[]
224+
pluralRules: Intl.PluralRules
200225
) {
201226
this.id = id;
202227
this.offset = offset;
203228
this.options = options;
204-
this.pluralRules = new Intl.PluralRules(locales, {
205-
type: useOrdinal ? 'ordinal' : 'cardinal'
206-
});
229+
this.pluralRules = pluralRules;
207230
}
208231

209232
getOption(value: number) {

0 commit comments

Comments
 (0)