@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
package/src/load-translations.ts
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
|
|
3
|
-
import glob from 'fast-glob';
|
|
4
|
-
import {
|
|
5
|
-
TranslationsByKey,
|
|
6
|
-
UserConfig,
|
|
7
|
-
LoadedTranslation,
|
|
8
|
-
LanguageTarget,
|
|
9
|
-
LanguageName,
|
|
10
|
-
} from '@vocab/types';
|
|
11
|
-
import chalk from 'chalk';
|
|
12
|
-
|
|
13
|
-
import { trace } from './logger';
|
|
14
|
-
import {
|
|
15
|
-
defaultTranslationDirSuffix,
|
|
16
|
-
Fallback,
|
|
17
|
-
getAltLanguageFilePath,
|
|
18
|
-
getAltLanguages,
|
|
19
|
-
getDevTranslationFileGlob,
|
|
20
|
-
} from './utils';
|
|
21
|
-
|
|
22
|
-
export function getUniqueKey(key: string, namespace: string) {
|
|
23
|
-
return `${key}.${namespace}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function mergeWithDevLanguage(
|
|
27
|
-
translation: TranslationsByKey,
|
|
28
|
-
devTranslation: TranslationsByKey,
|
|
29
|
-
) {
|
|
30
|
-
// Only use keys from the dev translation
|
|
31
|
-
const keys = Object.keys(devTranslation);
|
|
32
|
-
const newLanguage: TranslationsByKey = {};
|
|
33
|
-
for (const key of keys) {
|
|
34
|
-
if (translation[key]) {
|
|
35
|
-
newLanguage[key] = {
|
|
36
|
-
message: translation[key].message,
|
|
37
|
-
description: devTranslation[key].description,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return newLanguage;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function getLanguageFallbacks({
|
|
45
|
-
languages,
|
|
46
|
-
}: {
|
|
47
|
-
languages: Array<LanguageTarget>;
|
|
48
|
-
}) {
|
|
49
|
-
const languageFallbackMap = new Map<LanguageName, LanguageName>();
|
|
50
|
-
|
|
51
|
-
for (const lang of languages) {
|
|
52
|
-
if (lang.extends) {
|
|
53
|
-
languageFallbackMap.set(lang.name, lang.extends);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return languageFallbackMap;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function getLanguageHierarcy({
|
|
61
|
-
languages,
|
|
62
|
-
}: {
|
|
63
|
-
languages: Array<LanguageTarget>;
|
|
64
|
-
}) {
|
|
65
|
-
const hierarchyMap = new Map<LanguageName, Array<LanguageName>>();
|
|
66
|
-
const fallbacks = getLanguageFallbacks({ languages });
|
|
67
|
-
|
|
68
|
-
for (const lang of languages) {
|
|
69
|
-
const langHierachy = [];
|
|
70
|
-
|
|
71
|
-
let currLang = lang.extends;
|
|
72
|
-
|
|
73
|
-
while (currLang) {
|
|
74
|
-
langHierachy.push(currLang);
|
|
75
|
-
|
|
76
|
-
currLang = fallbacks.get(currLang);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
hierarchyMap.set(lang.name, langHierachy);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return hierarchyMap;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getNamespaceByFilePath(
|
|
86
|
-
relativePath: string,
|
|
87
|
-
{ translationsDirectorySuffix = defaultTranslationDirSuffix }: UserConfig,
|
|
88
|
-
) {
|
|
89
|
-
let namespace = path
|
|
90
|
-
.dirname(relativePath)
|
|
91
|
-
.replace(/^src\//, '')
|
|
92
|
-
.replace(/\//g, '_');
|
|
93
|
-
|
|
94
|
-
if (namespace.endsWith(translationsDirectorySuffix)) {
|
|
95
|
-
namespace = namespace.slice(0, -translationsDirectorySuffix.length);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return namespace;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function printValidationError(...params: unknown[]) {
|
|
102
|
-
// eslint-disable-next-line no-console
|
|
103
|
-
console.error(chalk.red('Error loading translation:'), ...params);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function getTranslationsFromFile(
|
|
107
|
-
translations: unknown,
|
|
108
|
-
{ isAltLanguage, filePath }: { isAltLanguage: boolean; filePath: string },
|
|
109
|
-
): { $namespace: unknown; keys: TranslationsByKey } {
|
|
110
|
-
if (!translations || typeof translations !== 'object') {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Unable to read translation file ${filePath}. Translations must be an object`,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
const { $namespace, ...keys } = translations as TranslationsByKey;
|
|
116
|
-
if (isAltLanguage && $namespace) {
|
|
117
|
-
printValidationError(
|
|
118
|
-
`Found $namespace in alt language file in ${filePath}. $namespace is only used in the dev language and will be ignored.`,
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
if (!isAltLanguage && $namespace && typeof $namespace !== 'string') {
|
|
122
|
-
printValidationError(
|
|
123
|
-
`Found non-string $namespace in language file in ${filePath}. $namespace must be a string.`,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
const validKeys: TranslationsByKey = {};
|
|
127
|
-
for (const [translationKey, translation] of Object.entries(keys)) {
|
|
128
|
-
if (typeof translation === 'string') {
|
|
129
|
-
printValidationError(
|
|
130
|
-
`Found string for a translation "${translationKey}" in ${filePath}. Translation must be an object of the format {mesage: string}.`,
|
|
131
|
-
);
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (!translation) {
|
|
135
|
-
printValidationError(
|
|
136
|
-
`Found empty translation "${translationKey}" in ${filePath}. Translation must be an object of the format {mesage: string}.`,
|
|
137
|
-
);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if (!translation.message || typeof translation.message !== 'string') {
|
|
141
|
-
printValidationError(
|
|
142
|
-
`No message found for translation "${translationKey}" in ${filePath}. Translation must be an object of the format {mesage: string}.`,
|
|
143
|
-
);
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
validKeys[translationKey] = translation;
|
|
147
|
-
}
|
|
148
|
-
return { $namespace, keys: validKeys };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function loadAltLanguageFile(
|
|
152
|
-
{
|
|
153
|
-
filePath,
|
|
154
|
-
languageName,
|
|
155
|
-
devTranslation,
|
|
156
|
-
fallbacks,
|
|
157
|
-
}: {
|
|
158
|
-
filePath: string;
|
|
159
|
-
languageName: string;
|
|
160
|
-
devTranslation: TranslationsByKey;
|
|
161
|
-
fallbacks: Fallback;
|
|
162
|
-
},
|
|
163
|
-
{ devLanguage, languages }: UserConfig,
|
|
164
|
-
): TranslationsByKey {
|
|
165
|
-
const result = {};
|
|
166
|
-
|
|
167
|
-
const languageHierarchy = getLanguageHierarcy({ languages }).get(
|
|
168
|
-
languageName,
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
if (!languageHierarchy) {
|
|
172
|
-
throw new Error(`Missing language hierarchy for ${languageName}`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const fallbackLanguages: Array<string> = [languageName];
|
|
176
|
-
|
|
177
|
-
if (fallbacks !== 'none') {
|
|
178
|
-
fallbackLanguages.unshift(...languageHierarchy);
|
|
179
|
-
|
|
180
|
-
if (fallbacks === 'all' && fallbackLanguages[0] !== devLanguage) {
|
|
181
|
-
fallbackLanguages.unshift(devLanguage);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
trace(
|
|
186
|
-
`Loading alt language file with precendence: ${fallbackLanguages
|
|
187
|
-
.slice()
|
|
188
|
-
.reverse()
|
|
189
|
-
.join(' -> ')}`,
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
for (const fallbackLang of fallbackLanguages) {
|
|
193
|
-
if (fallbackLang !== devLanguage) {
|
|
194
|
-
try {
|
|
195
|
-
const altFilePath = getAltLanguageFilePath(filePath, fallbackLang);
|
|
196
|
-
delete require.cache[altFilePath];
|
|
197
|
-
|
|
198
|
-
const translationFile = require(altFilePath);
|
|
199
|
-
const { keys } = getTranslationsFromFile(translationFile, {
|
|
200
|
-
filePath: altFilePath,
|
|
201
|
-
isAltLanguage: true,
|
|
202
|
-
});
|
|
203
|
-
Object.assign(result, mergeWithDevLanguage(keys, devTranslation));
|
|
204
|
-
} catch (e) {
|
|
205
|
-
trace(`Missing alt language file ${getAltLanguageFilePath(
|
|
206
|
-
filePath,
|
|
207
|
-
fallbackLang,
|
|
208
|
-
)}
|
|
209
|
-
`);
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
Object.assign(result, devTranslation);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return result;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function loadTranslation(
|
|
220
|
-
{
|
|
221
|
-
filePath,
|
|
222
|
-
fallbacks,
|
|
223
|
-
}: {
|
|
224
|
-
filePath: string;
|
|
225
|
-
fallbacks: Fallback;
|
|
226
|
-
},
|
|
227
|
-
userConfig: UserConfig,
|
|
228
|
-
): LoadedTranslation {
|
|
229
|
-
trace(
|
|
230
|
-
`Loading translation file in "${fallbacks}" fallback mode: "${filePath}"`,
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
const languageSet: Record<
|
|
234
|
-
string,
|
|
235
|
-
Record<string, { message: string; description?: string | undefined }>
|
|
236
|
-
> = {};
|
|
237
|
-
|
|
238
|
-
delete require.cache[filePath];
|
|
239
|
-
const translationContent = require(filePath);
|
|
240
|
-
const relativePath = path.relative(
|
|
241
|
-
userConfig.projectRoot || process.cwd(),
|
|
242
|
-
filePath,
|
|
243
|
-
);
|
|
244
|
-
const { $namespace, keys: devTranslation } = getTranslationsFromFile(
|
|
245
|
-
translationContent,
|
|
246
|
-
{
|
|
247
|
-
filePath,
|
|
248
|
-
isAltLanguage: false,
|
|
249
|
-
},
|
|
250
|
-
);
|
|
251
|
-
const namespace: string =
|
|
252
|
-
typeof $namespace === 'string'
|
|
253
|
-
? $namespace
|
|
254
|
-
: getNamespaceByFilePath(relativePath, userConfig);
|
|
255
|
-
|
|
256
|
-
trace(`Found file ${filePath}. Using namespace ${namespace}`);
|
|
257
|
-
|
|
258
|
-
languageSet[userConfig.devLanguage] = devTranslation;
|
|
259
|
-
const altLanguages = getAltLanguages(userConfig);
|
|
260
|
-
for (const languageName of altLanguages) {
|
|
261
|
-
languageSet[languageName] = loadAltLanguageFile(
|
|
262
|
-
{
|
|
263
|
-
filePath,
|
|
264
|
-
languageName,
|
|
265
|
-
devTranslation,
|
|
266
|
-
fallbacks,
|
|
267
|
-
},
|
|
268
|
-
userConfig,
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return {
|
|
273
|
-
filePath,
|
|
274
|
-
keys: Object.keys(devTranslation),
|
|
275
|
-
namespace,
|
|
276
|
-
relativePath,
|
|
277
|
-
languages: languageSet,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export async function loadAllTranslations(
|
|
282
|
-
{
|
|
283
|
-
fallbacks,
|
|
284
|
-
includeNodeModules,
|
|
285
|
-
}: { fallbacks: Fallback; includeNodeModules: boolean },
|
|
286
|
-
config: UserConfig,
|
|
287
|
-
): Promise<Array<LoadedTranslation>> {
|
|
288
|
-
const { projectRoot, ignore = [] } = config;
|
|
289
|
-
|
|
290
|
-
const translationFiles = await glob(getDevTranslationFileGlob(config), {
|
|
291
|
-
ignore: includeNodeModules ? ignore : [...ignore, '**/node_modules/**'],
|
|
292
|
-
absolute: true,
|
|
293
|
-
cwd: projectRoot,
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
trace(`Found ${translationFiles.length} translation files`);
|
|
297
|
-
|
|
298
|
-
const result = await Promise.all(
|
|
299
|
-
translationFiles.map((filePath) =>
|
|
300
|
-
loadTranslation({ filePath, fallbacks }, config),
|
|
301
|
-
),
|
|
302
|
-
);
|
|
303
|
-
const keys = new Set();
|
|
304
|
-
for (const loadedTranslation of result) {
|
|
305
|
-
for (const key of loadedTranslation.keys) {
|
|
306
|
-
const uniqueKey = getUniqueKey(key, loadedTranslation.namespace);
|
|
307
|
-
if (keys.has(uniqueKey)) {
|
|
308
|
-
trace(`Duplicate keys found`);
|
|
309
|
-
throw new Error(
|
|
310
|
-
`Duplicate keys found. Key with namespace ${loadedTranslation.namespace} and key ${key} was found multiple times.`,
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
keys.add(uniqueKey);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return result;
|
|
317
|
-
}
|
package/src/logger.ts
DELETED
package/src/runtime.test.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { FormatXMLElementFn } from 'intl-messageformat';
|
|
2
|
-
import { createTranslationFile, createLanguage } from './runtime';
|
|
3
|
-
|
|
4
|
-
const createDemoTranslationFile = () =>
|
|
5
|
-
createTranslationFile<
|
|
6
|
-
'en' | 'fr',
|
|
7
|
-
{
|
|
8
|
-
vocabPublishDate: <T = string>(values: {
|
|
9
|
-
publishDate: Date | number;
|
|
10
|
-
}) => string | T | Array<string | T>;
|
|
11
|
-
}
|
|
12
|
-
>({
|
|
13
|
-
en: createLanguage({
|
|
14
|
-
vocabPublishDate: 'Vocab was published on {publishDate, date, small}',
|
|
15
|
-
}),
|
|
16
|
-
fr: createLanguage({
|
|
17
|
-
vocabPublishDate: 'Vocab a été publié le {publishDate, date, medium}',
|
|
18
|
-
}),
|
|
19
|
-
});
|
|
20
|
-
const createDemoTranslationFileWithTag = () =>
|
|
21
|
-
createTranslationFile<
|
|
22
|
-
'en' | 'fr',
|
|
23
|
-
{
|
|
24
|
-
vocabPublishDate: <T = string>(values: {
|
|
25
|
-
link: FormatXMLElementFn<T>;
|
|
26
|
-
strong: FormatXMLElementFn<T>;
|
|
27
|
-
}) => string | T | Array<string | T>;
|
|
28
|
-
}
|
|
29
|
-
>({
|
|
30
|
-
en: createLanguage({
|
|
31
|
-
vocabPublishDate: '<link><strong>Vocab</strong> is awesome</link>!',
|
|
32
|
-
}),
|
|
33
|
-
fr: createLanguage({
|
|
34
|
-
vocabPublishDate: '<link><strong>Vocab</strong> est génial</link>!',
|
|
35
|
-
}),
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe('createTranslationFile', () => {
|
|
39
|
-
it('should return translations as a promise', async () => {
|
|
40
|
-
const translations = createDemoTranslationFile();
|
|
41
|
-
const translationModule = await translations.getMessages('en');
|
|
42
|
-
expect(
|
|
43
|
-
translationModule?.vocabPublishDate.format({
|
|
44
|
-
publishDate: 1605847714000,
|
|
45
|
-
}),
|
|
46
|
-
).toBe('Vocab was published on 11/20/2020');
|
|
47
|
-
});
|
|
48
|
-
it('should return TranslationModules with language as locale', () => {
|
|
49
|
-
const translations = createDemoTranslationFile();
|
|
50
|
-
const translationModule = translations.getLoadedMessages('en');
|
|
51
|
-
expect(
|
|
52
|
-
translationModule?.vocabPublishDate.format({
|
|
53
|
-
publishDate: 1605847714000,
|
|
54
|
-
}),
|
|
55
|
-
).toBe('Vocab was published on 11/20/2020');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Support for alternative ICU locales in Node not available in current CI environment
|
|
59
|
-
// Disabling test for now until `full-icu` can be added. See https://nodejs.org/api/intl.html#intl_options_for_building_node_js
|
|
60
|
-
// eslint-disable-next-line jest/no-disabled-tests
|
|
61
|
-
it.skip('should return TranslationModules with en-AU locale', () => {
|
|
62
|
-
const translations = createDemoTranslationFile();
|
|
63
|
-
const translationModule = translations.getLoadedMessages('en', 'en-AU');
|
|
64
|
-
expect(
|
|
65
|
-
translationModule?.vocabPublishDate.format({
|
|
66
|
-
publishDate: 1605847714000,
|
|
67
|
-
}),
|
|
68
|
-
).toBe('Vocab was published on 20/11/2020');
|
|
69
|
-
});
|
|
70
|
-
it('should return an array when tags return objects', () => {
|
|
71
|
-
const translations = createDemoTranslationFileWithTag();
|
|
72
|
-
const translationModule = translations.getLoadedMessages('en', 'en-US');
|
|
73
|
-
interface TagResult {
|
|
74
|
-
type: string;
|
|
75
|
-
children: unknown;
|
|
76
|
-
}
|
|
77
|
-
type ExpectedResultType = string | TagResult | Array<string | TagResult>;
|
|
78
|
-
if (!translationModule) {
|
|
79
|
-
throw new Error('no translationModule');
|
|
80
|
-
}
|
|
81
|
-
const result = translationModule.vocabPublishDate.format<TagResult>({
|
|
82
|
-
strong: (children) => ({
|
|
83
|
-
type: 'strong',
|
|
84
|
-
children,
|
|
85
|
-
}),
|
|
86
|
-
link: (children) => ({
|
|
87
|
-
type: 'link',
|
|
88
|
-
children,
|
|
89
|
-
}),
|
|
90
|
-
});
|
|
91
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
92
|
-
const _unused: ExpectedResultType = result;
|
|
93
|
-
expect(result).toEqual(expect.any(Array));
|
|
94
|
-
expect(result).toEqual([
|
|
95
|
-
{
|
|
96
|
-
children: [{ children: ['Vocab'], type: 'strong' }, ' is awesome'],
|
|
97
|
-
type: 'link',
|
|
98
|
-
},
|
|
99
|
-
'!',
|
|
100
|
-
]);
|
|
101
|
-
});
|
|
102
|
-
it('should return a string when all tags return strings', () => {
|
|
103
|
-
const translations = createDemoTranslationFileWithTag();
|
|
104
|
-
const translationModule = translations.getLoadedMessages('en', 'en-US');
|
|
105
|
-
type ExpectedResultType = string | Array<string>;
|
|
106
|
-
if (!translationModule) {
|
|
107
|
-
throw new Error('no translationModule');
|
|
108
|
-
}
|
|
109
|
-
const result = translationModule.vocabPublishDate.format({
|
|
110
|
-
strong: (children) => `*${children}*`,
|
|
111
|
-
link: (children) => `[${children}]()`,
|
|
112
|
-
});
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
114
|
-
const _unused: ExpectedResultType = result;
|
|
115
|
-
expect(typeof result).toBe('string');
|
|
116
|
-
expect(result).toBe('[*Vocab* is awesome]()!');
|
|
117
|
-
});
|
|
118
|
-
it('should return TranslationModules with en-US locale', () => {
|
|
119
|
-
const translations = createDemoTranslationFile();
|
|
120
|
-
const translationModule = translations.getLoadedMessages('en', 'en-US');
|
|
121
|
-
if (!translationModule) {
|
|
122
|
-
throw new Error('no translationModule');
|
|
123
|
-
}
|
|
124
|
-
const result = translationModule.vocabPublishDate.format({
|
|
125
|
-
publishDate: 1605847714000,
|
|
126
|
-
});
|
|
127
|
-
expect(result).toBe('Vocab was published on 11/20/2020');
|
|
128
|
-
});
|
|
129
|
-
it('should require parameters to be passed in', () => {
|
|
130
|
-
const translations = createDemoTranslationFile();
|
|
131
|
-
const translationModule = translations.getLoadedMessages('en');
|
|
132
|
-
expect(() => {
|
|
133
|
-
// @ts-expect-error Incorrect params parameter
|
|
134
|
-
const result = translationModule?.vocabPublishDate.format({});
|
|
135
|
-
return result;
|
|
136
|
-
}).toThrowError(
|
|
137
|
-
expect.objectContaining({
|
|
138
|
-
message: expect.stringContaining('not provided'),
|
|
139
|
-
}),
|
|
140
|
-
);
|
|
141
|
-
expect(() => {
|
|
142
|
-
const result = translationModule?.vocabPublishDate.format({
|
|
143
|
-
// @ts-expect-error Incorrect params parameter
|
|
144
|
-
unrelated: 'message',
|
|
145
|
-
});
|
|
146
|
-
return result;
|
|
147
|
-
}).toThrowError(
|
|
148
|
-
expect.objectContaining({
|
|
149
|
-
message: expect.stringContaining('not provided'),
|
|
150
|
-
}),
|
|
151
|
-
);
|
|
152
|
-
});
|
|
153
|
-
});
|
package/src/runtime.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { TranslationModule, TranslationMessagesByKey } from '@vocab/types';
|
|
2
|
-
|
|
3
|
-
import { getParsedICUMessages } from './icu-handler';
|
|
4
|
-
|
|
5
|
-
export { createTranslationFile } from './translation-file';
|
|
6
|
-
|
|
7
|
-
export const createLanguage = (
|
|
8
|
-
module: TranslationMessagesByKey,
|
|
9
|
-
): TranslationModule<any> => ({
|
|
10
|
-
getValue: (locale) => getParsedICUMessages(module, locale),
|
|
11
|
-
load: () => Promise.resolve(),
|
|
12
|
-
});
|
package/src/translation-file.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TranslationModule,
|
|
3
|
-
TranslationModuleByLanguage,
|
|
4
|
-
ParsedICUMessages,
|
|
5
|
-
LanguageName,
|
|
6
|
-
ParsedFormatFnByKey,
|
|
7
|
-
TranslationFile,
|
|
8
|
-
} from '@vocab/types';
|
|
9
|
-
|
|
10
|
-
export function createTranslationFile<
|
|
11
|
-
Language extends LanguageName,
|
|
12
|
-
FormatFnByKey extends ParsedFormatFnByKey,
|
|
13
|
-
>(
|
|
14
|
-
translationsByLanguage: TranslationModuleByLanguage<Language, FormatFnByKey>,
|
|
15
|
-
): TranslationFile<Language, FormatFnByKey> {
|
|
16
|
-
function getByLanguage(language: Language): TranslationModule<FormatFnByKey> {
|
|
17
|
-
const translationModule = translationsByLanguage[language];
|
|
18
|
-
if (!translationModule) {
|
|
19
|
-
throw new Error(
|
|
20
|
-
`Attempted to retrieve translations for unknown language "${language}"`,
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
return translationModule;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
getLoadedMessages(
|
|
28
|
-
language: Language,
|
|
29
|
-
locale?: string,
|
|
30
|
-
): ParsedICUMessages<FormatFnByKey> | null {
|
|
31
|
-
const translationModule = getByLanguage(language);
|
|
32
|
-
return translationModule.getValue(locale || language) || null;
|
|
33
|
-
},
|
|
34
|
-
getMessages(
|
|
35
|
-
language: Language,
|
|
36
|
-
locale?: string,
|
|
37
|
-
): Promise<ParsedICUMessages<FormatFnByKey>> {
|
|
38
|
-
const translationModule = getByLanguage(language);
|
|
39
|
-
return translationModule.load().then(() => {
|
|
40
|
-
const result = translationModule.getValue(locale || language);
|
|
41
|
-
if (!result) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
`Unable to find translations for ${language} after attempting to load. Module may have failed to load or an internal error may have occurred.`,
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
return result;
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
load(language: Language): Promise<void> {
|
|
50
|
-
const translationModule = getByLanguage(language);
|
|
51
|
-
return translationModule.load();
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
package/src/utils.test.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getDevLanguageFileFromTsFile,
|
|
3
|
-
getAltLanguageFilePath,
|
|
4
|
-
getTSFileFromDevLanguageFile,
|
|
5
|
-
getDevLanguageFileFromAltLanguageFile,
|
|
6
|
-
isDevLanguageFile,
|
|
7
|
-
isAltLanguageFile,
|
|
8
|
-
} from './utils';
|
|
9
|
-
|
|
10
|
-
describe('getDevLanguageFileFromTsFile', () => {
|
|
11
|
-
it('should find a translation.json file', () => {
|
|
12
|
-
expect(getDevLanguageFileFromTsFile('/my/foobar/index.ts')).toBe(
|
|
13
|
-
'/my/foobar/translations.json',
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('getAltLanguageFilePath', () => {
|
|
19
|
-
it('should find a translation.json file', () => {
|
|
20
|
-
expect(getAltLanguageFilePath('/my/awesome/translations.json', 'fr')).toBe(
|
|
21
|
-
'/my/awesome/fr.translations.json',
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('getTSFileFromDevLanguageFile', () => {
|
|
27
|
-
it('should find a translation.ts file', () => {
|
|
28
|
-
expect(getTSFileFromDevLanguageFile('/my/foobar/translations.json')).toBe(
|
|
29
|
-
'/my/foobar/index.ts',
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('getDevLanguageFileFromAltLanguageFile', () => {
|
|
35
|
-
it('should find a translation.json file', () => {
|
|
36
|
-
expect(
|
|
37
|
-
getDevLanguageFileFromAltLanguageFile(
|
|
38
|
-
'/my/awesome/__translations__/fr.translations.json',
|
|
39
|
-
),
|
|
40
|
-
).toBe('/my/awesome/__translations__/translations.json');
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('isDevLanguageFile', () => {
|
|
45
|
-
it('should match dev language filename', () => {
|
|
46
|
-
expect(
|
|
47
|
-
isDevLanguageFile('/my/awesome/__translations__/translations.json'),
|
|
48
|
-
).toBe(true);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should match relative dev language filename', () => {
|
|
52
|
-
expect(isDevLanguageFile('translations.json')).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should not match alt language filename', () => {
|
|
56
|
-
expect(
|
|
57
|
-
isDevLanguageFile('/my/awesome/__translations__/fr.translations.json'),
|
|
58
|
-
).toBe(false);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('isAltLanguageFile', () => {
|
|
63
|
-
it('should match alt language filename', () => {
|
|
64
|
-
expect(
|
|
65
|
-
isAltLanguageFile('/my/awesome/__translations__/fr.translations.json'),
|
|
66
|
-
).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should match relative alt language filename', () => {
|
|
70
|
-
expect(isAltLanguageFile('fr.translations.json')).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should not match alt language filename', () => {
|
|
74
|
-
expect(
|
|
75
|
-
isAltLanguageFile('/my/awesome/__translations__/translations.json'),
|
|
76
|
-
).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
});
|