intl-messageformat 4.1.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -44
- package/LICENSE +0 -0
- package/README.md +108 -59
- package/core.js +1 -0
- package/dist/compiler.d.ts +10 -4
- package/dist/compiler.js +11 -14
- package/dist/compiler.js.map +1 -1
- package/dist/core.d.ts +78 -0
- package/dist/core.js +248 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +4 -16
- package/dist/index.js +7 -198
- package/dist/index.js.map +1 -1
- package/dist/umd/intl-messageformat.js +1548 -1373
- package/dist/umd/intl-messageformat.js.map +1 -1
- package/dist/umd/intl-messageformat.min.js +1 -1
- package/dist/umd/intl-messageformat.min.js.map +1 -1
- package/index.js +0 -0
- package/lib/compiler.d.ts +10 -4
- package/lib/compiler.js +11 -14
- package/lib/compiler.js.map +1 -1
- package/lib/core.d.ts +78 -0
- package/lib/core.js +245 -0
- package/lib/core.js.map +1 -0
- package/lib/index.d.ts +4 -16
- package/lib/index.js +4 -198
- package/lib/index.js.map +1 -1
- package/package.json +4 -4
- package/src/compiler.ts +39 -16
- package/src/core.ts +269 -0
- package/src/index.ts +6 -242
- package/.nyc_output/34ec6f1e-d2e9-445f-8813-bd6e8b5975bb.json +0 -1
- package/.nyc_output/9b57550b-ff23-4ed6-b289-f7ff8d2beb4f.json +0 -1
- package/.nyc_output/b696f16a-7b55-4692-a441-41aa11ca2fb0.json +0 -1
- package/.nyc_output/processinfo/34ec6f1e-d2e9-445f-8813-bd6e8b5975bb.json +0 -1
- package/.nyc_output/processinfo/9b57550b-ff23-4ed6-b289-f7ff8d2beb4f.json +0 -1
- package/.nyc_output/processinfo/b696f16a-7b55-4692-a441-41aa11ca2fb0.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;EAIE
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;EAIE;AAEF,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,OAAO,iBAAiB,MAAM,QAAQ,CAAC;AAEvC,iBAAiB,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;AAGzC,cAAc,QAAQ,CAAC;AAEvB,eAAe,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intl-messageformat",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"module": "lib/index.js",
|
|
32
32
|
"typings": "lib/index.d.ts",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"intl-messageformat-parser": "^1.
|
|
34
|
+
"intl-messageformat-parser": "^1.8.1"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"clean": "rimraf dist lib",
|
|
38
|
-
"benchmark": "ts-node --project
|
|
38
|
+
"benchmark": "ts-node --project ./tsconfig.cjs.json tests/benchmark",
|
|
39
39
|
"build": "tsc && tsc -p tsconfig.cjs.json && rollup -c rollup.config.js",
|
|
40
40
|
"test": "mocha --opts ../../mocha.opts tests/index.ts",
|
|
41
41
|
"karma:local": "karma start karma.conf.js",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"test": "tests"
|
|
47
47
|
},
|
|
48
48
|
"license": "BSD-3-Clause",
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "60ddf374ed659ffe2f3807c3c7df411c80492761"
|
|
50
50
|
}
|
package/src/compiler.ts
CHANGED
|
@@ -18,6 +18,18 @@ export interface Formats {
|
|
|
18
18
|
time: Record<string, Intl.DateTimeFormatOptions>;
|
|
19
19
|
}
|
|
20
20
|
|
|
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
|
+
|
|
21
33
|
export type Pattern =
|
|
22
34
|
| string
|
|
23
35
|
| PluralOffsetString
|
|
@@ -35,10 +47,16 @@ export default class Compiler {
|
|
|
35
47
|
private pluralNumberFormat: Intl.NumberFormat | null = null;
|
|
36
48
|
private currentPlural: ArgumentElement | null | undefined = null;
|
|
37
49
|
private pluralStack: Array<ArgumentElement | null | undefined> = [];
|
|
50
|
+
private formatters: Formatters;
|
|
38
51
|
|
|
39
|
-
constructor(
|
|
52
|
+
constructor(
|
|
53
|
+
locales: string | string[],
|
|
54
|
+
formats: Formats,
|
|
55
|
+
formatters: Formatters
|
|
56
|
+
) {
|
|
40
57
|
this.locales = locales;
|
|
41
58
|
this.formats = formats;
|
|
59
|
+
this.formatters = formatters;
|
|
42
60
|
}
|
|
43
61
|
|
|
44
62
|
compile(ast: MessageFormatPattern): Pattern[] {
|
|
@@ -96,6 +114,7 @@ export default class Compiler {
|
|
|
96
114
|
|
|
97
115
|
compileArgument(element: ArgumentElement) {
|
|
98
116
|
const { format, id } = element;
|
|
117
|
+
const { formatters } = this;
|
|
99
118
|
|
|
100
119
|
if (!format) {
|
|
101
120
|
return new StringFormat(id);
|
|
@@ -106,31 +125,38 @@ export default class Compiler {
|
|
|
106
125
|
case 'numberFormat':
|
|
107
126
|
return {
|
|
108
127
|
id,
|
|
109
|
-
format:
|
|
110
|
-
|
|
128
|
+
format: formatters.getNumberFormat(
|
|
129
|
+
locales,
|
|
130
|
+
formats.number[format.style]
|
|
131
|
+
).format
|
|
111
132
|
};
|
|
112
133
|
|
|
113
134
|
case 'dateFormat':
|
|
114
135
|
return {
|
|
115
136
|
id,
|
|
116
|
-
format:
|
|
117
|
-
|
|
137
|
+
format: formatters.getDateTimeFormat(
|
|
138
|
+
locales,
|
|
139
|
+
formats.date[format.style]
|
|
140
|
+
).format
|
|
118
141
|
};
|
|
119
142
|
|
|
120
143
|
case 'timeFormat':
|
|
121
144
|
return {
|
|
122
145
|
id,
|
|
123
|
-
format:
|
|
124
|
-
|
|
146
|
+
format: formatters.getDateTimeFormat(
|
|
147
|
+
locales,
|
|
148
|
+
formats.time[format.style]
|
|
149
|
+
).format
|
|
125
150
|
};
|
|
126
151
|
|
|
127
152
|
case 'pluralFormat':
|
|
128
153
|
return new PluralFormat(
|
|
129
154
|
id,
|
|
130
|
-
format.ordinal,
|
|
131
155
|
format.offset,
|
|
132
156
|
this.compileOptions(element),
|
|
133
|
-
locales
|
|
157
|
+
formatters.getPluralRules(locales, {
|
|
158
|
+
type: format.ordinal ? 'ordinal' : 'cardinal'
|
|
159
|
+
})
|
|
134
160
|
);
|
|
135
161
|
|
|
136
162
|
case 'selectFormat':
|
|
@@ -176,7 +202,7 @@ abstract class Formatter {
|
|
|
176
202
|
abstract format(value: string | number): string;
|
|
177
203
|
}
|
|
178
204
|
|
|
179
|
-
|
|
205
|
+
class StringFormat extends Formatter {
|
|
180
206
|
format(value: number | string) {
|
|
181
207
|
if (!value && typeof value !== 'number') {
|
|
182
208
|
return '';
|
|
@@ -186,24 +212,21 @@ export class StringFormat extends Formatter {
|
|
|
186
212
|
}
|
|
187
213
|
}
|
|
188
214
|
|
|
189
|
-
|
|
215
|
+
class PluralFormat {
|
|
190
216
|
public id: string;
|
|
191
217
|
private offset: number;
|
|
192
218
|
private options: Record<string, Pattern[]>;
|
|
193
219
|
private pluralRules: Intl.PluralRules;
|
|
194
220
|
constructor(
|
|
195
221
|
id: string,
|
|
196
|
-
useOrdinal: boolean,
|
|
197
222
|
offset: number,
|
|
198
223
|
options: Record<string, Pattern[]>,
|
|
199
|
-
|
|
224
|
+
pluralRules: Intl.PluralRules
|
|
200
225
|
) {
|
|
201
226
|
this.id = id;
|
|
202
227
|
this.offset = offset;
|
|
203
228
|
this.options = options;
|
|
204
|
-
this.pluralRules =
|
|
205
|
-
type: useOrdinal ? 'ordinal' : 'cardinal'
|
|
206
|
-
});
|
|
229
|
+
this.pluralRules = pluralRules;
|
|
207
230
|
}
|
|
208
231
|
|
|
209
232
|
getOption(value: number) {
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
|
|
3
|
+
Copyrights licensed under the New BSD License.
|
|
4
|
+
See the accompanying LICENSE file for terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* jslint esnext: true */
|
|
8
|
+
|
|
9
|
+
import Compiler, {
|
|
10
|
+
Formats,
|
|
11
|
+
isSelectOrPluralFormat,
|
|
12
|
+
Pattern,
|
|
13
|
+
Formatters
|
|
14
|
+
} from './compiler';
|
|
15
|
+
import parser, { MessageFormatPattern } from 'intl-messageformat-parser';
|
|
16
|
+
|
|
17
|
+
// -- MessageFormat --------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
function resolveLocale(locales: string | string[]): string {
|
|
20
|
+
if (typeof locales === 'string') {
|
|
21
|
+
locales = [locales];
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return Intl.NumberFormat.supportedLocalesOf(locales, {
|
|
25
|
+
// IE11 localeMatcher `lookup` seems to convert `en` -> `en-US`
|
|
26
|
+
// but not other browsers,
|
|
27
|
+
localeMatcher: 'best fit'
|
|
28
|
+
})[0];
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return IntlMessageFormat.defaultLocale;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatPatterns(
|
|
35
|
+
pattern: Pattern[],
|
|
36
|
+
values?: Record<string, string | number | boolean | null | undefined>
|
|
37
|
+
) {
|
|
38
|
+
let result = '';
|
|
39
|
+
for (const part of pattern) {
|
|
40
|
+
// Exist early for string parts.
|
|
41
|
+
if (typeof part === 'string') {
|
|
42
|
+
result += part;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { id } = part;
|
|
47
|
+
|
|
48
|
+
// Enforce that all required values are provided by the caller.
|
|
49
|
+
if (!(values && id in values)) {
|
|
50
|
+
throw new FormatError(`A value must be provided for: ${id}`, id);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const value = values[id];
|
|
54
|
+
|
|
55
|
+
// Recursively format plural and select parts' option — which can be a
|
|
56
|
+
// nested pattern structure. The choosing of the option to use is
|
|
57
|
+
// abstracted-by and delegated-to the part helper object.
|
|
58
|
+
if (isSelectOrPluralFormat(part)) {
|
|
59
|
+
result += formatPatterns(part.getOption(value as any), values);
|
|
60
|
+
} else {
|
|
61
|
+
result += part.format(value as any);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function mergeConfig(c1: Record<string, object>, c2?: Record<string, object>) {
|
|
69
|
+
if (!c2) {
|
|
70
|
+
return c1;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
...(c1 || {}),
|
|
74
|
+
...(c2 || {}),
|
|
75
|
+
...Object.keys(c1).reduce((all: Record<string, object>, k) => {
|
|
76
|
+
all[k] = {
|
|
77
|
+
...c1[k],
|
|
78
|
+
...(c2[k] || {})
|
|
79
|
+
};
|
|
80
|
+
return all;
|
|
81
|
+
}, {})
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function mergeConfigs(
|
|
86
|
+
defaultConfig: Formats,
|
|
87
|
+
configs?: Partial<Formats>
|
|
88
|
+
): Formats {
|
|
89
|
+
if (!configs) {
|
|
90
|
+
return defaultConfig;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (Object.keys(defaultConfig) as Array<keyof Formats>).reduce(
|
|
94
|
+
(all: Formats, k: keyof Formats) => {
|
|
95
|
+
all[k] = mergeConfig(defaultConfig[k], configs[k]);
|
|
96
|
+
return all;
|
|
97
|
+
},
|
|
98
|
+
{ ...defaultConfig }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class FormatError extends Error {
|
|
103
|
+
public readonly variableId?: string;
|
|
104
|
+
constructor(msg?: string, variableId?: string) {
|
|
105
|
+
super(msg);
|
|
106
|
+
this.variableId = variableId;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface Options {
|
|
111
|
+
formatters?: Formatters;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createDefaultFormatters(): Formatters {
|
|
115
|
+
return {
|
|
116
|
+
getNumberFormat(...args) {
|
|
117
|
+
return new Intl.NumberFormat(...args);
|
|
118
|
+
},
|
|
119
|
+
getDateTimeFormat(...args) {
|
|
120
|
+
return new Intl.DateTimeFormat(...args);
|
|
121
|
+
},
|
|
122
|
+
getPluralRules(...args) {
|
|
123
|
+
return new Intl.PluralRules(...args);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class IntlMessageFormat {
|
|
129
|
+
private ast: MessageFormatPattern;
|
|
130
|
+
private locale: string;
|
|
131
|
+
private pattern: Pattern[];
|
|
132
|
+
private message: string | MessageFormatPattern;
|
|
133
|
+
constructor(
|
|
134
|
+
message: string | MessageFormatPattern,
|
|
135
|
+
locales: string | string[] = IntlMessageFormat.defaultLocale,
|
|
136
|
+
overrideFormats?: Partial<Formats>,
|
|
137
|
+
opts?: Options
|
|
138
|
+
) {
|
|
139
|
+
if (typeof message === 'string') {
|
|
140
|
+
if (!IntlMessageFormat.__parse) {
|
|
141
|
+
throw new TypeError(
|
|
142
|
+
'IntlMessageFormat.__parse must be set to process `message` of type `string`'
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
// Parse string messages into an AST.
|
|
146
|
+
this.ast = IntlMessageFormat.__parse(message);
|
|
147
|
+
} else {
|
|
148
|
+
this.ast = message;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.message = message;
|
|
152
|
+
|
|
153
|
+
if (!(this.ast && this.ast.type === 'messageFormatPattern')) {
|
|
154
|
+
throw new TypeError('A message must be provided as a String or AST.');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Creates a new object with the specified `formats` merged with the default
|
|
158
|
+
// formats.
|
|
159
|
+
const formats = mergeConfigs(IntlMessageFormat.formats, overrideFormats);
|
|
160
|
+
|
|
161
|
+
// Defined first because it's used to build the format pattern.
|
|
162
|
+
this.locale = resolveLocale(locales || []);
|
|
163
|
+
|
|
164
|
+
let formatters = (opts && opts.formatters) || createDefaultFormatters();
|
|
165
|
+
|
|
166
|
+
// Compile the `ast` to a pattern that is highly optimized for repeated
|
|
167
|
+
// `format()` invocations. **Note:** This passes the `locales` set provided
|
|
168
|
+
// to the constructor instead of just the resolved locale.
|
|
169
|
+
this.pattern = new Compiler(locales, formats, formatters).compile(this.ast);
|
|
170
|
+
|
|
171
|
+
// "Bind" `format()` method to `this` so it can be passed by reference like
|
|
172
|
+
// the other `Intl` APIs.
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
format = (
|
|
176
|
+
values?: Record<string, string | number | boolean | null | undefined>
|
|
177
|
+
) => {
|
|
178
|
+
try {
|
|
179
|
+
return formatPatterns(this.pattern, values);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
if (e.variableId) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`The intl string context variable '${e.variableId}' was not provided to the string '${this.message}'`
|
|
184
|
+
);
|
|
185
|
+
} else {
|
|
186
|
+
throw e;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
resolvedOptions() {
|
|
191
|
+
return { locale: this.locale };
|
|
192
|
+
}
|
|
193
|
+
getAst() {
|
|
194
|
+
return this.ast;
|
|
195
|
+
}
|
|
196
|
+
static defaultLocale = 'en';
|
|
197
|
+
static __parse: typeof parser['parse'] | undefined = undefined;
|
|
198
|
+
// Default format options used as the prototype of the `formats` provided to the
|
|
199
|
+
// constructor. These are used when constructing the internal Intl.NumberFormat
|
|
200
|
+
// and Intl.DateTimeFormat instances.
|
|
201
|
+
static formats = {
|
|
202
|
+
number: {
|
|
203
|
+
currency: {
|
|
204
|
+
style: 'currency'
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
percent: {
|
|
208
|
+
style: 'percent'
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
date: {
|
|
213
|
+
short: {
|
|
214
|
+
month: 'numeric',
|
|
215
|
+
day: 'numeric',
|
|
216
|
+
year: '2-digit'
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
medium: {
|
|
220
|
+
month: 'short',
|
|
221
|
+
day: 'numeric',
|
|
222
|
+
year: 'numeric'
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
long: {
|
|
226
|
+
month: 'long',
|
|
227
|
+
day: 'numeric',
|
|
228
|
+
year: 'numeric'
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
full: {
|
|
232
|
+
weekday: 'long',
|
|
233
|
+
month: 'long',
|
|
234
|
+
day: 'numeric',
|
|
235
|
+
year: 'numeric'
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
time: {
|
|
240
|
+
short: {
|
|
241
|
+
hour: 'numeric',
|
|
242
|
+
minute: 'numeric'
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
medium: {
|
|
246
|
+
hour: 'numeric',
|
|
247
|
+
minute: 'numeric',
|
|
248
|
+
second: 'numeric'
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
long: {
|
|
252
|
+
hour: 'numeric',
|
|
253
|
+
minute: 'numeric',
|
|
254
|
+
second: 'numeric',
|
|
255
|
+
timeZoneName: 'short'
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
full: {
|
|
259
|
+
hour: 'numeric',
|
|
260
|
+
minute: 'numeric',
|
|
261
|
+
second: 'numeric',
|
|
262
|
+
timeZoneName: 'short'
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export { Formats, Pattern } from './compiler';
|
|
269
|
+
export default IntlMessageFormat;
|
package/src/index.ts
CHANGED
|
@@ -4,248 +4,12 @@ Copyrights licensed under the New BSD License.
|
|
|
4
4
|
See the accompanying LICENSE file for terms.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import parser from 'intl-messageformat-parser';
|
|
8
|
+
import IntlMessageFormat from './core';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
import parser, { MessageFormatPattern } from 'intl-messageformat-parser';
|
|
11
|
-
|
|
12
|
-
// -- MessageFormat --------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
function resolveLocale(locales: string | string[]): string {
|
|
15
|
-
if (typeof locales === 'string') {
|
|
16
|
-
locales = [locales];
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
return Intl.NumberFormat.supportedLocalesOf(locales, {
|
|
20
|
-
// IE11 localeMatcher `lookup` seems to convert `en` -> `en-US`
|
|
21
|
-
// but not other browsers,
|
|
22
|
-
localeMatcher: 'best fit'
|
|
23
|
-
})[0];
|
|
24
|
-
} catch (e) {
|
|
25
|
-
return MessageFormat.defaultLocale;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function formatPatterns(
|
|
30
|
-
pattern: Pattern[],
|
|
31
|
-
values?: Record<string, string | number | boolean | null | undefined>
|
|
32
|
-
) {
|
|
33
|
-
let result = '';
|
|
34
|
-
for (const part of pattern) {
|
|
35
|
-
// Exist early for string parts.
|
|
36
|
-
if (typeof part === 'string') {
|
|
37
|
-
result += part;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { id } = part;
|
|
42
|
-
|
|
43
|
-
// Enforce that all required values are provided by the caller.
|
|
44
|
-
if (!(values && id in values)) {
|
|
45
|
-
throw new FormatError(`A value must be provided for: ${id}`, id);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const value = values[id];
|
|
49
|
-
|
|
50
|
-
// Recursively format plural and select parts' option — which can be a
|
|
51
|
-
// nested pattern structure. The choosing of the option to use is
|
|
52
|
-
// abstracted-by and delegated-to the part helper object.
|
|
53
|
-
if (isSelectOrPluralFormat(part)) {
|
|
54
|
-
result += formatPatterns(part.getOption(value as any), values);
|
|
55
|
-
} else {
|
|
56
|
-
result += part.format(value as any);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function mergeConfig(c1: Record<string, object>, c2?: Record<string, object>) {
|
|
64
|
-
if (!c2) {
|
|
65
|
-
return c1;
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
...(c1 || {}),
|
|
69
|
-
...(c2 || {}),
|
|
70
|
-
...Object.keys(c1).reduce((all: Record<string, object>, k) => {
|
|
71
|
-
all[k] = {
|
|
72
|
-
...c1[k],
|
|
73
|
-
...(c2[k] || {})
|
|
74
|
-
};
|
|
75
|
-
return all;
|
|
76
|
-
}, {})
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function mergeConfigs(
|
|
81
|
-
defaultConfig: Formats,
|
|
82
|
-
configs?: Partial<Formats>
|
|
83
|
-
): Formats {
|
|
84
|
-
if (!configs) {
|
|
85
|
-
return defaultConfig;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return (Object.keys(defaultConfig) as Array<keyof Formats>).reduce(
|
|
89
|
-
(all: Formats, k: keyof Formats) => {
|
|
90
|
-
all[k] = mergeConfig(defaultConfig[k], configs[k]);
|
|
91
|
-
return all;
|
|
92
|
-
},
|
|
93
|
-
{ ...defaultConfig }
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
class FormatError extends Error {
|
|
98
|
-
public readonly variableId?: string;
|
|
99
|
-
constructor(msg?: string, variableId?: string) {
|
|
100
|
-
super(msg);
|
|
101
|
-
this.variableId = variableId;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface IntlMessageFormat {
|
|
106
|
-
new (
|
|
107
|
-
message: string | MessageFormatPattern,
|
|
108
|
-
locales?: string | string[],
|
|
109
|
-
overrideFormats?: Partial<Formats>
|
|
110
|
-
): IntlMessageFormat;
|
|
111
|
-
(
|
|
112
|
-
message: string | MessageFormatPattern,
|
|
113
|
-
locales?: string | string[],
|
|
114
|
-
overrideFormats?: Partial<Formats>
|
|
115
|
-
): IntlMessageFormat;
|
|
116
|
-
format(
|
|
117
|
-
values?: Record<string, string | number | boolean | null | undefined>
|
|
118
|
-
): string;
|
|
119
|
-
resolvedOptions(): { locale: string };
|
|
120
|
-
getAst(): ReturnType<typeof parser['parse']>;
|
|
121
|
-
defaultLocale: string;
|
|
122
|
-
formats: Formats;
|
|
123
|
-
__parse: typeof parser['parse'];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const MessageFormat: IntlMessageFormat = ((
|
|
127
|
-
message: string | MessageFormatPattern,
|
|
128
|
-
locales: string | string[] = MessageFormat.defaultLocale,
|
|
129
|
-
overrideFormats?: Partial<Formats>
|
|
130
|
-
) => {
|
|
131
|
-
// Parse string messages into an AST.
|
|
132
|
-
const ast =
|
|
133
|
-
typeof message === 'string' ? MessageFormat.__parse(message) : message;
|
|
134
|
-
|
|
135
|
-
if (!(ast && ast.type === 'messageFormatPattern')) {
|
|
136
|
-
throw new TypeError('A message must be provided as a String or AST.');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Creates a new object with the specified `formats` merged with the default
|
|
140
|
-
// formats.
|
|
141
|
-
const formats = mergeConfigs(MessageFormat.formats, overrideFormats);
|
|
142
|
-
|
|
143
|
-
// Defined first because it's used to build the format pattern.
|
|
144
|
-
const locale = resolveLocale(locales || []);
|
|
145
|
-
|
|
146
|
-
// Compile the `ast` to a pattern that is highly optimized for repeated
|
|
147
|
-
// `format()` invocations. **Note:** This passes the `locales` set provided
|
|
148
|
-
// to the constructor instead of just the resolved locale.
|
|
149
|
-
const pattern = new Compiler(locales, formats).compile(ast);
|
|
150
|
-
|
|
151
|
-
// "Bind" `format()` method to `this` so it can be passed by reference like
|
|
152
|
-
// the other `Intl` APIs.
|
|
153
|
-
return {
|
|
154
|
-
format(
|
|
155
|
-
values?: Record<string, string | number | boolean | null | undefined>
|
|
156
|
-
) {
|
|
157
|
-
try {
|
|
158
|
-
return formatPatterns(pattern, values);
|
|
159
|
-
} catch (e) {
|
|
160
|
-
if (e.variableId) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
`The intl string context variable '${e.variableId}' was not provided to the string '${message}'`
|
|
163
|
-
);
|
|
164
|
-
} else {
|
|
165
|
-
throw e;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
resolvedOptions() {
|
|
170
|
-
return { locale };
|
|
171
|
-
},
|
|
172
|
-
getAst() {
|
|
173
|
-
return ast;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
}) as any;
|
|
177
|
-
|
|
178
|
-
MessageFormat.defaultLocale = 'en';
|
|
179
|
-
// Default format options used as the prototype of the `formats` provided to the
|
|
180
|
-
// constructor. These are used when constructing the internal Intl.NumberFormat
|
|
181
|
-
// and Intl.DateTimeFormat instances.
|
|
182
|
-
MessageFormat.formats = {
|
|
183
|
-
number: {
|
|
184
|
-
currency: {
|
|
185
|
-
style: 'currency'
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
percent: {
|
|
189
|
-
style: 'percent'
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
date: {
|
|
194
|
-
short: {
|
|
195
|
-
month: 'numeric',
|
|
196
|
-
day: 'numeric',
|
|
197
|
-
year: '2-digit'
|
|
198
|
-
},
|
|
199
|
-
|
|
200
|
-
medium: {
|
|
201
|
-
month: 'short',
|
|
202
|
-
day: 'numeric',
|
|
203
|
-
year: 'numeric'
|
|
204
|
-
},
|
|
205
|
-
|
|
206
|
-
long: {
|
|
207
|
-
month: 'long',
|
|
208
|
-
day: 'numeric',
|
|
209
|
-
year: 'numeric'
|
|
210
|
-
},
|
|
211
|
-
|
|
212
|
-
full: {
|
|
213
|
-
weekday: 'long',
|
|
214
|
-
month: 'long',
|
|
215
|
-
day: 'numeric',
|
|
216
|
-
year: 'numeric'
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
|
|
220
|
-
time: {
|
|
221
|
-
short: {
|
|
222
|
-
hour: 'numeric',
|
|
223
|
-
minute: 'numeric'
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
medium: {
|
|
227
|
-
hour: 'numeric',
|
|
228
|
-
minute: 'numeric',
|
|
229
|
-
second: 'numeric'
|
|
230
|
-
},
|
|
231
|
-
|
|
232
|
-
long: {
|
|
233
|
-
hour: 'numeric',
|
|
234
|
-
minute: 'numeric',
|
|
235
|
-
second: 'numeric',
|
|
236
|
-
timeZoneName: 'short'
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
full: {
|
|
240
|
-
hour: 'numeric',
|
|
241
|
-
minute: 'numeric',
|
|
242
|
-
second: 'numeric',
|
|
243
|
-
timeZoneName: 'short'
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
MessageFormat.__parse = parser.parse;
|
|
10
|
+
IntlMessageFormat.__parse = parser.parse;
|
|
249
11
|
|
|
250
12
|
export { Formats, Pattern } from './compiler';
|
|
251
|
-
export
|
|
13
|
+
export * from './core';
|
|
14
|
+
export { Formatters } from './compiler';
|
|
15
|
+
export default IntlMessageFormat;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"uuid":"34ec6f1e-d2e9-445f-8813-bd6e8b5975bb","parent":null,"pid":5446,"argv":["/home/longho/.nvm/versions/node/v12.3.1/bin/node","/home/longho/.nvm/versions/node/v12.3.1/bin/npm","t"],"execArgv":[],"cwd":"/mnt/c/src/formatjs/packages/intl-messageformat","time":1559351011753,"ppid":5429,"root":"94cf06ed-2315-4819-86e9-a1ae5ab2cbb0","coverageFilename":"/mnt/c/src/formatjs/packages/intl-messageformat/.nyc_output/34ec6f1e-d2e9-445f-8813-bd6e8b5975bb.json","files":[]}
|