@lingual/i18n-check 0.8.3 → 0.8.4
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/README.md +22 -0
- package/dist/bin/index.js +3 -0
- package/dist/bin/index.test.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +28 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -223,6 +223,28 @@ yarn i18n:check --locales translations/folderExamples -s en-US -e translations/f
|
|
|
223
223
|
The `--exclude` option also accepts a mix of files and folders, which follows the same pattern as above, i.e.
|
|
224
224
|
`-e translations/folderExamples/fr/* translations/messageExamples/it.json`
|
|
225
225
|
|
|
226
|
+
### --ignore, -i
|
|
227
|
+
|
|
228
|
+
There can be situations where we only want to translate a feature for a specific region and therefore need to ignore any missing key checks against non supported locales. Another scenario is that we know of the missing keys and want to be able to skip these missing keys when running checks. For these aforementioned scenarios, by using the `--ignore` or `-i` option you can specify which keys to ignore, additionally also being able to define ignoring all keys inside a defined path, i.e. `some.keys.path.*`.
|
|
229
|
+
|
|
230
|
+
To ignore regular keys:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
yarn i18n:check --locales translations/folderExamples -s en-US -i some.key.to.ignore other.key.to.ignore
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
To ignore all keys within a provided path:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
yarn i18n:check --locales translations/folderExamples -s en-US -i "some.path.to.keys.*"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
A mix of regular keys and paths:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
yarn i18n:check --locales translations/folderExamples -s en-US -i "some.path.to.keys.*" some.key.to.ignore other.key.to.ignore
|
|
246
|
+
```
|
|
247
|
+
|
|
226
248
|
### --parser-component-functions
|
|
227
249
|
|
|
228
250
|
When using the `--unused` option, there will be situations where the i18next-parser will not be able to find components that wrap a `Trans` component.The component names for i18next-parser to match should be provided via the `--parser-component-functions` option. This option should onlybe used to define additional names for matching, a by default `Trans` will always be matched.
|
package/dist/bin/index.js
CHANGED
|
@@ -24,6 +24,7 @@ commander_1.program
|
|
|
24
24
|
.option('-o, --only <only...>', 'define the specific checks you want to run: invalidKeys, missingKeys, unused, undefined. By default the check will validate against missing and invalid keys, i.e. --only invalidKeys,missingKeys')
|
|
25
25
|
.option('-r, --reporter <style>', 'define the reporting style: standard or summary')
|
|
26
26
|
.option('-e, --exclude <exclude...>', 'define the file(s) and/or folders(s) that should be excluded from the check')
|
|
27
|
+
.option('-i, --ignore <ignore...>', 'define the key(s) or group of keys (i.e. `some.namespace.*`) that should be excluded from the check')
|
|
27
28
|
.option('-u, --unused <paths...>', 'define the source path(s) to find all unused and undefined keys')
|
|
28
29
|
.option('--parser-component-functions <components...>', 'a list of component names to parse when using the --unused option')
|
|
29
30
|
.parse();
|
|
@@ -47,6 +48,7 @@ const main = async () => {
|
|
|
47
48
|
const localePath = commander_1.program.getOptionValue('locales');
|
|
48
49
|
const format = commander_1.program.getOptionValue('format');
|
|
49
50
|
const exclude = commander_1.program.getOptionValue('exclude');
|
|
51
|
+
const ignore = commander_1.program.getOptionValue('ignore');
|
|
50
52
|
const unusedSrcPath = commander_1.program.getOptionValue('unused');
|
|
51
53
|
const componentFunctions = commander_1.program.getOptionValue('parserComponentFunctions');
|
|
52
54
|
if (!srcPath) {
|
|
@@ -77,6 +79,7 @@ const main = async () => {
|
|
|
77
79
|
const options = {
|
|
78
80
|
checks: getCheckOptions(),
|
|
79
81
|
format: format ?? undefined,
|
|
82
|
+
ignore,
|
|
80
83
|
};
|
|
81
84
|
const fileInfos = [];
|
|
82
85
|
files.sort().forEach((file) => {
|
package/dist/bin/index.test.js
CHANGED
|
@@ -489,6 +489,38 @@ ${(0, errorReporters_1.formatTable)([
|
|
|
489
489
|
],
|
|
490
490
|
])}
|
|
491
491
|
|
|
492
|
+
`);
|
|
493
|
+
});
|
|
494
|
+
it('should skip ignored keys when checking for missing/invalid keys', async () => {
|
|
495
|
+
const stdout = await execAsync('node dist/bin/index.js -l translations/multipleFilesFolderExample translations/flattenExamples -s en-US -i "other.nested.*" test.drive.four');
|
|
496
|
+
const result = stdout.split('Done')[0];
|
|
497
|
+
expect(result).toEqual(`i18n translations checker
|
|
498
|
+
Source: en-US
|
|
499
|
+
|
|
500
|
+
Found missing keys!
|
|
501
|
+
${(0, errorReporters_1.formatTable)([
|
|
502
|
+
[['file', 'key']],
|
|
503
|
+
[[multiFiles('de-DE/one.json'), 'message.text-format']],
|
|
504
|
+
])}
|
|
505
|
+
|
|
506
|
+
Found invalid keys!
|
|
507
|
+
${(0, errorReporters_1.formatTable)([
|
|
508
|
+
[['info', 'result']],
|
|
509
|
+
[
|
|
510
|
+
['file', multiFiles('de-DE/one.json')],
|
|
511
|
+
['key', 'message.select'],
|
|
512
|
+
[
|
|
513
|
+
'msg',
|
|
514
|
+
'Expected element of type "select" but received "argument", Unexpected date element, Unexpected date element...',
|
|
515
|
+
],
|
|
516
|
+
],
|
|
517
|
+
[
|
|
518
|
+
['file', multiFiles('de-DE/three.json')],
|
|
519
|
+
['key', 'multipleVariables'],
|
|
520
|
+
['msg', 'Expected argument to contain "user" but received "name"'],
|
|
521
|
+
],
|
|
522
|
+
])}
|
|
523
|
+
|
|
492
524
|
`);
|
|
493
525
|
});
|
|
494
526
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Context } from './errorReporters';
|
|
|
3
3
|
export type Options = {
|
|
4
4
|
format?: 'icu' | 'i18next' | 'react-intl' | 'next-intl';
|
|
5
5
|
checks?: Context[];
|
|
6
|
+
ignore?: string[];
|
|
6
7
|
};
|
|
7
8
|
export declare const checkInvalidTranslations: (source: Translation, targets: Record<string, Translation>, options?: Options) => InvalidTranslationsResult;
|
|
8
9
|
export declare const checkMissingTranslations: (source: Translation, targets: Record<string, Translation>) => CheckResult;
|
package/dist/index.js
CHANGED
|
@@ -26,22 +26,23 @@ const checkTranslations = (source, targets, options = { format: 'icu', checks: [
|
|
|
26
26
|
const { checks = ['invalidKeys', 'missingKeys'] } = options;
|
|
27
27
|
const missingKeys = {};
|
|
28
28
|
const invalidKeys = {};
|
|
29
|
-
const
|
|
30
|
-
const
|
|
29
|
+
const hasMissingKeysCheck = checks.includes('missingKeys');
|
|
30
|
+
const hasInvalidKeysCheck = checks.includes('invalidKeys');
|
|
31
31
|
source.forEach(({ name, content }) => {
|
|
32
32
|
const files = Object.fromEntries(targets
|
|
33
33
|
.filter(({ reference }) => reference === name)
|
|
34
34
|
.map(({ name, content }) => [name, content]));
|
|
35
|
-
if (
|
|
36
|
-
|
|
35
|
+
if (hasMissingKeysCheck) {
|
|
36
|
+
const filteredContent = filterKeys(content, options.ignore ?? []);
|
|
37
|
+
merge(missingKeys, (0, exports.checkMissingTranslations)(filteredContent, files));
|
|
37
38
|
}
|
|
38
|
-
if (
|
|
39
|
+
if (hasInvalidKeysCheck) {
|
|
39
40
|
merge(invalidKeys, (0, exports.checkInvalidTranslations)(content, files, options));
|
|
40
41
|
}
|
|
41
42
|
});
|
|
42
43
|
return {
|
|
43
|
-
missingKeys:
|
|
44
|
-
invalidKeys:
|
|
44
|
+
missingKeys: hasMissingKeysCheck ? missingKeys : undefined,
|
|
45
|
+
invalidKeys: hasInvalidKeysCheck ? invalidKeys : undefined,
|
|
45
46
|
};
|
|
46
47
|
};
|
|
47
48
|
exports.checkTranslations = checkTranslations;
|
|
@@ -102,7 +103,7 @@ const findUnusedI18NextTranslations = async (source, filesToParse, componentFunc
|
|
|
102
103
|
continue;
|
|
103
104
|
}
|
|
104
105
|
// find the file name
|
|
105
|
-
const [fileName] = (name.split(path_1.default.sep).pop() ??
|
|
106
|
+
const [fileName] = (name.split(path_1.default.sep).pop() ?? '').split('.');
|
|
106
107
|
if (!extractedResultSet.has(`${fileName}.${keyInSource}`) &&
|
|
107
108
|
!extractedResultSet.has(keyInSource)) {
|
|
108
109
|
found.push(keyInSource);
|
|
@@ -270,14 +271,14 @@ const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
|
|
|
270
271
|
// The current implementation considers the key as used no matter the namespace.
|
|
271
272
|
for (const entry of entries) {
|
|
272
273
|
// check for namespace, i.e. `namespace:some.key`
|
|
273
|
-
const [namespace, ...keyParts] = entry.key.split(
|
|
274
|
+
const [namespace, ...keyParts] = entry.key.split(':');
|
|
274
275
|
// If there is a namespace make sure to assign the namespace
|
|
275
276
|
// and update the key name
|
|
276
277
|
// Ensure that the assumed key is not the default value
|
|
277
278
|
if (keyParts.length > 0 && entry.key !== entry.defaultValue) {
|
|
278
279
|
entry.namespace = namespace;
|
|
279
280
|
// rebuild the key without the namespace
|
|
280
|
-
entry.key = keyParts.join(
|
|
281
|
+
entry.key = keyParts.join(':');
|
|
281
282
|
}
|
|
282
283
|
if (entry.returnObjects) {
|
|
283
284
|
skippableKeys.push(entry.key);
|
|
@@ -293,6 +294,23 @@ const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
|
|
|
293
294
|
});
|
|
294
295
|
return { extractedResult, skippableKeys };
|
|
295
296
|
};
|
|
297
|
+
const filterKeys = (content, keysToIgnore = []) => {
|
|
298
|
+
if (keysToIgnore.length > 0) {
|
|
299
|
+
return Object.entries(content).reduce((acc, [key, value]) => {
|
|
300
|
+
if (keysToIgnore.find((ignoreKey) => {
|
|
301
|
+
if (ignoreKey.endsWith('*')) {
|
|
302
|
+
return key.includes(ignoreKey.slice(0, ignoreKey.length - 1));
|
|
303
|
+
}
|
|
304
|
+
return ignoreKey === key;
|
|
305
|
+
})) {
|
|
306
|
+
return acc;
|
|
307
|
+
}
|
|
308
|
+
acc[key] = value;
|
|
309
|
+
return acc;
|
|
310
|
+
}, {});
|
|
311
|
+
}
|
|
312
|
+
return content;
|
|
313
|
+
};
|
|
296
314
|
function _flatten(object, prefix = null, result = {}) {
|
|
297
315
|
for (const key in object) {
|
|
298
316
|
const propName = prefix ? `${prefix}.${key}` : key;
|