intl-messageformat 10.1.1 → 10.1.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/BUILD +126 -0
- package/CHANGELOG.md +1141 -0
- package/LICENSE.md +0 -0
- package/README.md +0 -0
- package/index.ts +11 -0
- package/package.json +5 -5
- package/src/core.ts +294 -0
- package/src/error.ts +65 -0
- package/src/formatters.ts +311 -0
- package/tests/benchmark.ts +103 -0
- package/tests/index.test.ts +968 -0
- package/tsconfig.json +5 -0
- package/index.d.ts +0 -6
- package/index.d.ts.map +0 -1
- package/index.js +0 -13
- package/intl-messageformat.esm.js +0 -3717
- package/intl-messageformat.iife.js +0 -3788
- package/lib/index.d.ts +0 -6
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -10
- package/lib/src/core.d.ts +0 -34
- package/lib/src/core.d.ts.map +0 -1
- package/lib/src/core.js +0 -240
- package/lib/src/error.d.ts +0 -28
- package/lib/src/error.d.ts.map +0 -1
- package/lib/src/error.js +0 -48
- package/lib/src/formatters.d.ts +0 -47
- package/lib/src/formatters.d.ts.map +0 -1
- package/lib/src/formatters.js +0 -177
- package/src/core.d.ts +0 -34
- package/src/core.d.ts.map +0 -1
- package/src/core.js +0 -243
- package/src/error.d.ts +0 -28
- package/src/error.d.ts.map +0 -1
- package/src/error.js +0 -51
- package/src/formatters.d.ts +0 -47
- package/src/formatters.d.ts.map +0 -1
- package/src/formatters.js +0 -182
|
@@ -0,0 +1,968 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2011-2013, Yahoo! Inc. All rights reserved.
|
|
3
|
+
* Copyrights licensed under the New BSD License.
|
|
4
|
+
* See the accompanying LICENSE file for terms.
|
|
5
|
+
*/
|
|
6
|
+
import {IntlMessageFormat} from '../src/core'
|
|
7
|
+
import {PART_TYPE} from '../src/formatters'
|
|
8
|
+
import {parse} from '@formatjs/icu-messageformat-parser'
|
|
9
|
+
import 'jasmine-expect'
|
|
10
|
+
|
|
11
|
+
describe('IntlMessageFormat', function () {
|
|
12
|
+
it('should be a function', function () {
|
|
13
|
+
expect(typeof IntlMessageFormat).toBe('function')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should accept formatters', function () {
|
|
17
|
+
const mf = new IntlMessageFormat(
|
|
18
|
+
'My name is {FIRST} {LAST}, age {age, number}, time {time, time}, date {date, date}.',
|
|
19
|
+
'en',
|
|
20
|
+
undefined
|
|
21
|
+
)
|
|
22
|
+
const ts = 12 * 3600 * 1e3
|
|
23
|
+
const output = mf.format({
|
|
24
|
+
FIRST: 'Anthony',
|
|
25
|
+
LAST: 'Pipkin',
|
|
26
|
+
age: 8,
|
|
27
|
+
time: ts,
|
|
28
|
+
date: ts,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expect(output).toMatch(
|
|
32
|
+
/My name is Anthony Pipkin, age 8, time \d{1,2}:\d{1,2}:\d{1,2} [AP]M, date \d{1,2}\/\d{1,2}\/\d{4}\./
|
|
33
|
+
)
|
|
34
|
+
expect(output).toContain(new Intl.DateTimeFormat().format(ts))
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// INSTANCE METHODS
|
|
38
|
+
|
|
39
|
+
describe('#resolvedOptions( )', function () {
|
|
40
|
+
it('should be a function', function () {
|
|
41
|
+
const mf = new IntlMessageFormat('')
|
|
42
|
+
expect(typeof mf.resolvedOptions).toBe('function')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should have a `locale` property', function () {
|
|
46
|
+
const mf = new IntlMessageFormat('')
|
|
47
|
+
expect(Object.keys(mf.resolvedOptions())).toContain('locale')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('`locale`', function () {
|
|
51
|
+
it('should default to host locale', function () {
|
|
52
|
+
const mf = new IntlMessageFormat('')
|
|
53
|
+
expect(mf.resolvedOptions().locale).toBe(
|
|
54
|
+
new Intl.NumberFormat().resolvedOptions().locale
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should normalize the casing', function () {
|
|
59
|
+
let mf = new IntlMessageFormat('', 'en-us')
|
|
60
|
+
expect(mf.resolvedOptions().locale).toBe('en-US')
|
|
61
|
+
|
|
62
|
+
mf = new IntlMessageFormat('', 'EN-US')
|
|
63
|
+
expect(mf.resolvedOptions().locale).toBe('en-US')
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should handle @ correctly', function () {
|
|
69
|
+
const mf = new IntlMessageFormat('hi @{there}', 'en')
|
|
70
|
+
expect(
|
|
71
|
+
mf.format({
|
|
72
|
+
there: '2008',
|
|
73
|
+
})
|
|
74
|
+
).toBe('hi @2008')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('#format( [object] )', function () {
|
|
78
|
+
it('should be a function', function () {
|
|
79
|
+
const mf = new IntlMessageFormat('')
|
|
80
|
+
expect(typeof mf.format).toBe('function')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should return a string', function () {
|
|
84
|
+
const mf = new IntlMessageFormat('')
|
|
85
|
+
expect(typeof mf.format()).toBe('string')
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe('#format([ast])', function () {
|
|
90
|
+
it('should format ast', function () {
|
|
91
|
+
const mf = new IntlMessageFormat(parse('hello world'))
|
|
92
|
+
expect(mf.format()).toBe('hello world')
|
|
93
|
+
})
|
|
94
|
+
it('should format ast w/ placeholders', function () {
|
|
95
|
+
const mf = new IntlMessageFormat(parse('hello world, {name}'))
|
|
96
|
+
expect(mf.format({name: 'foo'})).toBe('hello world, foo')
|
|
97
|
+
})
|
|
98
|
+
it('should format ast w/o parser', function () {
|
|
99
|
+
const mf = new IntlMessageFormat(parse('hello world'))
|
|
100
|
+
expect(mf.format()).toBe('hello world')
|
|
101
|
+
})
|
|
102
|
+
it('should format ast w/ placeholders w/o parser', function () {
|
|
103
|
+
const mf = new IntlMessageFormat(parse('hello world, {name}'))
|
|
104
|
+
expect(mf.format({name: 'foo'})).toBe('hello world, foo')
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('using a string pattern', function () {
|
|
109
|
+
it('should properly replace direct arguments in the string', function () {
|
|
110
|
+
const mf = new IntlMessageFormat('My name is {FIRST} {LAST}.')
|
|
111
|
+
const output = mf.format({
|
|
112
|
+
FIRST: 'Anthony',
|
|
113
|
+
LAST: 'Pipkin',
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
expect(output).toBe('My name is Anthony Pipkin.')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should not ignore zero values', function () {
|
|
120
|
+
const mf = new IntlMessageFormat('I am {age} years old.')
|
|
121
|
+
const output = mf.format({
|
|
122
|
+
age: 0,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
expect(output).toBe('I am 0 years old.')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should ignore false, null, and undefined', function () {
|
|
129
|
+
const mf = new IntlMessageFormat('{a}{b}{c}')
|
|
130
|
+
const output = mf.format({
|
|
131
|
+
a: false,
|
|
132
|
+
b: null,
|
|
133
|
+
c: undefined,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
expect(output).toBe('')
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe('and plurals under the Arabic locale', function () {
|
|
141
|
+
const msg =
|
|
142
|
+
'' +
|
|
143
|
+
'I have {numPeople, plural,' +
|
|
144
|
+
'zero {zero points}' +
|
|
145
|
+
'one {a point}' +
|
|
146
|
+
'two {two points}' +
|
|
147
|
+
'few {a few points}' +
|
|
148
|
+
'many {lots of points}' +
|
|
149
|
+
'other {some other amount of points}}' +
|
|
150
|
+
'.'
|
|
151
|
+
|
|
152
|
+
const msgFmt = new IntlMessageFormat(msg, 'ar')
|
|
153
|
+
|
|
154
|
+
it('should match zero', function () {
|
|
155
|
+
const m = msgFmt.format({
|
|
156
|
+
numPeople: 0,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
expect(m).toBe('I have zero points.')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should match one', function () {
|
|
163
|
+
const m = msgFmt.format({
|
|
164
|
+
numPeople: 1,
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
expect(m).toBe('I have a point.')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('should match two', function () {
|
|
171
|
+
const m = msgFmt.format({
|
|
172
|
+
numPeople: 2,
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
expect(m).toBe('I have two points.')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('should match few', function () {
|
|
179
|
+
const m = msgFmt.format({
|
|
180
|
+
numPeople: 5,
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
expect(m).toBe('I have a few points.')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('should match many', function () {
|
|
187
|
+
const m = msgFmt.format({
|
|
188
|
+
numPeople: 20,
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
expect(m).toBe('I have lots of points.')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('should match other', function () {
|
|
195
|
+
const m = msgFmt.format({
|
|
196
|
+
numPeople: 100,
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
expect(m).toBe('I have some other amount of points.')
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
describe('and plurals under the Welsh locale', function () {
|
|
204
|
+
const msg =
|
|
205
|
+
'' +
|
|
206
|
+
'I have {numPeople, plural,' +
|
|
207
|
+
'zero {zero points}' +
|
|
208
|
+
'one {a point}' +
|
|
209
|
+
'two {two points}' +
|
|
210
|
+
'few {a few points}' +
|
|
211
|
+
'many {lots of points}' +
|
|
212
|
+
'other {some other amount of points}}' +
|
|
213
|
+
'.'
|
|
214
|
+
|
|
215
|
+
const msgFmt = new IntlMessageFormat(msg, 'cy')
|
|
216
|
+
|
|
217
|
+
it('should match zero', function () {
|
|
218
|
+
const m = msgFmt.format({
|
|
219
|
+
numPeople: 0,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
expect(m).toBe('I have zero points.')
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should match one', function () {
|
|
226
|
+
const m = msgFmt.format({
|
|
227
|
+
numPeople: 1,
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
expect(m).toBe('I have a point.')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('should match two', function () {
|
|
234
|
+
const m = msgFmt.format({
|
|
235
|
+
numPeople: 2,
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
expect(m).toBe('I have two points.')
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('should match few', function () {
|
|
242
|
+
const m = msgFmt.format({
|
|
243
|
+
numPeople: 3,
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
expect(m).toBe('I have a few points.')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('should match many', function () {
|
|
250
|
+
const m = msgFmt.format({
|
|
251
|
+
numPeople: 6,
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
expect(m).toBe('I have lots of points.')
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('should match other', function () {
|
|
258
|
+
const m = msgFmt.format({
|
|
259
|
+
numPeople: 100,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
expect(m).toBe('I have some other amount of points.')
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('and changing the locale', function () {
|
|
267
|
+
const simple = {
|
|
268
|
+
en: '{NAME} went to {CITY}.',
|
|
269
|
+
|
|
270
|
+
fr:
|
|
271
|
+
'{NAME} est {GENDER, select, ' +
|
|
272
|
+
'female {allée}' +
|
|
273
|
+
'other {allé}}' +
|
|
274
|
+
' à {CITY}.',
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const complex = {
|
|
278
|
+
en: '{TRAVELLERS} went to {CITY}.',
|
|
279
|
+
|
|
280
|
+
fr:
|
|
281
|
+
'{TRAVELLERS} {TRAVELLER_COUNT, plural, ' +
|
|
282
|
+
'=1 {est {GENDER, select, ' +
|
|
283
|
+
'female {allée}' +
|
|
284
|
+
'other {allé}}}' +
|
|
285
|
+
'other {sont {GENDER, select, ' +
|
|
286
|
+
'female {allées}' +
|
|
287
|
+
'other {allés}}}}' +
|
|
288
|
+
' à {CITY}.',
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const maleObj = {
|
|
292
|
+
NAME: 'Tony',
|
|
293
|
+
CITY: 'Paris',
|
|
294
|
+
GENDER: 'male',
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const femaleObj = {
|
|
298
|
+
NAME: 'Jenny',
|
|
299
|
+
CITY: 'Paris',
|
|
300
|
+
GENDER: 'female',
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const maleTravelers = {
|
|
304
|
+
TRAVELLERS: 'Lucas, Tony and Drew',
|
|
305
|
+
TRAVELLER_COUNT: 3,
|
|
306
|
+
GENDER: 'male',
|
|
307
|
+
CITY: 'Paris',
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const femaleTravelers = {
|
|
311
|
+
TRAVELLERS: 'Monica',
|
|
312
|
+
TRAVELLER_COUNT: 1,
|
|
313
|
+
GENDER: 'female',
|
|
314
|
+
CITY: 'Paris',
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
it('should format message en-US simple with different objects', function () {
|
|
318
|
+
const msgFmt = new IntlMessageFormat(simple.en, 'en-US')
|
|
319
|
+
expect(msgFmt.format(maleObj)).toBe('Tony went to Paris.')
|
|
320
|
+
expect(msgFmt.format(femaleObj)).toBe('Jenny went to Paris.')
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should format message fr-FR simple with different objects', function () {
|
|
324
|
+
const msgFmt = new IntlMessageFormat(simple.fr, 'fr-FR')
|
|
325
|
+
expect(msgFmt.format(maleObj)).toBe('Tony est allé à Paris.')
|
|
326
|
+
expect(msgFmt.format(femaleObj)).toBe('Jenny est allée à Paris.')
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('should format message en-US complex with different objects', function () {
|
|
330
|
+
const msgFmt = new IntlMessageFormat(complex.en, 'en-US')
|
|
331
|
+
expect(msgFmt.format(maleTravelers)).toBe(
|
|
332
|
+
'Lucas, Tony and Drew went to Paris.'
|
|
333
|
+
)
|
|
334
|
+
expect(msgFmt.format(femaleTravelers)).toBe('Monica went to Paris.')
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it('should format message fr-FR complex with different objects', function () {
|
|
338
|
+
const msgFmt = new IntlMessageFormat(complex.fr, 'fr-FR')
|
|
339
|
+
expect(msgFmt.format(maleTravelers)).toBe(
|
|
340
|
+
'Lucas, Tony and Drew sont allés à Paris.'
|
|
341
|
+
)
|
|
342
|
+
expect(msgFmt.format(femaleTravelers)).toBe('Monica est allée à Paris.')
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
describe('and change the locale with different counts', function () {
|
|
347
|
+
const messages = {
|
|
348
|
+
en:
|
|
349
|
+
'{COMPANY_COUNT, plural, ' +
|
|
350
|
+
'=1 {One company}' +
|
|
351
|
+
'other {# companies}}' +
|
|
352
|
+
' published new books.',
|
|
353
|
+
|
|
354
|
+
ru:
|
|
355
|
+
'{COMPANY_COUNT, plural, ' +
|
|
356
|
+
'=1 {Одна компания опубликовала}' +
|
|
357
|
+
'one {# компания опубликовала}' +
|
|
358
|
+
'few {# компании опубликовали}' +
|
|
359
|
+
'many {# компаний опубликовали}' +
|
|
360
|
+
'other {# компаний опубликовали}}' +
|
|
361
|
+
' новые книги.',
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
it('should format a message with en-US locale', function () {
|
|
365
|
+
const msgFmt = new IntlMessageFormat(messages.en, 'en-US')
|
|
366
|
+
|
|
367
|
+
expect(msgFmt.format({COMPANY_COUNT: 0})).toBe(
|
|
368
|
+
'0 companies published new books.'
|
|
369
|
+
)
|
|
370
|
+
expect(msgFmt.format({COMPANY_COUNT: 1})).toBe(
|
|
371
|
+
'One company published new books.'
|
|
372
|
+
)
|
|
373
|
+
expect(msgFmt.format({COMPANY_COUNT: 2})).toBe(
|
|
374
|
+
'2 companies published new books.'
|
|
375
|
+
)
|
|
376
|
+
expect(msgFmt.format({COMPANY_COUNT: 5})).toBe(
|
|
377
|
+
'5 companies published new books.'
|
|
378
|
+
)
|
|
379
|
+
expect(msgFmt.format({COMPANY_COUNT: 10})).toBe(
|
|
380
|
+
'10 companies published new books.'
|
|
381
|
+
)
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('should format a message with ru-RU locale', function () {
|
|
385
|
+
const msgFmt = new IntlMessageFormat(messages.ru, 'ru-RU')
|
|
386
|
+
|
|
387
|
+
expect(msgFmt.format({COMPANY_COUNT: 0})).toBe(
|
|
388
|
+
'0 компаний опубликовали новые книги.'
|
|
389
|
+
)
|
|
390
|
+
expect(msgFmt.format({COMPANY_COUNT: 1})).toBe(
|
|
391
|
+
'Одна компания опубликовала новые книги.'
|
|
392
|
+
)
|
|
393
|
+
expect(msgFmt.format({COMPANY_COUNT: 2})).toBe(
|
|
394
|
+
'2 компании опубликовали новые книги.'
|
|
395
|
+
)
|
|
396
|
+
expect(msgFmt.format({COMPANY_COUNT: 5})).toBe(
|
|
397
|
+
'5 компаний опубликовали новые книги.'
|
|
398
|
+
)
|
|
399
|
+
expect(msgFmt.format({COMPANY_COUNT: 10})).toBe(
|
|
400
|
+
'10 компаний опубликовали новые книги.'
|
|
401
|
+
)
|
|
402
|
+
expect(msgFmt.format({COMPANY_COUNT: 21})).toBe(
|
|
403
|
+
'21 компания опубликовала новые книги.'
|
|
404
|
+
)
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
describe('arguments with', function () {
|
|
409
|
+
describe('no spaces', function () {
|
|
410
|
+
const msg = new IntlMessageFormat('{STATE}'),
|
|
411
|
+
state = 'Missouri'
|
|
412
|
+
|
|
413
|
+
it('should fail when the argument in the pattern is not provided', function () {
|
|
414
|
+
expect(msg.format).toThrow(
|
|
415
|
+
Error(
|
|
416
|
+
'The intl string context variable "STATE" was not provided to the string "{STATE}"'
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it('should fail when the argument in the pattern has a typo', function () {
|
|
422
|
+
function formatWithValueNameTypo() {
|
|
423
|
+
return msg.format({'ST ATE': state})
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
expect(formatWithValueNameTypo).toThrow(
|
|
427
|
+
Error(
|
|
428
|
+
'The intl string context variable "STATE" was not provided to the string "{STATE}"'
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('should succeed when the argument is correct', function () {
|
|
434
|
+
expect(msg.format({STATE: state})).toBe(state)
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
describe('a numeral', function () {
|
|
439
|
+
const msg = new IntlMessageFormat('{ST1ATE}')
|
|
440
|
+
const state = 'Missouri'
|
|
441
|
+
|
|
442
|
+
it('should fail when the argument in the pattern is not provided', function () {
|
|
443
|
+
function formatWithMissingValue() {
|
|
444
|
+
return msg.format({FOO: state})
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
expect(formatWithMissingValue).toThrow(
|
|
448
|
+
Error(
|
|
449
|
+
'The intl string context variable "ST1ATE" was not provided to the string "{ST1ATE}"'
|
|
450
|
+
)
|
|
451
|
+
)
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
it('should fail when the argument in the pattern has a typo', function () {
|
|
455
|
+
function formatWithMissingValue() {
|
|
456
|
+
msg.format({'ST ATE': state})
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
expect(formatWithMissingValue).toThrow(
|
|
460
|
+
Error(
|
|
461
|
+
'The intl string context variable "ST1ATE" was not provided to the string "{ST1ATE}"'
|
|
462
|
+
)
|
|
463
|
+
)
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it('should succeed when the argument is correct', function () {
|
|
467
|
+
expect(msg.format({ST1ATE: state})).toBe(state)
|
|
468
|
+
})
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
describe('selectordinal arguments', function () {
|
|
473
|
+
const msg =
|
|
474
|
+
'This is my {year, selectordinal, one{#st} two{#nd} few{#rd} other{#th}} birthday.'
|
|
475
|
+
|
|
476
|
+
it('should parse without errors', function () {
|
|
477
|
+
expect(() => IntlMessageFormat.__parse!(msg)).not.toThrow()
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
it('should use ordinal pluralization rules', function () {
|
|
481
|
+
const mf = new IntlMessageFormat(msg, 'en')
|
|
482
|
+
|
|
483
|
+
expect(mf.format({year: 1})).toBe('This is my 1st birthday.')
|
|
484
|
+
expect(mf.format({year: 2})).toBe('This is my 2nd birthday.')
|
|
485
|
+
expect(mf.format({year: 3})).toBe('This is my 3rd birthday.')
|
|
486
|
+
expect(mf.format({year: 4})).toBe('This is my 4th birthday.')
|
|
487
|
+
expect(mf.format({year: 11})).toBe('This is my 11th birthday.')
|
|
488
|
+
expect(mf.format({year: 21})).toBe('This is my 21st birthday.')
|
|
489
|
+
expect(mf.format({year: 22})).toBe('This is my 22nd birthday.')
|
|
490
|
+
expect(mf.format({year: 33})).toBe('This is my 33rd birthday.')
|
|
491
|
+
expect(mf.format({year: 44})).toBe('This is my 44th birthday.')
|
|
492
|
+
expect(mf.format({year: 1024})).toBe('This is my 1,024th birthday.')
|
|
493
|
+
})
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
describe('exceptions', function () {
|
|
497
|
+
it('should use the correct PT plural rules', function () {
|
|
498
|
+
const msg = '{num, plural, one{one} other{other}}'
|
|
499
|
+
const pt = new IntlMessageFormat(msg, 'pt')
|
|
500
|
+
const ptMZ = new IntlMessageFormat(msg, 'pt-MZ')
|
|
501
|
+
|
|
502
|
+
expect(pt.format({num: 0})).toBe('one')
|
|
503
|
+
// According to https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/plurals.json#L599-L606
|
|
504
|
+
expect(ptMZ.format({num: 0})).toBe('one')
|
|
505
|
+
expect(ptMZ.format({num: 100})).toBe('other')
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
it('should take negative number as plural', function () {
|
|
509
|
+
const msg =
|
|
510
|
+
'{num, plural, offset:-1 =-1{negative one} one{one} other{other}}'
|
|
511
|
+
const mf = new IntlMessageFormat(msg, 'en')
|
|
512
|
+
|
|
513
|
+
expect(mf.format({num: -1})).toBe('negative one')
|
|
514
|
+
expect(mf.format({num: 0})).toBe('one')
|
|
515
|
+
expect(mf.format({num: 1})).toBe('other')
|
|
516
|
+
})
|
|
517
|
+
it('should take empty string value', function () {
|
|
518
|
+
const msg = '"{value}"'
|
|
519
|
+
const mf = new IntlMessageFormat(msg, 'en')
|
|
520
|
+
|
|
521
|
+
expect(mf.formatToParts({value: ''})).toEqual([
|
|
522
|
+
{type: PART_TYPE.literal, value: '""'},
|
|
523
|
+
])
|
|
524
|
+
expect(mf.format({value: ''})).toBe('""')
|
|
525
|
+
})
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
it('should handle offset in plural #', function () {
|
|
529
|
+
const msg = `{num_guests, plural, offset:1
|
|
530
|
+
=0 {{host} does not give a party.}
|
|
531
|
+
=1 {{host} invites {guest} to their party.}
|
|
532
|
+
=2 {{host} invites {guest} and one other person to their party.}
|
|
533
|
+
other {{host} invites {guest} and # other people to their party.}
|
|
534
|
+
}`
|
|
535
|
+
const mf = new IntlMessageFormat(msg, 'en')
|
|
536
|
+
expect(mf.format({host: 'The host', guest: 'Alice', num_guests: 0})).toBe(
|
|
537
|
+
'The host does not give a party.'
|
|
538
|
+
)
|
|
539
|
+
expect(mf.format({host: 'The host', guest: 'Alice', num_guests: 1})).toBe(
|
|
540
|
+
'The host invites Alice to their party.'
|
|
541
|
+
)
|
|
542
|
+
expect(mf.format({host: 'The host', guest: 'Alice', num_guests: 2})).toBe(
|
|
543
|
+
'The host invites Alice and one other person to their party.'
|
|
544
|
+
)
|
|
545
|
+
expect(mf.format({host: 'The host', guest: 'Alice', num_guests: 3})).toBe(
|
|
546
|
+
'The host invites Alice and 2 other people to their party.'
|
|
547
|
+
)
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
it('regression issue #437', function () {
|
|
551
|
+
const mf = new IntlMessageFormat(
|
|
552
|
+
'{score, plural, one {# shopper} other {# shoppers}}',
|
|
553
|
+
'en'
|
|
554
|
+
)
|
|
555
|
+
expect(mf.format({score: 1})).toBe('1 shopper')
|
|
556
|
+
expect(mf.format({score: 2})).toBe('2 shoppers')
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
describe('xml', function () {
|
|
560
|
+
it('should handle @ correctly', function () {
|
|
561
|
+
const mf = new IntlMessageFormat('hi @{there}', 'en')
|
|
562
|
+
expect(
|
|
563
|
+
mf.format({
|
|
564
|
+
there: '2008',
|
|
565
|
+
})
|
|
566
|
+
).toEqual('hi @2008')
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
it('simple message', function () {
|
|
570
|
+
const mf = new IntlMessageFormat('hello <b>world</b>', 'en')
|
|
571
|
+
expect(mf.format<object>({b: parts => ({parts})})).toEqual([
|
|
572
|
+
'hello ',
|
|
573
|
+
{parts: ['world']},
|
|
574
|
+
])
|
|
575
|
+
})
|
|
576
|
+
it('nested tag message', function () {
|
|
577
|
+
const mf = new IntlMessageFormat(
|
|
578
|
+
'hello <b>world<i>!</i> <br/> </b>',
|
|
579
|
+
'en'
|
|
580
|
+
)
|
|
581
|
+
expect(
|
|
582
|
+
mf.format<object>({
|
|
583
|
+
b: chunks => ({chunks}),
|
|
584
|
+
i: c => ({val: `$$${c}$$`}),
|
|
585
|
+
})
|
|
586
|
+
).toEqual(['hello ', {chunks: ['world', {val: '$$!$$'}, ' <br/> ']}])
|
|
587
|
+
})
|
|
588
|
+
it('deep format nested tag message', function () {
|
|
589
|
+
const mf = new IntlMessageFormat(
|
|
590
|
+
'hello <b>world<i>!</i> <br/> </b>',
|
|
591
|
+
'en'
|
|
592
|
+
)
|
|
593
|
+
expect(
|
|
594
|
+
mf.format<object>({
|
|
595
|
+
b: chunks => ['<b>', ...chunks, '</b>'],
|
|
596
|
+
i: c => ({val: `$$${c}$$`}),
|
|
597
|
+
})
|
|
598
|
+
).toEqual(['hello <b>world', {val: '$$!$$'}, ' <br/> </b>'])
|
|
599
|
+
})
|
|
600
|
+
it('simple message w/ placeholder and no tag', function () {
|
|
601
|
+
const mf = new IntlMessageFormat('hello {placeholder} {var2}', 'en')
|
|
602
|
+
expect(
|
|
603
|
+
mf.format({
|
|
604
|
+
placeholder: {name: 'gaga'},
|
|
605
|
+
var2: {foo: 1},
|
|
606
|
+
})
|
|
607
|
+
).toEqual(['hello ', {name: 'gaga'}, ' ', {foo: 1}])
|
|
608
|
+
})
|
|
609
|
+
it('simple message w/ placeholder', function () {
|
|
610
|
+
const mf = new IntlMessageFormat(
|
|
611
|
+
'hello <b>world</b> <a>{placeholder}</a>',
|
|
612
|
+
'en'
|
|
613
|
+
)
|
|
614
|
+
expect(
|
|
615
|
+
mf.format<object>({
|
|
616
|
+
b: parts => ({parts}),
|
|
617
|
+
placeholder: 'gaga',
|
|
618
|
+
a: parts => ({parts}),
|
|
619
|
+
})
|
|
620
|
+
).toEqual(['hello ', {parts: ['world']}, ' ', {parts: ['gaga']}])
|
|
621
|
+
})
|
|
622
|
+
it('message w/ placeholder & HTML entities', function () {
|
|
623
|
+
const mf = new IntlMessageFormat('Hello<<tag>{text}</tag>', 'en')
|
|
624
|
+
expect(
|
|
625
|
+
mf.format<object>({
|
|
626
|
+
tag: parts => ({parts}),
|
|
627
|
+
text: '<asd>',
|
|
628
|
+
})
|
|
629
|
+
).toEqual(['Hello<', {parts: ['<asd>']}])
|
|
630
|
+
})
|
|
631
|
+
it('message w/ placeholder & >', function () {
|
|
632
|
+
const mf = new IntlMessageFormat(
|
|
633
|
+
'< hello <b>world</b> {token} <> <a>{placeholder}</a>',
|
|
634
|
+
'en'
|
|
635
|
+
)
|
|
636
|
+
expect(
|
|
637
|
+
mf.format<object>({
|
|
638
|
+
b: parts => ({parts}),
|
|
639
|
+
token: '<asd>',
|
|
640
|
+
placeholder: '>',
|
|
641
|
+
a: parts => ({parts}),
|
|
642
|
+
})
|
|
643
|
+
).toEqual([
|
|
644
|
+
'< hello ',
|
|
645
|
+
{parts: ['world']},
|
|
646
|
+
' <asd> <> ',
|
|
647
|
+
{parts: ['>']},
|
|
648
|
+
])
|
|
649
|
+
})
|
|
650
|
+
it('select message w/ placeholder & >', function () {
|
|
651
|
+
const mf = new IntlMessageFormat(
|
|
652
|
+
'{gender, select, male {< hello <b>world</b> {token} <> <a>{placeholder}</a>} female {<b>foo <> bar</b>} other {<b>foo <> bar</b>}}',
|
|
653
|
+
'en'
|
|
654
|
+
)
|
|
655
|
+
expect(
|
|
656
|
+
mf.format<object>({
|
|
657
|
+
gender: 'male',
|
|
658
|
+
b: str => ({str}),
|
|
659
|
+
token: '<asd>',
|
|
660
|
+
placeholder: '>',
|
|
661
|
+
a: str => ({str}),
|
|
662
|
+
})
|
|
663
|
+
).toEqual([
|
|
664
|
+
'< hello ',
|
|
665
|
+
{str: ['world']},
|
|
666
|
+
' <asd> <> ',
|
|
667
|
+
{str: ['>']},
|
|
668
|
+
])
|
|
669
|
+
expect(
|
|
670
|
+
mf.format<object>({
|
|
671
|
+
gender: 'female',
|
|
672
|
+
b: str => ({str}),
|
|
673
|
+
})
|
|
674
|
+
).toEqual({str: ['foo <> bar']})
|
|
675
|
+
})
|
|
676
|
+
it('should allow escaping tag as legacy HTML', function () {
|
|
677
|
+
const mf = new IntlMessageFormat(
|
|
678
|
+
"hello '<b>world</b>' '<a>'{placeholder}'</a>'",
|
|
679
|
+
'en'
|
|
680
|
+
)
|
|
681
|
+
expect(
|
|
682
|
+
mf.format({
|
|
683
|
+
placeholder: '<foo>gaga</foo>',
|
|
684
|
+
})
|
|
685
|
+
).toEqual('hello <b>world</b> <a><foo>gaga</foo></a>')
|
|
686
|
+
})
|
|
687
|
+
it('should handle tag w/ rich text', function () {
|
|
688
|
+
const mf = new IntlMessageFormat('hello <foo>{bar}</foo> test', 'en')
|
|
689
|
+
expect(
|
|
690
|
+
mf.format<object>({
|
|
691
|
+
foo: obj => ({
|
|
692
|
+
obj,
|
|
693
|
+
}),
|
|
694
|
+
bar: {bar: 1},
|
|
695
|
+
})
|
|
696
|
+
).toEqual(['hello ', {obj: [{bar: 1}]}, ' test'])
|
|
697
|
+
})
|
|
698
|
+
it('should handle tag in plural', function () {
|
|
699
|
+
const mf = new IntlMessageFormat(
|
|
700
|
+
'You have {count, plural, =1 {<b>1</b> Message} other {<b>#</b> Messages}}',
|
|
701
|
+
'en'
|
|
702
|
+
)
|
|
703
|
+
expect(
|
|
704
|
+
mf.format<string>({
|
|
705
|
+
b: chunks => `{}${chunks}{}`,
|
|
706
|
+
count: 1000,
|
|
707
|
+
})
|
|
708
|
+
).toBe('You have {}1,000{} Messages')
|
|
709
|
+
})
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
it('custom formats should work for time', function () {
|
|
713
|
+
const msg = 'Today is {time, time, verbose}'
|
|
714
|
+
const mf = new IntlMessageFormat(msg, 'en', {
|
|
715
|
+
time: {
|
|
716
|
+
verbose: {
|
|
717
|
+
month: 'long',
|
|
718
|
+
day: 'numeric',
|
|
719
|
+
year: 'numeric',
|
|
720
|
+
hour: 'numeric',
|
|
721
|
+
minute: 'numeric',
|
|
722
|
+
second: 'numeric',
|
|
723
|
+
timeZoneName: 'short',
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
})
|
|
727
|
+
expect(mf.format({time: new Date(0)})).toContain(
|
|
728
|
+
new Intl.DateTimeFormat('en', {
|
|
729
|
+
month: 'long',
|
|
730
|
+
day: 'numeric',
|
|
731
|
+
year: 'numeric',
|
|
732
|
+
hour: 'numeric',
|
|
733
|
+
minute: 'numeric',
|
|
734
|
+
second: 'numeric',
|
|
735
|
+
timeZoneName: 'short',
|
|
736
|
+
}).format(0)
|
|
737
|
+
)
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
it('custom formats should work for date', function () {
|
|
741
|
+
const msg = 'Today is {time, date, verbose}'
|
|
742
|
+
const mf = new IntlMessageFormat(msg, 'en', {
|
|
743
|
+
date: {
|
|
744
|
+
verbose: {
|
|
745
|
+
month: 'long',
|
|
746
|
+
day: 'numeric',
|
|
747
|
+
year: 'numeric',
|
|
748
|
+
hour: 'numeric',
|
|
749
|
+
minute: 'numeric',
|
|
750
|
+
second: 'numeric',
|
|
751
|
+
timeZoneName: 'short',
|
|
752
|
+
},
|
|
753
|
+
},
|
|
754
|
+
})
|
|
755
|
+
expect(mf.format({time: 0})).toContain(
|
|
756
|
+
new Intl.DateTimeFormat('en', {
|
|
757
|
+
month: 'long',
|
|
758
|
+
day: 'numeric',
|
|
759
|
+
year: 'numeric',
|
|
760
|
+
hour: 'numeric',
|
|
761
|
+
minute: 'numeric',
|
|
762
|
+
second: 'numeric',
|
|
763
|
+
timeZoneName: 'short',
|
|
764
|
+
}).format(0)
|
|
765
|
+
)
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
it('custom formats should work for number', function () {
|
|
769
|
+
const msg = 'Today is {time, number, verbose}'
|
|
770
|
+
const mf = new IntlMessageFormat(msg, 'en', {
|
|
771
|
+
number: {
|
|
772
|
+
verbose: {
|
|
773
|
+
minimumFractionDigits: 5,
|
|
774
|
+
maximumFractionDigits: 5,
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
})
|
|
778
|
+
expect(mf.format({time: 0.1234567})).toContain(
|
|
779
|
+
new Intl.NumberFormat('en', {
|
|
780
|
+
minimumFractionDigits: 5,
|
|
781
|
+
maximumFractionDigits: 5,
|
|
782
|
+
}).format(0.1234567)
|
|
783
|
+
)
|
|
784
|
+
})
|
|
785
|
+
|
|
786
|
+
describe('# symbol in plural rule argument', () => {
|
|
787
|
+
it('replaces unquoted # symbol in the plural rule option with actual number', () => {
|
|
788
|
+
const mf = new IntlMessageFormat(
|
|
789
|
+
'You {count, plural, one {worked for # hour} other {worked for # hours}} today.',
|
|
790
|
+
'en'
|
|
791
|
+
)
|
|
792
|
+
expect(mf.format({count: 1})).toBe('You worked for 1 hour today.')
|
|
793
|
+
expect(mf.format({count: 3})).toBe('You worked for 3 hours today.')
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
it('preserves quoted # symbol in the plural option', () => {
|
|
797
|
+
const mf = new IntlMessageFormat(
|
|
798
|
+
"You {count, plural, one {worked for '#' hour} other {worked for '#' hours}} today.",
|
|
799
|
+
'en'
|
|
800
|
+
)
|
|
801
|
+
expect(mf.format({count: 1})).toBe('You worked for # hour today.')
|
|
802
|
+
expect(mf.format({count: 3})).toBe('You worked for # hours today.')
|
|
803
|
+
})
|
|
804
|
+
|
|
805
|
+
it('does not format # symbol in the plural option as a standalone part', () => {
|
|
806
|
+
const mf = new IntlMessageFormat(
|
|
807
|
+
'You {count, plural, one {worked for # hour} other {worked for # hours}} today.',
|
|
808
|
+
'en'
|
|
809
|
+
)
|
|
810
|
+
expect(mf.formatToParts({count: 1})).toEqual([
|
|
811
|
+
{type: PART_TYPE.literal, value: 'You worked for 1 hour today.'},
|
|
812
|
+
])
|
|
813
|
+
})
|
|
814
|
+
|
|
815
|
+
it('does not replace # symbol in deeply nested sub messages', () => {
|
|
816
|
+
const mf = new IntlMessageFormat(
|
|
817
|
+
`You {count, plural,
|
|
818
|
+
one {worked for {unit, select,
|
|
819
|
+
hour {# hour}
|
|
820
|
+
other {# unit}
|
|
821
|
+
}}
|
|
822
|
+
other {worked for {unit, select,
|
|
823
|
+
hour {# hours}
|
|
824
|
+
other {# units}
|
|
825
|
+
}}
|
|
826
|
+
} today.`,
|
|
827
|
+
'en'
|
|
828
|
+
)
|
|
829
|
+
expect(mf.format({count: 1, unit: 'hour'})).toBe(
|
|
830
|
+
'You worked for # hour today.'
|
|
831
|
+
)
|
|
832
|
+
expect(mf.format({count: 3, unit: 'hour'})).toBe(
|
|
833
|
+
'You worked for # hours today.'
|
|
834
|
+
)
|
|
835
|
+
})
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
it('default format like in ICU should work', function () {
|
|
839
|
+
expect(
|
|
840
|
+
new IntlMessageFormat('{amount, number, integer}', 'en-US').format({
|
|
841
|
+
amount: 123456.78,
|
|
842
|
+
})
|
|
843
|
+
).toBe('123,457')
|
|
844
|
+
})
|
|
845
|
+
|
|
846
|
+
it('number skeleton', function () {
|
|
847
|
+
expect(
|
|
848
|
+
new IntlMessageFormat(
|
|
849
|
+
'{amount, number, ::currency/CAD .0 group-off}',
|
|
850
|
+
'en-US'
|
|
851
|
+
).format({amount: 123456.78})
|
|
852
|
+
).toMatch(/\$123456.8/) // Deal w/ IE11
|
|
853
|
+
expect(
|
|
854
|
+
new IntlMessageFormat(
|
|
855
|
+
'{amount, number, ::currency/GBP .0#}',
|
|
856
|
+
'en-US'
|
|
857
|
+
).format({amount: 123456.789})
|
|
858
|
+
).toBe('£123,456.79')
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
it('number skeleton with scale', function () {
|
|
862
|
+
expect(
|
|
863
|
+
new IntlMessageFormat(
|
|
864
|
+
'{amount, number, ::percent scale/0.01}',
|
|
865
|
+
'en-US'
|
|
866
|
+
).format({amount: 12.3})
|
|
867
|
+
).toBe('12%')
|
|
868
|
+
})
|
|
869
|
+
|
|
870
|
+
it('date skeleton', function () {
|
|
871
|
+
expect(
|
|
872
|
+
new IntlMessageFormat('{d, date, ::yyyyMMMdd}', 'en-US').format({
|
|
873
|
+
d: new Date(0),
|
|
874
|
+
})
|
|
875
|
+
).toMatch(/[A-Z][a-z]{2}(.*?)\d{2}(.*?),(.*?)\d{4}/) // Deal w/ IE11
|
|
876
|
+
expect(
|
|
877
|
+
new IntlMessageFormat('{d, date, ::yyyyMMdd}', 'en-US').format({
|
|
878
|
+
d: new Date(0),
|
|
879
|
+
})
|
|
880
|
+
).toMatch(/\d{2}(.*?)\/(.*?)\d{2}(.*?)\/(.*?)\d{4}/) // Deal w/ IE11
|
|
881
|
+
})
|
|
882
|
+
// Node 10 DateTimeFormat hour: 2-digit is buggy
|
|
883
|
+
if (!process.version || process.version.indexOf('v10') < 0) {
|
|
884
|
+
it('time skeleton', function () {
|
|
885
|
+
expect(
|
|
886
|
+
new IntlMessageFormat('{d, time, ::hhmmss}', 'en-US').format({
|
|
887
|
+
d: new Date(0),
|
|
888
|
+
})
|
|
889
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}(.*?)[AP]M/) // Deal w/ IE11
|
|
890
|
+
expect(
|
|
891
|
+
new IntlMessageFormat('{d, time, ::hhmmssz}', 'en-US').format({
|
|
892
|
+
d: new Date(0),
|
|
893
|
+
})
|
|
894
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}(.*?)[AP]M/) // Deal w/ IE11
|
|
895
|
+
expect(
|
|
896
|
+
new IntlMessageFormat('{d, time, ::hhmmssz}', 'en-US').format({
|
|
897
|
+
d: new Date(0),
|
|
898
|
+
})
|
|
899
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}(.*?)[AP]M/) // Deal w/ IE11
|
|
900
|
+
|
|
901
|
+
expect(
|
|
902
|
+
new IntlMessageFormat('{d, time, ::jjmmss}', 'de-DE').format({
|
|
903
|
+
d: new Date(0),
|
|
904
|
+
})
|
|
905
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}$/) // Deal w/ IE11
|
|
906
|
+
|
|
907
|
+
expect(
|
|
908
|
+
new IntlMessageFormat('{d, time, ::jjmmss}', 'en-US').format({
|
|
909
|
+
d: new Date(0),
|
|
910
|
+
})
|
|
911
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}(.*?)[AP]M$/) // Deal w/ IE11
|
|
912
|
+
|
|
913
|
+
expect(
|
|
914
|
+
new IntlMessageFormat('{d, time, ::jjmmssz}', 'de-DE').format({
|
|
915
|
+
d: new Date(0),
|
|
916
|
+
})
|
|
917
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}(.*?)[A-Z]{3}/) // Deal w/ IE11
|
|
918
|
+
|
|
919
|
+
expect(
|
|
920
|
+
new IntlMessageFormat('{d, time, ::jjmmssz}', 'en-US').format({
|
|
921
|
+
d: new Date(0),
|
|
922
|
+
})
|
|
923
|
+
).toMatch(/\d{2}(.*?):(.*?)\d{2}(.*?):(.*?)\d{2}(.*?)[AP]M(.*?)[A-Z]{3}/) // Deal w/ IE11
|
|
924
|
+
})
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
describe('formatToParts', function () {
|
|
928
|
+
it('should be able to take React Element', function () {
|
|
929
|
+
const element = {}
|
|
930
|
+
const parts = new IntlMessageFormat(
|
|
931
|
+
'a react {element}',
|
|
932
|
+
'en'
|
|
933
|
+
).formatToParts({
|
|
934
|
+
element,
|
|
935
|
+
})
|
|
936
|
+
expect(parts).toEqual([
|
|
937
|
+
{type: PART_TYPE.literal, value: 'a react '},
|
|
938
|
+
{type: PART_TYPE.object, value: element},
|
|
939
|
+
])
|
|
940
|
+
})
|
|
941
|
+
})
|
|
942
|
+
|
|
943
|
+
describe('no locale', function () {
|
|
944
|
+
describe('no locale provided', function () {
|
|
945
|
+
it('should default to English', function () {
|
|
946
|
+
const msg = new IntlMessageFormat(
|
|
947
|
+
'I have {NUM_BOOKS, plural, =1 {1 book} other {# books}}.'
|
|
948
|
+
)
|
|
949
|
+
expect(msg.resolvedOptions().locale).toBe(
|
|
950
|
+
new Intl.NumberFormat().resolvedOptions().locale
|
|
951
|
+
)
|
|
952
|
+
expect(msg.format({NUM_BOOKS: 2})).toBe('I have 2 books.')
|
|
953
|
+
})
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
describe('invalid locale default', function () {
|
|
957
|
+
it('should fallback to default locale', function () {
|
|
958
|
+
const msg = new IntlMessageFormat(
|
|
959
|
+
'{COMPANY_COUNT, plural, =1 {One company} other {# companies}} published new books.',
|
|
960
|
+
'fu-BA'
|
|
961
|
+
)
|
|
962
|
+
const m = msg.format({COMPANY_COUNT: 1})
|
|
963
|
+
|
|
964
|
+
expect(m).toBe('One company published new books.')
|
|
965
|
+
})
|
|
966
|
+
})
|
|
967
|
+
})
|
|
968
|
+
})
|