@oracle/oraclejet-icu-l10n 16.0.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.
Files changed (33) hide show
  1. package/Bundler.d.ts +46 -0
  2. package/Bundler.js +312 -0
  3. package/PatternCompiler.d.ts +83 -0
  4. package/PatternCompiler.js +253 -0
  5. package/README.md +321 -0
  6. package/l10nBundleBuilder.js +88 -0
  7. package/package.json +30 -0
  8. package/test/bundleTypeTest.ts +40 -0
  9. package/test/cliTest.js +48 -0
  10. package/test/jest.config.js +9 -0
  11. package/test/l10nTest.js +492 -0
  12. package/test/resources/CustomMessageType.d.ts +6 -0
  13. package/test/resources/custom-hooks-no-def.js +3 -0
  14. package/test/resources/custom-hooks.js +8 -0
  15. package/test/resources/escape-util.js +3 -0
  16. package/test/resources/nls/app-strings-legacy-keys.json +5 -0
  17. package/test/resources/nls/app-strings-x.json +4 -0
  18. package/test/resources/nls/app-strings.json +48 -0
  19. package/test/resources/nls/azb/app-strings.json +3 -0
  20. package/test/resources/nls/azb-AZ/app-strings.json +3 -0
  21. package/test/resources/nls/ro-MD/app-strings.json +3 -0
  22. package/test/resources/nls/ru/app-strings.json +4 -0
  23. package/test/resources/nls/ru-RU/app-strings-x.json +3 -0
  24. package/test/resources/nls/ru-RU/app-strings.json +3 -0
  25. package/test/resources/nls/zh/app-strings.json +3 -0
  26. package/test/resources/nls/zh-Hans/app-strings.json +2 -0
  27. package/test/resources/nls/zh-Hans/extra-app-strings-x.json +3 -0
  28. package/test/resources/nls/zh-Hans-CN/app-strings.json +2 -0
  29. package/test/resources/nls-bad-metadata/app-strings-bad-root.json +11 -0
  30. package/test/resources/nls-extra-keys/README.md +3 -0
  31. package/test/resources/nls-extra-keys/app-bundle-bad.json +4 -0
  32. package/test/resources/nls-extra-keys/en/app-bundle-bad.json +3 -0
  33. package/tsconfig.json +7 -0
