@ultraq/icu-message-formatter 0.12.0 → 0.13.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 +6 -0
- package/README.md +3 -2
- package/dist/icu-message-formatter.es.min.js +1 -1
- package/dist/icu-message-formatter.es.min.js.map +1 -1
- package/dist/icu-message-formatter.min.js +1 -1
- package/dist/icu-message-formatter.min.js.map +1 -1
- package/index.d.ts +5 -0
- package/lib/icu-message-formatter.cjs.js +222 -244
- package/lib/icu-message-formatter.cjs.js.map +1 -1
- package/lib/icu-message-formatter.es.js +221 -234
- package/lib/icu-message-formatter.es.js.map +1 -1
- package/package.json +31 -23
- package/rollup.config.dist.js +1 -2
- package/source/IcuMessageFormatter.js +16 -5
- package/source/MessageFormatter.js +35 -9
- package/source/MessageFormatter.test.js +8 -8
- package/source/pluralTypeHandler.js +14 -14
- package/source/pluralTypeHandler.test.js +17 -17
- package/source/selectTypeHandler.js +10 -10
- package/source/selectTypeHandler.test.js +3 -3
- package/source/utilities.js +29 -21
- package/source/utilities.test.js +6 -6
- package/types/IcuMessageFormatter.d.ts +4 -0
- package/types/MessageFormatter.d.ts +71 -0
- package/types/pluralTypeHandler.d.ts +15 -0
- package/types/selectTypeHandler.d.ts +15 -0
- package/types/typedefs.d.ts +3 -0
- package/types/utilities.d.ts +52 -0
@@ -1,21 +1,9 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
4
|
-
|
5
|
-
var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
|
6
|
-
var _classCallCheck = require('@babel/runtime/helpers/classCallCheck');
|
7
|
-
var _createClass = require('@babel/runtime/helpers/createClass');
|
8
3
|
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
|
9
4
|
var arrayUtils = require('@ultraq/array-utils');
|
10
5
|
var functionUtils = require('@ultraq/function-utils');
|
11
6
|
|
12
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
13
|
-
|
14
|
-
var _slicedToArray__default = /*#__PURE__*/_interopDefaultLegacy(_slicedToArray);
|
15
|
-
var _classCallCheck__default = /*#__PURE__*/_interopDefaultLegacy(_classCallCheck);
|
16
|
-
var _createClass__default = /*#__PURE__*/_interopDefaultLegacy(_createClass);
|
17
|
-
var _defineProperty__default = /*#__PURE__*/_interopDefaultLegacy(_defineProperty);
|
18
|
-
|
19
7
|
/*
|
20
8
|
* Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)
|
21
9
|
*
|
@@ -33,389 +21,380 @@ var _defineProperty__default = /*#__PURE__*/_interopDefaultLegacy(_definePropert
|
|
33
21
|
*/
|
34
22
|
|
35
23
|
/**
|
36
|
-
*
|
37
|
-
*
|
38
|
-
*
|
24
|
+
* @typedef ParseCasesResult
|
25
|
+
* @property {string[]} args
|
26
|
+
* A list of prepended arguments.
|
27
|
+
* @property {Record<string,string>} cases
|
28
|
+
* A map of all cases.
|
29
|
+
*/
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Most branch-based type handlers are based around "cases". For example,
|
33
|
+
* `select` and `plural` compare compare a value to "case keys" to choose a
|
34
|
+
* subtranslation.
|
39
35
|
*
|
40
|
-
* This util splits "matches" portions provided to the aforementioned
|
41
|
-
*
|
42
|
-
*
|
43
|
-
*
|
36
|
+
* This util splits "matches" portions provided to the aforementioned handlers
|
37
|
+
* into case strings, and extracts any prepended arguments (for example,
|
38
|
+
* `plural` supports an `offset:n` argument used for populating the magic `#`
|
39
|
+
* variable).
|
44
40
|
*
|
45
|
-
* @param {
|
46
|
-
* @return {
|
47
|
-
* The `arguments` key points to a list of prepended arguments.
|
41
|
+
* @param {string} string
|
42
|
+
* @return {ParseCasesResult}
|
48
43
|
*/
|
49
44
|
function parseCases(string) {
|
50
|
-
|
51
|
-
|
52
|
-
};
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
var latestTerm = null;
|
58
|
-
var inTerm = false;
|
59
|
-
var i = 0;
|
60
|
-
|
45
|
+
const isWhitespace = ch => /\s/.test(ch);
|
46
|
+
const args = [];
|
47
|
+
const cases = {};
|
48
|
+
let currTermStart = 0;
|
49
|
+
let latestTerm = null;
|
50
|
+
let inTerm = false;
|
51
|
+
let i = 0;
|
61
52
|
while (i < string.length) {
|
62
53
|
// Term ended
|
63
54
|
if (inTerm && (isWhitespace(string[i]) || string[i] === '{')) {
|
64
55
|
inTerm = false;
|
65
|
-
latestTerm = string.slice(currTermStart, i);
|
56
|
+
latestTerm = string.slice(currTermStart, i);
|
66
57
|
|
58
|
+
// We want to process the opening char again so the case will be properly registered.
|
67
59
|
if (string[i] === '{') {
|
68
60
|
i--;
|
69
61
|
}
|
70
|
-
}
|
62
|
+
}
|
63
|
+
|
64
|
+
// New term
|
71
65
|
else if (!inTerm && !isWhitespace(string[i])) {
|
72
|
-
|
73
|
-
// case, or add that as an argument.
|
66
|
+
const caseBody = string[i] === '{';
|
74
67
|
|
68
|
+
// If there's a previous term, we can either handle a whole
|
69
|
+
// case, or add that as an argument.
|
75
70
|
if (latestTerm && caseBody) {
|
76
|
-
|
77
|
-
|
71
|
+
const branchEndIndex = findClosingBracket(string, i);
|
78
72
|
if (branchEndIndex === -1) {
|
79
|
-
throw new Error(
|
73
|
+
throw new Error(`Unbalanced curly braces in string: "${string}"`);
|
80
74
|
}
|
81
|
-
|
82
75
|
cases[latestTerm] = string.slice(i + 1, branchEndIndex); // Don't include the braces
|
83
76
|
|
84
77
|
i = branchEndIndex; // Will be moved up where needed at end of loop.
|
85
|
-
|
86
78
|
latestTerm = null;
|
87
79
|
} else {
|
88
80
|
if (latestTerm) {
|
89
81
|
args.push(latestTerm);
|
90
82
|
latestTerm = null;
|
91
83
|
}
|
92
|
-
|
93
84
|
inTerm = true;
|
94
85
|
currTermStart = i;
|
95
86
|
}
|
96
87
|
}
|
97
|
-
|
98
88
|
i++;
|
99
89
|
}
|
100
|
-
|
101
90
|
if (inTerm) {
|
102
91
|
latestTerm = string.slice(currTermStart);
|
103
92
|
}
|
104
|
-
|
105
93
|
if (latestTerm) {
|
106
94
|
args.push(latestTerm);
|
107
95
|
}
|
108
|
-
|
109
96
|
return {
|
110
|
-
args
|
111
|
-
cases
|
97
|
+
args,
|
98
|
+
cases
|
112
99
|
};
|
113
100
|
}
|
101
|
+
|
114
102
|
/**
|
115
103
|
* Finds the index of the matching closing curly bracket, including through
|
116
104
|
* strings that could have nested brackets.
|
117
105
|
*
|
118
|
-
* @param {
|
119
|
-
* @param {
|
120
|
-
* @return {
|
121
|
-
* closing bracket
|
106
|
+
* @param {string} string
|
107
|
+
* @param {number} fromIndex
|
108
|
+
* @return {number}
|
109
|
+
* The index of the matching closing bracket, or -1 if no closing bracket
|
110
|
+
* could be found.
|
122
111
|
*/
|
123
|
-
|
124
112
|
function findClosingBracket(string, fromIndex) {
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
var char = string.charAt(i);
|
129
|
-
|
113
|
+
let depth = 0;
|
114
|
+
for (let i = fromIndex + 1; i < string.length; i++) {
|
115
|
+
let char = string.charAt(i);
|
130
116
|
if (char === '}') {
|
131
117
|
if (depth === 0) {
|
132
118
|
return i;
|
133
119
|
}
|
134
|
-
|
135
120
|
depth--;
|
136
121
|
} else if (char === '{') {
|
137
122
|
depth++;
|
138
123
|
}
|
139
124
|
}
|
140
|
-
|
141
125
|
return -1;
|
142
126
|
}
|
127
|
+
|
143
128
|
/**
|
144
129
|
* Split a `{key, type, format}` block into those 3 parts, taking into account
|
145
130
|
* nested message syntax that can exist in the `format` part.
|
146
131
|
*
|
147
|
-
* @param {
|
148
|
-
* @return {
|
132
|
+
* @param {string} block
|
133
|
+
* @return {string[]}
|
149
134
|
* An array with `key`, `type`, and `format` items in that order, if present
|
150
135
|
* in the formatted argument block.
|
151
136
|
*/
|
152
|
-
|
153
137
|
function splitFormattedArgument(block) {
|
154
138
|
return split(block.slice(1, -1), ',', 3);
|
155
139
|
}
|
140
|
+
|
156
141
|
/**
|
157
142
|
* Like `String.prototype.split()` but where the limit parameter causes the
|
158
143
|
* remainder of the string to be grouped together in a final entry.
|
159
144
|
*
|
160
145
|
* @private
|
161
|
-
* @param {
|
162
|
-
* @param {
|
163
|
-
* @param {
|
164
|
-
* @param {
|
165
|
-
* @return {
|
146
|
+
* @param {string} string
|
147
|
+
* @param {string} separator
|
148
|
+
* @param {number} limit
|
149
|
+
* @param {string[]} accumulator
|
150
|
+
* @return {string[]}
|
166
151
|
*/
|
167
|
-
|
168
152
|
function split(string, separator, limit) {
|
169
|
-
|
170
|
-
|
153
|
+
let accumulator = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
171
154
|
if (!string) {
|
172
155
|
return accumulator;
|
173
156
|
}
|
174
|
-
|
175
157
|
if (limit === 1) {
|
176
158
|
accumulator.push(string);
|
177
159
|
return accumulator;
|
178
160
|
}
|
179
|
-
|
180
|
-
var indexOfDelimiter = string.indexOf(separator);
|
181
|
-
|
161
|
+
let indexOfDelimiter = string.indexOf(separator);
|
182
162
|
if (indexOfDelimiter === -1) {
|
183
163
|
accumulator.push(string);
|
184
164
|
return accumulator;
|
185
165
|
}
|
186
|
-
|
187
|
-
|
188
|
-
var tail = string.substring(indexOfDelimiter + separator.length + 1).trim();
|
166
|
+
let head = string.substring(0, indexOfDelimiter).trim();
|
167
|
+
let tail = string.substring(indexOfDelimiter + separator.length + 1).trim();
|
189
168
|
accumulator.push(head);
|
190
169
|
return split(tail, separator, limit - 1, accumulator);
|
191
170
|
}
|
192
171
|
|
172
|
+
/**
|
173
|
+
* @typedef {Record<string,any>} FormatValues
|
174
|
+
*/
|
175
|
+
|
176
|
+
/**
|
177
|
+
* @callback ProcessFunction
|
178
|
+
* @param {string} message
|
179
|
+
* @param {FormatValues} [values={}]
|
180
|
+
* @return {any[]}
|
181
|
+
*/
|
182
|
+
|
183
|
+
/**
|
184
|
+
* @callback TypeHandler
|
185
|
+
* @param {any} value
|
186
|
+
* The object which matched the key of the block being processed.
|
187
|
+
* @param {string} matches
|
188
|
+
* Any format options associated with the block being processed.
|
189
|
+
* @param {string} locale
|
190
|
+
* The locale to use for formatting.
|
191
|
+
* @param {FormatValues} values
|
192
|
+
* The object of placeholder data given to the original `format`/`process`
|
193
|
+
* call.
|
194
|
+
* @param {ProcessFunction} process
|
195
|
+
* The `process` function itself so that sub-messages can be processed by type
|
196
|
+
* handlers.
|
197
|
+
* @return {any | any[]}
|
198
|
+
*/
|
199
|
+
|
193
200
|
/**
|
194
201
|
* The main class for formatting messages.
|
195
202
|
*
|
196
203
|
* @author Emanuel Rabina
|
197
204
|
*/
|
198
|
-
|
199
|
-
var MessageFormatter = /*#__PURE__*/function () {
|
205
|
+
class MessageFormatter {
|
200
206
|
/**
|
201
207
|
* Creates a new formatter that can work using any of the custom type handlers
|
202
208
|
* you register.
|
203
209
|
*
|
204
|
-
* @param {
|
205
|
-
* @param {
|
210
|
+
* @param {string} locale
|
211
|
+
* @param {Record<string,TypeHandler>} [typeHandlers]
|
206
212
|
* Optional object where the keys are the names of the types to register,
|
207
213
|
* their values being the functions that will return a nicely formatted
|
208
214
|
* string for the data and locale they are given.
|
209
215
|
*/
|
210
|
-
|
216
|
+
constructor(locale) {
|
211
217
|
var _this = this;
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
218
|
+
let typeHandlers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
219
|
+
/**
|
220
|
+
* Formats an ICU message syntax string using `values` for placeholder data
|
221
|
+
* and any currently-registered type handlers.
|
222
|
+
*
|
223
|
+
* @type {(message: string, values?: FormatValues) => string}
|
224
|
+
*/
|
225
|
+
_defineProperty(this, "format", functionUtils.memoize(function (message) {
|
226
|
+
let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
219
227
|
return arrayUtils.flatten(_this.process(message, values)).join('');
|
220
228
|
}));
|
221
|
-
|
222
229
|
this.locale = locale;
|
223
230
|
this.typeHandlers = typeHandlers;
|
224
231
|
}
|
225
232
|
/**
|
226
|
-
*
|
227
|
-
* and any currently-registered type handlers.
|
233
|
+
* Process an ICU message syntax string using `values` for placeholder data
|
234
|
+
* and any currently-registered type handlers. The result of this method is
|
235
|
+
* an array of the component parts after they have been processed in turn by
|
236
|
+
* their own type handlers. This raw output is useful for other renderers,
|
237
|
+
* eg: React where components can be used instead of being forced to return
|
238
|
+
* raw strings.
|
228
239
|
*
|
229
|
-
* @
|
230
|
-
*
|
231
|
-
*
|
240
|
+
* This method is used by {@link MessageFormatter#format} where it acts as a
|
241
|
+
* string renderer.
|
242
|
+
*
|
243
|
+
* @param {string} message
|
244
|
+
* @param {FormatValues} [values]
|
245
|
+
* @return {any[]}
|
232
246
|
*/
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
function process(message) {
|
254
|
-
var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
255
|
-
|
256
|
-
if (!message) {
|
257
|
-
return [];
|
258
|
-
}
|
259
|
-
|
260
|
-
var blockStartIndex = message.indexOf('{');
|
261
|
-
|
262
|
-
if (blockStartIndex !== -1) {
|
263
|
-
var blockEndIndex = findClosingBracket(message, blockStartIndex);
|
264
|
-
|
265
|
-
if (blockEndIndex !== -1) {
|
266
|
-
var block = message.substring(blockStartIndex, blockEndIndex + 1);
|
267
|
-
|
268
|
-
if (block) {
|
269
|
-
var result = [];
|
270
|
-
var head = message.substring(0, blockStartIndex);
|
271
|
-
|
272
|
-
if (head) {
|
273
|
-
result.push(head);
|
274
|
-
}
|
275
|
-
|
276
|
-
var _splitFormattedArgume = splitFormattedArgument(block),
|
277
|
-
_splitFormattedArgume2 = _slicedToArray__default["default"](_splitFormattedArgume, 3),
|
278
|
-
key = _splitFormattedArgume2[0],
|
279
|
-
type = _splitFormattedArgume2[1],
|
280
|
-
format = _splitFormattedArgume2[2];
|
281
|
-
|
282
|
-
var body = values[key];
|
283
|
-
|
284
|
-
if (body === null || body === undefined) {
|
285
|
-
body = '';
|
286
|
-
}
|
287
|
-
|
288
|
-
var typeHandler = type && this.typeHandlers[type];
|
289
|
-
result.push(typeHandler ? typeHandler(body, format, this.locale, values, this.process.bind(this)) : body);
|
290
|
-
var tail = message.substring(blockEndIndex + 1);
|
291
|
-
|
292
|
-
if (tail) {
|
293
|
-
result.push(this.process(tail, values));
|
294
|
-
}
|
295
|
-
|
296
|
-
return result;
|
247
|
+
process(message) {
|
248
|
+
let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
249
|
+
if (!message) {
|
250
|
+
return [];
|
251
|
+
}
|
252
|
+
let blockStartIndex = message.indexOf('{');
|
253
|
+
if (blockStartIndex !== -1) {
|
254
|
+
let blockEndIndex = findClosingBracket(message, blockStartIndex);
|
255
|
+
if (blockEndIndex !== -1) {
|
256
|
+
let block = message.substring(blockStartIndex, blockEndIndex + 1);
|
257
|
+
if (block) {
|
258
|
+
let result = [];
|
259
|
+
let head = message.substring(0, blockStartIndex);
|
260
|
+
if (head) {
|
261
|
+
result.push(head);
|
262
|
+
}
|
263
|
+
let [key, type, format] = splitFormattedArgument(block);
|
264
|
+
let body = values[key];
|
265
|
+
if (body === null || body === undefined) {
|
266
|
+
body = '';
|
297
267
|
}
|
298
|
-
|
299
|
-
|
268
|
+
let typeHandler = type && this.typeHandlers[type];
|
269
|
+
result.push(typeHandler ? typeHandler(body, format, this.locale, values, this.process.bind(this)) : body);
|
270
|
+
let tail = message.substring(blockEndIndex + 1);
|
271
|
+
if (tail) {
|
272
|
+
result.push(this.process(tail, values));
|
273
|
+
}
|
274
|
+
return result;
|
300
275
|
}
|
276
|
+
} else {
|
277
|
+
throw new Error(`Unbalanced curly braces in string: "${message}"`);
|
301
278
|
}
|
302
|
-
|
303
|
-
return [message];
|
304
279
|
}
|
305
|
-
|
280
|
+
return [message];
|
281
|
+
}
|
282
|
+
}
|
306
283
|
|
307
|
-
|
308
|
-
|
284
|
+
/*
|
285
|
+
* Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)
|
286
|
+
*
|
287
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
288
|
+
* you may not use this file except in compliance with the License.
|
289
|
+
* You may obtain a copy of the License at
|
290
|
+
*
|
291
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
292
|
+
*
|
293
|
+
* Unless required by applicable law or agreed to in writing, software
|
294
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
295
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
296
|
+
* See the License for the specific language governing permissions and
|
297
|
+
* limitations under the License.
|
298
|
+
*/
|
309
299
|
|
310
|
-
|
300
|
+
let pluralFormatter;
|
301
|
+
let keyCounter = 0;
|
311
302
|
|
312
|
-
|
313
|
-
|
314
|
-
|
303
|
+
// All the special keywords that can be used in `plural` blocks for the various branches
|
304
|
+
const ONE = 'one';
|
305
|
+
const OTHER$1 = 'other';
|
315
306
|
|
316
|
-
var ONE = 'one';
|
317
|
-
var OTHER$1 = 'other';
|
318
307
|
/**
|
319
308
|
* @private
|
320
|
-
* @param {
|
321
|
-
* @param {
|
322
|
-
* @return {
|
309
|
+
* @param {string} caseBody
|
310
|
+
* @param {number} value
|
311
|
+
* @return {{caseBody: string, numberValues: object}}
|
323
312
|
*/
|
324
|
-
|
325
313
|
function replaceNumberSign(caseBody, value) {
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
314
|
+
let i = 0;
|
315
|
+
let output = '';
|
316
|
+
let numBraces = 0;
|
317
|
+
const numberValues = {};
|
331
318
|
while (i < caseBody.length) {
|
332
319
|
if (caseBody[i] === '#' && !numBraces) {
|
333
|
-
|
334
|
-
output +=
|
320
|
+
let keyParam = `__hashToken${keyCounter++}`;
|
321
|
+
output += `{${keyParam}, number}`;
|
335
322
|
numberValues[keyParam] = value;
|
336
323
|
} else {
|
337
324
|
output += caseBody[i];
|
338
325
|
}
|
339
|
-
|
340
326
|
if (caseBody[i] === '{') {
|
341
327
|
numBraces++;
|
342
328
|
} else if (caseBody[i] === '}') {
|
343
329
|
numBraces--;
|
344
330
|
}
|
345
|
-
|
346
331
|
i++;
|
347
332
|
}
|
348
|
-
|
349
333
|
return {
|
350
334
|
caseBody: output,
|
351
|
-
numberValues
|
335
|
+
numberValues
|
352
336
|
};
|
353
337
|
}
|
338
|
+
|
354
339
|
/**
|
355
340
|
* Handler for `plural` statements within ICU message syntax strings. Returns
|
356
341
|
* a formatted string for the branch that closely matches the current value.
|
357
342
|
*
|
358
343
|
* See https://formatjs.io/docs/core-concepts/icu-syntax#plural-format for more
|
359
344
|
* details on how the `plural` statement works.
|
360
|
-
*
|
361
|
-
* @param {
|
362
|
-
* @param {
|
363
|
-
* @param {
|
364
|
-
* @param {
|
365
|
-
* @param {
|
366
|
-
* @return {
|
345
|
+
*
|
346
|
+
* @param {string} value
|
347
|
+
* @param {string} matches
|
348
|
+
* @param {string} locale
|
349
|
+
* @param {Record<string,any>} values
|
350
|
+
* @param {(message: string, values?: Record<string,any>) => any[]} process
|
351
|
+
* @return {any | any[]}
|
367
352
|
*/
|
368
|
-
|
369
|
-
|
370
353
|
function pluralTypeHandler(value) {
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
args.forEach(function (arg) {
|
354
|
+
let matches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
355
|
+
let locale = arguments.length > 2 ? arguments[2] : undefined;
|
356
|
+
let values = arguments.length > 3 ? arguments[3] : undefined;
|
357
|
+
let process = arguments.length > 4 ? arguments[4] : undefined;
|
358
|
+
const {
|
359
|
+
args,
|
360
|
+
cases
|
361
|
+
} = parseCases(matches);
|
362
|
+
let intValue = parseInt(value);
|
363
|
+
args.forEach(arg => {
|
382
364
|
if (arg.startsWith('offset:')) {
|
383
365
|
intValue -= parseInt(arg.slice('offset:'.length));
|
384
366
|
}
|
385
367
|
});
|
386
|
-
|
387
|
-
|
368
|
+
const keywordPossibilities = [];
|
388
369
|
if ('PluralRules' in Intl) {
|
389
370
|
// Effectively memoize because instantiation of `Int.*` objects is expensive.
|
390
371
|
if (pluralFormatter === undefined || pluralFormatter.resolvedOptions().locale !== locale) {
|
391
372
|
pluralFormatter = new Intl.PluralRules(locale);
|
392
373
|
}
|
374
|
+
const pluralKeyword = pluralFormatter.select(intValue);
|
393
375
|
|
394
|
-
|
395
|
-
|
376
|
+
// Other is always added last with least priority, so we don't want to add it here.
|
396
377
|
if (pluralKeyword !== OTHER$1) {
|
397
378
|
keywordPossibilities.push(pluralKeyword);
|
398
379
|
}
|
399
380
|
}
|
400
|
-
|
401
381
|
if (intValue === 1) {
|
402
382
|
keywordPossibilities.push(ONE);
|
403
383
|
}
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
for (var i = 0; i < keywordPossibilities.length; i++) {
|
408
|
-
var keyword = keywordPossibilities[i];
|
409
|
-
|
384
|
+
keywordPossibilities.push(`=${intValue}`, OTHER$1);
|
385
|
+
for (let i = 0; i < keywordPossibilities.length; i++) {
|
386
|
+
const keyword = keywordPossibilities[i];
|
410
387
|
if (keyword in cases) {
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
return
|
388
|
+
const {
|
389
|
+
caseBody,
|
390
|
+
numberValues
|
391
|
+
} = replaceNumberSign(cases[keyword], intValue);
|
392
|
+
return process(caseBody, {
|
393
|
+
...values,
|
394
|
+
...numberValues
|
395
|
+
});
|
416
396
|
}
|
417
397
|
}
|
418
|
-
|
419
398
|
return value;
|
420
399
|
}
|
421
400
|
|
@@ -434,7 +413,9 @@ function pluralTypeHandler(value) {
|
|
434
413
|
* See the License for the specific language governing permissions and
|
435
414
|
* limitations under the License.
|
436
415
|
*/
|
437
|
-
|
416
|
+
|
417
|
+
const OTHER = 'other';
|
418
|
+
|
438
419
|
/**
|
439
420
|
* Handler for `select` statements within ICU message syntax strings. Returns
|
440
421
|
* a formatted string for the branch that closely matches the current value.
|
@@ -442,28 +423,25 @@ var OTHER = 'other';
|
|
442
423
|
* See https://formatjs.io/docs/core-concepts/icu-syntax#select-format for more
|
443
424
|
* details on how the `select` statement works.
|
444
425
|
*
|
445
|
-
* @param {
|
446
|
-
* @param {
|
447
|
-
* @param {
|
448
|
-
* @param {
|
449
|
-
* @param {
|
450
|
-
* @return {
|
426
|
+
* @param {string} value
|
427
|
+
* @param {string} matches
|
428
|
+
* @param {string} locale
|
429
|
+
* @param {Record<string,any>} values
|
430
|
+
* @param {(message: string, values?: Record<string,any>) => any[]} process
|
431
|
+
* @return {any | any[]}
|
451
432
|
*/
|
452
|
-
|
453
433
|
function selectTypeHandler(value) {
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
434
|
+
let matches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
435
|
+
let values = arguments.length > 3 ? arguments[3] : undefined;
|
436
|
+
let process = arguments.length > 4 ? arguments[4] : undefined;
|
437
|
+
const {
|
438
|
+
cases
|
439
|
+
} = parseCases(matches);
|
461
440
|
if (value in cases) {
|
462
|
-
return
|
441
|
+
return process(cases[value], values);
|
463
442
|
} else if (OTHER in cases) {
|
464
|
-
return
|
443
|
+
return process(cases[OTHER], values);
|
465
444
|
}
|
466
|
-
|
467
445
|
return value;
|
468
446
|
}
|
469
447
|
|