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