@@ -0,0 +1,492 @@
1
+ 'use strict';
2
+ const assert = require('assert');
3
+ const path = require('path');
4
+ const { build, isNlsDir } = require('../Bundler');
5
+ const fsx = require('fs-extra');
6
+ const vm = require('vm');
7
+ const glob = require('glob');
8
+
9
+ const rootDir = path.join(__dirname, 'resources/nls');
10
+ const outputRoot = path.resolve(__dirname, 'built');
11
+
12
+ function runTestCases(extractBundle) {
13
+ describe('Bundle files', () => {
14
+ it('creates the root bundle in English', () => {
15
+ const bundle = extractBundle(`app-strings.js`);
16
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard Content Area');
17
+ assert.equal(
18
+ bundle.pageIntro({ name: 'Dashboard' }),
19
+ 'To change the content of this section, you will make edits to the Dashboard file located in the /js/views folder.'
20
+ );
21
+ assert.equal(bundle.alpha(), 'Alpha');
22
+ assert.equal(bundle.beta(), 'Beta');
23
+ assert.equal(bundle.delta(), 'Delta');
24
+ assert.equal(bundle.epsilon(), 'Epsilon');
25
+ assert.equal(bundle.blank(), '');
26
+ });
27
+
28
+ it('creates the azb bundle in Azerbaijani', () => {
29
+ const bundle = extractBundle('azb/app-strings.js');
30
+ assert.equal(bundle.welcome(), 'Xoş gəlmisiniz');
31
+ });
32
+
33
+ it('creates the azb-AZ bundle in Azerbaijani', () => {
34
+ const bundle = extractBundle('azb-AZ/app-strings.js');
35
+ assert.equal(bundle.welcome(), 'Xoş gəlmisiniz');
36
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard Məzmun sahəsi');
37
+ });
38
+
39
+ it('creates the ru bundle in Russian', () => {
40
+ const bundle = extractBundle(`ru/app-strings.js`);
41
+ // Derived value from root bundle--in English
42
+ assert.equal(bundle.welcome(), 'Welcome');
43
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard область содержимого');
44
+ });
45
+
46
+ it('creates the ru-RU bundle in Russian', () => {
47
+ const bundle = extractBundle(`ru-RU/app-strings.js`);
48
+ // Derived value from root bundle--in English
49
+ assert.equal(bundle.welcome(), 'Welcome');
50
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard область содержимого');
51
+ assert.equal(
52
+ bundle.pageIntro({ name: 'Dashboard' }),
53
+ 'Чтобы изменить содержимое этого раздела, вы внесете изменения в файл Dashboard, расположенный в папке /js/views.'
54
+ );
55
+ });
56
+
57
+ it('creates the zh bundle in Russian', () => {
58
+ const bundle = extractBundle(`zh/app-strings.js`);
59
+ // Derived value from root bundle--in English
60
+ assert.equal(bundle.welcome(), '欢迎');
61
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard Content Area');
62
+ });
63
+
64
+ it('creates the zh-Hans bundle in Russian', () => {
65
+ const bundle = extractBundle(`zh-Hans/app-strings.js`);
66
+ // Derived value from root bundle--in English
67
+ assert.equal(bundle.welcome(), '欢迎');
68
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard Content Area');
69
+ });
70
+
71
+ it('creates the zh-Hans-CN bundle in Russian', () => {
72
+ const bundle = extractBundle(`zh-Hans-CN/app-strings.js`);
73
+ // Derived value from root bundle--in English
74
+ assert.equal(bundle.welcome(), '欢迎');
75
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard Content Area');
76
+ });
77
+
78
+ it('creates parameterized plural values', () => {
79
+ const bundle = extractBundle(`ru-RU/app-strings.js`);
80
+ assert.equal(
81
+ bundle.invitation({
82
+ host: 'Barbara',
83
+ guest: 'Phil',
84
+ gender_of_host: 'female',
85
+ num_guests: 0
86
+ }),
87
+ 'Barbara does not give a party.'
88
+ );
89
+ assert.equal(
90
+ bundle.invitation({
91
+ host: 'Barbara',
92
+ guest: 'Phil',
93
+ gender_of_host: 'female',
94
+ num_guests: 1
95
+ }),
96
+ 'Barbara invites Phil to her party.'
97
+ );
98
+ assert.equal(
99
+ bundle.invitation({
100
+ host: 'Barbara',
101
+ guest: 'Phil',
102
+ gender_of_host: 'female',
103
+ num_guests: 2
104
+ }),
105
+ 'Barbara invites Phil and one other person to her party.'
106
+ );
107
+ assert.equal(
108
+ bundle.invitation({
109
+ host: 'Bob',
110
+ guest: 'Sue',
111
+ gender_of_host: 'male',
112
+ num_guests: 3
113
+ }),
114
+ 'Bob invites Sue and 2 other people to his party.'
115
+ );
116
+ });
117
+
118
+ it('uses plural select rules for buckets', () => {
119
+ const enBundle = extractBundle(`app-strings.js`);
120
+ assert.equal(
121
+ enBundle.plural({
122
+ total_rows: 0
123
+ }),
124
+ 'No rows'
125
+ );
126
+ assert.equal(
127
+ enBundle.plural({
128
+ total_rows: 1
129
+ }),
130
+ '1 row'
131
+ );
132
+ assert.equal(
133
+ enBundle.plural({
134
+ total_rows: 2
135
+ }),
136
+ '2 rows'
137
+ );
138
+
139
+ const ruBundle = extractBundle(`ru-RU/app-strings.js`);
140
+ assert.equal(
141
+ ruBundle.plural({
142
+ total_rows: 0
143
+ }),
144
+ 'нет строк'
145
+ );
146
+ assert.equal(
147
+ ruBundle.plural({
148
+ total_rows: 1
149
+ }),
150
+ '1 ряд'
151
+ );
152
+ assert.equal(
153
+ ruBundle.plural({
154
+ total_rows: 2
155
+ }),
156
+ '2 ряда'
157
+ );
158
+ });
159
+ });
160
+
161
+ describe('Number formats', () => {
162
+ const bundle = extractBundle('app-strings.js');
163
+
164
+ it('formats date/time', () => {
165
+ const testDate = '2022-09-29 07:25 GMT';
166
+ assert.equal(
167
+ bundle.dateFormat({
168
+ date_time: testDate,
169
+ item: 'an alien',
170
+ planet: 5
171
+ }),
172
+ 'At 25 past, on September 29, there was an alien on planet 5.'
173
+ );
174
+ });
175
+
176
+ it('formats currency', () => {
177
+ assert.equal(bundle.currencyFormat({ gbp: 99 }), 'The budget is +£99 and no more.');
178
+ });
179
+ });
180
+
181
+ describe('Metadata', () => {
182
+ const bundle = extractBundle('ro-MD/app-strings.js');
183
+
184
+ it('redefining parameter type should cause failure', () => {
185
+ let didFail = false;
186
+ try {
187
+ build({
188
+ rootDir: path.join(`${rootDir}-bad-metadata`),
189
+ locale: 'ro-MD',
190
+ bundleName: 'app-strings-bad-root.json',
191
+ outDir: path.join(outputRoot, 'nls-bad-metadata')
192
+ });
193
+ } catch (ex) {
194
+ didFail = !!ex.message.match(/While processing ro-MD\/app-strings-bad-root.json/);
195
+ }
196
+ assert(didFail === true);
197
+ })
198
+
199
+ it('metadata keys should not be in bundle', () => {
200
+ assert.strictEqual(Object.keys(bundle).every(k => !k.startsWith('@')), true);
201
+ });
202
+
203
+ it('metadata parameters present in locale bundle', () => {
204
+ const birthDate = '2022-09-29 07:25 GMT';
205
+ assert.equal(bundle.localeParams({
206
+ total: 1,
207
+ gender: 'male',
208
+ title: 'Mr.',
209
+ birthDate
210
+ }), 'Moldavian total: 1 unu, gender: male, title: Mr., birthDate: 29.09.2022');
211
+ });
212
+ });
213
+
214
+ }
215
+
216
+ describe('Custom hooks', () => {
217
+ it('can produce custom return types', () => {
218
+ const outDir = path.resolve(outputRoot, 'custom-hooks');
219
+ fsx.removeSync(outDir);
220
+ build({
221
+ rootDir,
222
+ bundleName: 'app-strings.json',
223
+ locale: 'en-US',
224
+ outDir,
225
+ hooks: require.resolve('./resources/custom-hooks.js'),
226
+ module: 'cjs'
227
+ });
228
+
229
+ const bundle = extractCjsBundle(outDir, 'app-strings.js').default;
230
+ expect(bundle.welcome().bundle).toEqual('app-strings.json');
231
+ expect(bundle.welcome().key).toEqual('welcome');
232
+ expect(bundle.welcome().params).toBeUndefined();
233
+ expect(bundle.welcome().value).toEqual('Welcome');
234
+ });
235
+
236
+ it('can produce custom return types without definition', () => {
237
+ const outDir = path.resolve(outputRoot, 'custom-hooks-no-def');
238
+ fsx.removeSync(outDir);
239
+ build({
240
+ rootDir,
241
+ bundleName: 'app-strings.json',
242
+ locale: 'en-US',
243
+ outDir,
244
+ hooks: require.resolve('./resources/custom-hooks-no-def.js'),
245
+ module: 'cjs'
246
+ });
247
+
248
+ const bundle = extractCjsBundle(outDir, 'app-strings.js').default;
249
+ expect(bundle.welcome().bundleId).toEqual('app-strings.json');
250
+ expect(bundle.welcome().id).toEqual('welcome');
251
+ expect(bundle.welcome().params).toBeUndefined();
252
+ expect(bundle.welcome().translation).toEqual('Welcome');
253
+ });
254
+ });
255
+
256
+ /**
257
+ * Read contents of CJS bundle
258
+ * @param {string} outDir
259
+ * @param {string} filePath
260
+ */
261
+ function extractCjsBundle(outDir, filepath) {
262
+ return require(path.join(outDir, filepath));
263
+ }
264
+
265
+ describe('CJS default export', () => {
266
+ const outDir = path.resolve(outputRoot, 'cjs');
267
+
268
+ fsx.removeSync(outDir);
269
+ build({
270
+ rootDir,
271
+ bundleName: 'app-strings.json',
272
+ locale: 'en-US',
273
+ outDir,
274
+ module: 'cjs'
275
+ });
276
+
277
+ runTestCases((fp) => extractCjsBundle(outDir, fp).default);
278
+ });
279
+
280
+ describe('AMD', () => {
281
+ /**
282
+ * Read contents of ES6 file and convert to CommonJS export for Node testing
283
+ * @param {string} filepath
284
+ */
285
+ function extractBundle(filepath, exportType) {
286
+ const bundleContents = fsx.readFileSync(filepath).toString();
287
+ // Create a 'define' method in the context which returns the object passed in
288
+ const script = new vm.Script(bundleContents, '');
289
+ const context = vm.createContext({
290
+ define: function (rb) {
291
+ // define({ ... })
292
+ if (arguments.length === 1) {
293
+ return rb;
294
+ }
295
+ // define(['require', 'exports'], function(require, exports))
296
+ const exp = {};
297
+ // pass exports for every argument
298
+ const args = new Array(arguments[0].length);
299
+ const ret = arguments[1](...args.fill(exp));
300
+ // AMD may return the value or assign to 'exports'
301
+ const resource = ret || exp;
302
+ return exportType === 'default' ? resource.default : resource;
303
+ }
304
+ });
305
+ const rb = script.runInContext(context);
306
+ return rb;
307
+ }
308
+
309
+ function testAmd(exportType) {
310
+ const outDir = path.resolve(outputRoot, `amd-${exportType}`);
311
+
312
+ fsx.removeSync(outDir);
313
+ build({
314
+ rootDir,
315
+ bundleName: 'app-strings.json',
316
+ locale: 'en-US',
317
+ outDir,
318
+ module: 'amd',
319
+ exportType
320
+ });
321
+
322
+ runTestCases((fp) => extractBundle(path.join(outDir, fp), exportType));
323
+ }
324
+
325
+ describe('default output', () => {
326
+ testAmd('default');
327
+ });
328
+
329
+ describe('named output', () => {
330
+ testAmd('named');
331
+ });
332
+
333
+ describe('legacy-amd', () => {
334
+ const outDir = path.resolve(outputRoot, 'legacy-amd');
335
+
336
+ beforeAll(() => {
337
+ fsx.removeSync(outDir);
338
+ build({
339
+ rootDir,
340
+ bundleName: 'app-strings-legacy-keys.json',
341
+ locale: 'en-US',
342
+ outDir,
343
+ module: 'legacy-amd'
344
+ });
345
+ });
346
+
347
+ it('does not contain ts output', () => {
348
+ expect(glob.sync(path.join(outDir, '**', '*.ts'))).toEqual([]);
349
+ });
350
+
351
+ it('creates non-standard keys', () => {
352
+ const bundle = extractBundle(
353
+ path.join(outDir, 'app-strings-legacy-keys.js'),
354
+ 'named'
355
+ );
356
+ expect(bundle['dot.key']()).toEqual('Dot key');
357
+ expect(bundle['.leading.dot.key']()).toEqual('Leading dot key');
358
+ expect(bundle['dash-key']()).toEqual('Dash key');
359
+ });
360
+ });
361
+ });
362
+
363
+ describe('Extra locales', () => {
364
+ const outDir = path.resolve(outputRoot, 'extra');
365
+ const additionalLocales = ['de', 'de-DE', 'fr-FR', 'ru', 'ru-RU'];
366
+ const allLocales = [
367
+ ...new Set(fsx.readdirSync(rootDir).filter(isNlsDir).concat(additionalLocales).sort())
368
+ ];
369
+
370
+ fsx.removeSync(outDir);
371
+ build({
372
+ rootDir,
373
+ bundleName: 'app-strings.json',
374
+ locale: 'en-US',
375
+ outDir,
376
+ module: 'cjs',
377
+ additionalLocales
378
+ });
379
+
380
+ const extractBundle = (fp) => extractCjsBundle(outDir, fp).default;
381
+ runTestCases(extractBundle);
382
+
383
+ it('creates the de bundle in English', () => {
384
+ const bundle = extractBundle('de/app-strings.js');
385
+ assert.equal(bundle.welcome(), 'Welcome');
386
+ });
387
+
388
+ it('creates the de-DE bundle in English', () => {
389
+ const bundle = extractBundle('de-DE/app-strings.js');
390
+ assert.equal(bundle.welcome(), 'Welcome');
391
+ });
392
+
393
+ it('creates the fr-FR bundle in English', () => {
394
+ const bundle = extractBundle('fr-FR/app-strings.js');
395
+ assert.equal(bundle.welcome(), 'Welcome');
396
+ });
397
+
398
+ it('exports the list of supported locales', () => {
399
+ const supportedLocales = extractBundle('supportedLocales.js');
400
+ assert.deepEqual(supportedLocales, allLocales);
401
+ });
402
+ });
403
+
404
+ describe('Override files', () => {
405
+ const outDir = path.resolve(outputRoot, 'override');
406
+ const extractBundle = (fp) => extractCjsBundle(outDir, fp).default;
407
+
408
+ describe('with root overrides', () => {
409
+ build({
410
+ rootDir,
411
+ bundleName: 'app-strings-x.json',
412
+ locale: 'en-US',
413
+ override: true,
414
+ additionalLocales: ['en', 'ru-RU'],
415
+ outDir,
416
+ module: 'cjs'
417
+ });
418
+
419
+ it('creates the root override bundle in English', () => {
420
+ const bundle = extractBundle(`app-strings-x.js`);
421
+ assert.equal(bundle.pageIntro(), 'root pageIntro');
422
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard root contentArea');
423
+ });
424
+
425
+ it('creates the en override bundle from root', () => {
426
+ const bundle = extractBundle(`en/app-strings-x.js`);
427
+ assert.equal(bundle.pageIntro(), 'root pageIntro');
428
+ assert.equal(bundle.contentArea({ name: 'Dashboard' }), 'Dashboard root contentArea');
429
+ });
430
+
431
+ it('creates the ru-RU override bundle in Russian', () => {
432
+ const bundle = extractBundle(`ru-RU/app-strings-x.js`);
433
+ assert.equal(Object.keys(bundle).length, 2, "override should have root keys");
434
+ assert.equal(
435
+ bundle.contentArea({ name: 'Dashboard' }), 'Dashboard ru-RU contentArea override');
436
+ assert.equal(bundle.pageIntro(), 'root pageIntro');
437
+ });
438
+
439
+ it('should not create overrides in any other locales', () => {
440
+ let exists = false;
441
+ try {
442
+ extractBundle('ru/app-strings-x.js');
443
+ exists = true;
444
+ } catch (ex) {}
445
+ try {
446
+ extractBundle('fr/app-strings-x.js');
447
+ exists = true;
448
+ } catch (ex) {}
449
+ expect(exists).toBeFalsy();
450
+ });
451
+ });
452
+
453
+ describe('without root overrides', () => {
454
+ build({
455
+ rootDir,
456
+ bundleName: 'extra-app-strings-x.json',
457
+ override: true,
458
+ additionalLocales: ['zh-Hans'],
459
+ outDir,
460
+ module: 'cjs'
461
+ });
462
+
463
+ it('creates only zh-Hans overrides', () => {
464
+ const generatedFiles = glob.sync(`${outDir}/**/extra-app-strings-x.js`);
465
+ expect(generatedFiles.length).toEqual(1);
466
+
467
+ const bundle = extractBundle('zh-Hans/extra-app-strings-x.js');
468
+ assert.equal(Object.keys(bundle).length, 1, "override should not have root keys");
469
+ assert.equal(
470
+ bundle.contentArea({ name: 'Dashboard' }), 'Dashboard zh-Hans contentArea override');
471
+ })
472
+ })
473
+
474
+ })
475
+
476
+ describe('extra keys', () => {
477
+ const outDir = path.resolve(outputRoot, 'extra-keys');
478
+
479
+ it('should fail if locale bundle has extra keys', () => {
480
+ let didFail = false;
481
+ try {
482
+ build({
483
+ rootDir: `${rootDir}-extra-keys`,
484
+ bundleName: 'app-bundle-bad.json',
485
+ outDir
486
+ });
487
+ } catch (ex) {
488
+ didFail = ex.message.indexOf('description') >= 0;
489
+ }
490
+ expect(didFail).toBeTruthy();
491
+ })
492
+ })
@@ -0,0 +1,6 @@
1
+ export declare type CustomMessageType = {
2
+ bundle: string;
3
+ key: string;
4
+ params: { [key: string]: any } | undefined;
5
+ value: string;
6
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ convertor: '(args: ParamsType) => args'
3
+ };
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ typeImport: {
3
+ CustomMessageType: 'import { CustomMessageType } from "../../resources/CustomMessageType";'
4
+ },
5
+ otherImports: 'import { escape } from "../../resources/escape-util";',
6
+ // remap some of the parameter names to different keys
7
+ convertor: '(args: ParamsType): CustomMessageType => ({bundle: args.bundleId, key: args.id, params: args.params, value: escape(args.translation)})'
8
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ escape: (s) => s
3
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ "dot.key": "Dot key",
3
+ ".leading.dot.key": "Leading dot key",
4
+ "dash-key": "Dash key"
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "pageIntro": "root pageIntro",
3
+ "contentArea": "{name} root contentArea"
4
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "@count": {
3
+ "type": "number"
4
+ },
5
+ "@@x-base-bundle": true,
6
+ "welcome": "Welcome",
7
+ "contentArea": "{name} Content Area",
8
+ "pageIntro": "To change the content of this section, you will make edits to the {name} file located in the /js/views folder.",
9
+ "invitation": "{gender_of_host, select, female {{num_guests, plural, offset:1 =0 {{host} does not give a party.} =1 {{host} invites {guest} to her party.} =2 {{host} invites {guest} and one other person to her party.} other {{host} invites {guest} and # other people to her party.}}} male {{num_guests, plural, offset:1 =0 {{host} does not give a party.} =1 {{host} invites {guest} to his party.} =2 {{host} invites {guest} and one other person to his party.} other {{host} invites {guest} and # other people to his party.}}} other {{num_guests, plural, offset:1 =0 {{host} does not give a party.} =1 {{host} invites {guest} to their party.} =2 {{host} invites {guest} and one other person to their party.} other {{host} invites {guest} and # other people to their party.}}}}",
10
+ "plural": "{total_rows, plural, offset:0 =0 {No rows} one {# row} other {# rows}}",
11
+ "dateFormat": "At {date_time,time,::jmm} past, on {date_time,date,::dMMMM}, there was {item} on planet {planet,number,integer}.",
12
+ "currencyFormat": "The budget is {gbp, number, :: +! K currency/GBP} and no more.",
13
+ "@@locale": "en",
14
+ "alpha": "Alpha",
15
+ "beta": "Beta",
16
+ "another_button": "Another button",
17
+ "@another_button": {
18
+ "description": "anything"
19
+ },
20
+ "delta": "Delta",
21
+ "epsilon": "Epsilon",
22
+ "blank": "",
23
+ "testPlural": "You have {itemCount, plural, =0 {no items} one {item} other {{itemCount} items}}",
24
+ "localeParams": "Other locale params besides this {one}",
25
+ "@localeParams": {
26
+ "placeholders": {
27
+ "total": {
28
+ "type": "plural",
29
+ "description": "first param is used in dev English bundle"
30
+ },
31
+ "gender": {
32
+ "type": "select",
33
+ "description": "This is an extra one that might be used in other languages"
34
+ },
35
+ "title": {
36
+ "type": "text",
37
+ "description": "Person's title, Mr., Mrs., etc."
38
+ },
39
+ "birthDate": {
40
+ "type": "date",
41
+ "description": "Your b-day"
42
+ },
43
+ "one": {
44
+ "description": "{one} param is described here without redefining the type. should be ok."
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "welcome": "Xoş gəlmisiniz"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "contentArea": "{name} Məzmun sahəsi"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "localeParams": "Moldavian total: {total, plural, offset:0 =0 {nici unul} one {# unu} other {# alte}}, gender: {gender}, title: {title}, birthDate: {birthDate, date}"
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "contentArea": "{name} область содержимого",
3
+ "plural": "{total_rows, plural, offset:0 =0 {нет строк} one {# ряд} other {# ряда}}"
4
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "contentArea": "{name} ru-RU contentArea override"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "pageIntro": "Чтобы изменить содержимое этого раздела, вы внесете изменения в файл {name}, расположенный в папке /js/views."
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "welcome": "欢迎"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "contentArea": "{name} zh-Hans contentArea override"
3
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "welcome": "welcome {name}",
3
+ "@welcome": {
4
+ "placeholders": {
5
+ "name": {
6
+ "type": "number",
7
+ "description": "This redefinition should cause the builder to fail"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,3 @@
1
+ #
2
+
3
+ This folder contains a root and locale bundle file, but the bundle file contains more keys than are in the root. This should cause the bundle builder to fail.
@@ -0,0 +1,4 @@
1
+ {
2
+ "welcome": "Welcome",
3
+ "intro": "Intro"
4
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "This key should cause the bundle builder to fail."
3
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "compilerOptions": {
3
+ "esModuleInterop": true,
4
+ "strict": true
5
+ },
6
+ "include": [ "**/*.ts" ]
7
+ }