@vocab/core 1.0.3 → 1.1.1
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 +28 -0
- package/README.md +148 -1
- package/dist/declarations/src/generate-language.d.ts +5 -0
- package/dist/declarations/src/load-translations.d.ts +18 -2
- package/dist/declarations/src/validate/index.d.ts +1 -1
- package/dist/vocab-core.cjs.dev.js +191 -44
- package/dist/vocab-core.cjs.prod.js +191 -44
- package/dist/vocab-core.esm.js +191 -45
- package/package.json +27 -2
- package/src/ValidationError.ts +0 -9
- package/src/compile.ts +0 -329
- package/src/config.test.ts +0 -39
- package/src/config.ts +0 -144
- package/src/icu-handler.ts +0 -35
- package/src/index.ts +0 -14
- package/src/load-translations.ts +0 -317
- package/src/logger.ts +0 -9
- package/src/runtime.test.ts +0 -153
- package/src/runtime.ts +0 -12
- package/src/translation-file.ts +0 -54
- package/src/utils.test.ts +0 -78
- package/src/utils.ts +0 -143
- package/src/validate/index.test.ts +0 -64
- package/src/validate/index.ts +0 -81
|
@@ -10,6 +10,8 @@ var chokidar = require('chokidar');
|
|
|
10
10
|
var chalk = require('chalk');
|
|
11
11
|
var debug = require('debug');
|
|
12
12
|
var glob = require('fast-glob');
|
|
13
|
+
var IntlMessageFormat = require('intl-messageformat');
|
|
14
|
+
var printer = require('@formatjs/icu-messageformat-parser/printer');
|
|
13
15
|
var findUp = require('find-up');
|
|
14
16
|
var Validator = require('fastest-validator');
|
|
15
17
|
|
|
@@ -21,6 +23,7 @@ var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
|
21
23
|
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
22
24
|
var debug__default = /*#__PURE__*/_interopDefault(debug);
|
|
23
25
|
var glob__default = /*#__PURE__*/_interopDefault(glob);
|
|
26
|
+
var IntlMessageFormat__default = /*#__PURE__*/_interopDefault(IntlMessageFormat);
|
|
24
27
|
var findUp__default = /*#__PURE__*/_interopDefault(findUp);
|
|
25
28
|
var Validator__default = /*#__PURE__*/_interopDefault(Validator);
|
|
26
29
|
|
|
@@ -108,11 +111,78 @@ function getTranslationMessages(translations) {
|
|
|
108
111
|
return mapValues(translations, v => v.message);
|
|
109
112
|
}
|
|
110
113
|
|
|
114
|
+
function generateLanguageFromTranslations({
|
|
115
|
+
baseTranslations,
|
|
116
|
+
generator
|
|
117
|
+
}) {
|
|
118
|
+
if (!generator.transformElement && !generator.transformMessage) {
|
|
119
|
+
return baseTranslations;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const translationKeys = Object.keys(baseTranslations);
|
|
123
|
+
const generatedTranslations = {};
|
|
124
|
+
|
|
125
|
+
for (const translationKey of translationKeys) {
|
|
126
|
+
const translation = baseTranslations[translationKey];
|
|
127
|
+
let transformedMessage = translation.message;
|
|
128
|
+
|
|
129
|
+
if (generator.transformElement) {
|
|
130
|
+
const messageAst = new IntlMessageFormat__default['default'](translation.message).getAst();
|
|
131
|
+
const transformedAst = messageAst.map(transformMessageFormatElement(generator.transformElement));
|
|
132
|
+
transformedMessage = printer.printAST(transformedAst);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (generator.transformMessage) {
|
|
136
|
+
transformedMessage = generator.transformMessage(transformedMessage);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
generatedTranslations[translationKey] = {
|
|
140
|
+
message: transformedMessage
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return generatedTranslations;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function transformMessageFormatElement(transformElement) {
|
|
148
|
+
return messageFormatElement => {
|
|
149
|
+
const transformedMessageFormatElement = { ...messageFormatElement
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
switch (transformedMessageFormatElement.type) {
|
|
153
|
+
case icuMessageformatParser.TYPE.literal:
|
|
154
|
+
const transformedValue = transformElement(transformedMessageFormatElement.value);
|
|
155
|
+
transformedMessageFormatElement.value = transformedValue;
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case icuMessageformatParser.TYPE.select:
|
|
159
|
+
case icuMessageformatParser.TYPE.plural:
|
|
160
|
+
const transformedOptions = { ...transformedMessageFormatElement.options
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
for (const key of Object.keys(transformedOptions)) {
|
|
164
|
+
transformedOptions[key].value = transformedOptions[key].value.map(transformMessageFormatElement(transformElement));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case icuMessageformatParser.TYPE.tag:
|
|
170
|
+
const transformedChildren = transformedMessageFormatElement.children.map(transformMessageFormatElement(transformElement));
|
|
171
|
+
transformedMessageFormatElement.children = transformedChildren;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return transformedMessageFormatElement;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
111
179
|
function getUniqueKey(key, namespace) {
|
|
112
180
|
return `${key}.${namespace}`;
|
|
113
181
|
}
|
|
114
|
-
|
|
115
|
-
|
|
182
|
+
function mergeWithDevLanguageTranslation({
|
|
183
|
+
translation,
|
|
184
|
+
devTranslation
|
|
185
|
+
}) {
|
|
116
186
|
// Only use keys from the dev translation
|
|
117
187
|
const keys = Object.keys(devTranslation);
|
|
118
188
|
const newLanguage = {};
|
|
@@ -143,7 +213,7 @@ function getLanguageFallbacks({
|
|
|
143
213
|
return languageFallbackMap;
|
|
144
214
|
}
|
|
145
215
|
|
|
146
|
-
function
|
|
216
|
+
function getLanguageHierarchy({
|
|
147
217
|
languages
|
|
148
218
|
}) {
|
|
149
219
|
const hierarchyMap = new Map();
|
|
@@ -152,19 +222,45 @@ function getLanguageHierarcy({
|
|
|
152
222
|
});
|
|
153
223
|
|
|
154
224
|
for (const lang of languages) {
|
|
155
|
-
const
|
|
225
|
+
const langHierarchy = [];
|
|
156
226
|
let currLang = lang.extends;
|
|
157
227
|
|
|
158
228
|
while (currLang) {
|
|
159
|
-
|
|
229
|
+
langHierarchy.push(currLang);
|
|
160
230
|
currLang = fallbacks.get(currLang);
|
|
161
231
|
}
|
|
162
232
|
|
|
163
|
-
hierarchyMap.set(lang.name,
|
|
233
|
+
hierarchyMap.set(lang.name, langHierarchy);
|
|
164
234
|
}
|
|
165
235
|
|
|
166
236
|
return hierarchyMap;
|
|
167
237
|
}
|
|
238
|
+
function getFallbackLanguageOrder({
|
|
239
|
+
languages,
|
|
240
|
+
languageName,
|
|
241
|
+
devLanguage,
|
|
242
|
+
fallbacks
|
|
243
|
+
}) {
|
|
244
|
+
const languageHierarchy = getLanguageHierarchy({
|
|
245
|
+
languages
|
|
246
|
+
}).get(languageName);
|
|
247
|
+
|
|
248
|
+
if (!languageHierarchy) {
|
|
249
|
+
throw new Error(`Missing language hierarchy for ${languageName}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const fallbackLanguageOrder = [languageName];
|
|
253
|
+
|
|
254
|
+
if (fallbacks !== 'none') {
|
|
255
|
+
fallbackLanguageOrder.unshift(...languageHierarchy.reverse());
|
|
256
|
+
|
|
257
|
+
if (fallbacks === 'all' && fallbackLanguageOrder[0] !== devLanguage) {
|
|
258
|
+
fallbackLanguageOrder.unshift(devLanguage);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return fallbackLanguageOrder;
|
|
263
|
+
}
|
|
168
264
|
|
|
169
265
|
function getNamespaceByFilePath(relativePath, {
|
|
170
266
|
translationsDirectorySuffix = defaultTranslationDirSuffix
|
|
@@ -208,17 +304,17 @@ function getTranslationsFromFile(translations, {
|
|
|
208
304
|
|
|
209
305
|
for (const [translationKey, translation] of Object.entries(keys)) {
|
|
210
306
|
if (typeof translation === 'string') {
|
|
211
|
-
printValidationError(`Found string for a translation "${translationKey}" in ${filePath}. Translation must be an object of the format {
|
|
307
|
+
printValidationError(`Found string for a translation "${translationKey}" in ${filePath}. Translation must be an object of the format {message: string}.`);
|
|
212
308
|
continue;
|
|
213
309
|
}
|
|
214
310
|
|
|
215
311
|
if (!translation) {
|
|
216
|
-
printValidationError(`Found empty translation "${translationKey}" in ${filePath}. Translation must be an object of the format {
|
|
312
|
+
printValidationError(`Found empty translation "${translationKey}" in ${filePath}. Translation must be an object of the format {message: string}.`);
|
|
217
313
|
continue;
|
|
218
314
|
}
|
|
219
315
|
|
|
220
316
|
if (!translation.message || typeof translation.message !== 'string') {
|
|
221
|
-
printValidationError(`No message found for translation "${translationKey}" in ${filePath}. Translation must be an object of the format {
|
|
317
|
+
printValidationError(`No message found for translation "${translationKey}" in ${filePath}. Translation must be an object of the format {message: string}.`);
|
|
222
318
|
continue;
|
|
223
319
|
}
|
|
224
320
|
|
|
@@ -240,54 +336,44 @@ function loadAltLanguageFile({
|
|
|
240
336
|
devLanguage,
|
|
241
337
|
languages
|
|
242
338
|
}) {
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
languages
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const fallbackLanguages = [languageName];
|
|
253
|
-
|
|
254
|
-
if (fallbacks !== 'none') {
|
|
255
|
-
fallbackLanguages.unshift(...languageHierarchy);
|
|
256
|
-
|
|
257
|
-
if (fallbacks === 'all' && fallbackLanguages[0] !== devLanguage) {
|
|
258
|
-
fallbackLanguages.unshift(devLanguage);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
trace(`Loading alt language file with precendence: ${fallbackLanguages.slice().reverse().join(' -> ')}`);
|
|
339
|
+
const altLanguageTranslation = {};
|
|
340
|
+
const fallbackLanguageOrder = getFallbackLanguageOrder({
|
|
341
|
+
languages,
|
|
342
|
+
languageName,
|
|
343
|
+
devLanguage,
|
|
344
|
+
fallbacks
|
|
345
|
+
});
|
|
346
|
+
trace(`Loading alt language file with precedence: ${fallbackLanguageOrder.slice().reverse().join(' -> ')}`);
|
|
263
347
|
|
|
264
|
-
for (const
|
|
265
|
-
if (
|
|
348
|
+
for (const fallbackLanguage of fallbackLanguageOrder) {
|
|
349
|
+
if (fallbackLanguage !== devLanguage) {
|
|
266
350
|
try {
|
|
267
|
-
const altFilePath = getAltLanguageFilePath(filePath,
|
|
351
|
+
const altFilePath = getAltLanguageFilePath(filePath, fallbackLanguage);
|
|
268
352
|
delete require.cache[altFilePath];
|
|
269
353
|
|
|
270
354
|
const translationFile = require(altFilePath);
|
|
271
355
|
|
|
272
356
|
const {
|
|
273
|
-
keys
|
|
357
|
+
keys: fallbackLanguageTranslation
|
|
274
358
|
} = getTranslationsFromFile(translationFile, {
|
|
275
359
|
filePath: altFilePath,
|
|
276
360
|
isAltLanguage: true
|
|
277
361
|
});
|
|
278
|
-
Object.assign(
|
|
362
|
+
Object.assign(altLanguageTranslation, mergeWithDevLanguageTranslation({
|
|
363
|
+
translation: fallbackLanguageTranslation,
|
|
364
|
+
devTranslation
|
|
365
|
+
}));
|
|
279
366
|
} catch (e) {
|
|
280
|
-
trace(`Missing alt language file ${getAltLanguageFilePath(filePath,
|
|
367
|
+
trace(`Missing alt language file ${getAltLanguageFilePath(filePath, fallbackLanguage)}
|
|
281
368
|
`);
|
|
282
369
|
}
|
|
283
370
|
} else {
|
|
284
|
-
Object.assign(
|
|
371
|
+
Object.assign(altLanguageTranslation, devTranslation);
|
|
285
372
|
}
|
|
286
373
|
}
|
|
287
374
|
|
|
288
|
-
return
|
|
375
|
+
return altLanguageTranslation;
|
|
289
376
|
}
|
|
290
|
-
|
|
291
377
|
function loadTranslation({
|
|
292
378
|
filePath,
|
|
293
379
|
fallbacks
|
|
@@ -320,6 +406,19 @@ function loadTranslation({
|
|
|
320
406
|
}, userConfig);
|
|
321
407
|
}
|
|
322
408
|
|
|
409
|
+
for (const generatedLanguage of userConfig.generatedLanguages || []) {
|
|
410
|
+
const {
|
|
411
|
+
name: generatedLanguageName,
|
|
412
|
+
generator
|
|
413
|
+
} = generatedLanguage;
|
|
414
|
+
const baseLanguage = generatedLanguage.extends || userConfig.devLanguage;
|
|
415
|
+
const baseTranslations = languageSet[baseLanguage];
|
|
416
|
+
languageSet[generatedLanguageName] = generateLanguageFromTranslations({
|
|
417
|
+
baseTranslations,
|
|
418
|
+
generator
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
323
422
|
return {
|
|
324
423
|
filePath,
|
|
325
424
|
keys: Object.keys(devTranslation),
|
|
@@ -603,7 +702,7 @@ async function writeIfChanged(filepath, contents) {
|
|
|
603
702
|
}
|
|
604
703
|
|
|
605
704
|
/* eslint-disable no-console */
|
|
606
|
-
function findMissingKeys(loadedTranslation, devLanguageName,
|
|
705
|
+
function findMissingKeys(loadedTranslation, devLanguageName, altLanguages) {
|
|
607
706
|
const devLanguage = loadedTranslation.languages[devLanguageName];
|
|
608
707
|
|
|
609
708
|
if (!devLanguage) {
|
|
@@ -615,7 +714,7 @@ function findMissingKeys(loadedTranslation, devLanguageName, altLangauges) {
|
|
|
615
714
|
const requiredKeys = Object.keys(devLanguage);
|
|
616
715
|
|
|
617
716
|
if (requiredKeys.length > 0) {
|
|
618
|
-
for (const altLanguageName of
|
|
717
|
+
for (const altLanguageName of altLanguages) {
|
|
619
718
|
var _loadedTranslation$la;
|
|
620
719
|
|
|
621
720
|
const altLanguage = (_loadedTranslation$la = loadedTranslation.languages[altLanguageName]) !== null && _loadedTranslation$la !== void 0 ? _loadedTranslation$la : {};
|
|
@@ -691,6 +790,35 @@ const schema = {
|
|
|
691
790
|
}
|
|
692
791
|
}
|
|
693
792
|
},
|
|
793
|
+
generatedLanguages: {
|
|
794
|
+
type: 'array',
|
|
795
|
+
items: {
|
|
796
|
+
type: 'object',
|
|
797
|
+
props: {
|
|
798
|
+
name: {
|
|
799
|
+
type: 'string'
|
|
800
|
+
},
|
|
801
|
+
extends: {
|
|
802
|
+
type: 'string',
|
|
803
|
+
optional: true
|
|
804
|
+
},
|
|
805
|
+
generator: {
|
|
806
|
+
type: 'object',
|
|
807
|
+
props: {
|
|
808
|
+
transformElement: {
|
|
809
|
+
type: 'function',
|
|
810
|
+
optional: true
|
|
811
|
+
},
|
|
812
|
+
transformMessage: {
|
|
813
|
+
type: 'function',
|
|
814
|
+
optional: true
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
optional: true
|
|
821
|
+
},
|
|
694
822
|
translationsDirectorySuffix: {
|
|
695
823
|
type: 'string',
|
|
696
824
|
optional: true
|
|
@@ -728,13 +856,12 @@ function validateConfig(c) {
|
|
|
728
856
|
|
|
729
857
|
return v.message;
|
|
730
858
|
}).join(' \n'));
|
|
731
|
-
}
|
|
732
|
-
|
|
859
|
+
}
|
|
733
860
|
|
|
734
|
-
const languageStrings = c.languages.map(v => v.name);
|
|
861
|
+
const languageStrings = c.languages.map(v => v.name); // Dev Language should exist in languages
|
|
735
862
|
|
|
736
863
|
if (!languageStrings.includes(c.devLanguage)) {
|
|
737
|
-
throw new ValidationError('InvalidDevLanguage', `
|
|
864
|
+
throw new ValidationError('InvalidDevLanguage', `The dev language "${chalk__default['default'].bold.cyan(c.devLanguage)}" was not found in languages ${languageStrings.join(', ')}.`);
|
|
738
865
|
}
|
|
739
866
|
|
|
740
867
|
const foundLanguages = [];
|
|
@@ -752,6 +879,26 @@ function validateConfig(c) {
|
|
|
752
879
|
}
|
|
753
880
|
}
|
|
754
881
|
|
|
882
|
+
const foundGeneratedLanguages = [];
|
|
883
|
+
|
|
884
|
+
for (const generatedLang of c.generatedLanguages || []) {
|
|
885
|
+
// Generated languages must only exist once
|
|
886
|
+
if (foundGeneratedLanguages.includes(generatedLang.name)) {
|
|
887
|
+
throw new ValidationError('DuplicateGeneratedLanguage', `The generated language "${chalk__default['default'].bold.cyan(generatedLang.name)}" was defined multiple times.`);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
foundGeneratedLanguages.push(generatedLang.name); // Generated language names must not conflict with language names
|
|
891
|
+
|
|
892
|
+
if (languageStrings.includes(generatedLang.name)) {
|
|
893
|
+
throw new ValidationError('InvalidGeneratedLanguage', `The generated language "${chalk__default['default'].bold.cyan(generatedLang.name)}" is already defined as a language.`);
|
|
894
|
+
} // Any extends must be in languages
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
if (generatedLang.extends && !languageStrings.includes(generatedLang.extends)) {
|
|
898
|
+
throw new ValidationError('InvalidExtends', `The generated language "${chalk__default['default'].bold.cyan(generatedLang.name)}"'s extends of ${chalk__default['default'].bold.cyan(generatedLang.extends)} was not found in languages ${languageStrings.join(', ')}.`);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
755
902
|
trace('Configuration file is valid');
|
|
756
903
|
return true;
|
|
757
904
|
}
|