@lingual/i18n-check 0.8.2 → 0.8.3
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 +10 -18
- package/dist/bin/index.js +58 -57
- package/dist/bin/index.test.js +289 -289
- package/dist/errorReporters.d.ts +1 -1
- package/dist/errorReporters.js +21 -21
- package/dist/errorReporters.test.js +39 -39
- package/dist/index.d.ts +3 -3
- package/dist/index.js +59 -42
- package/dist/utils/findInvalidTranslations.d.ts +2 -2
- package/dist/utils/findInvalidTranslations.js +20 -19
- package/dist/utils/findInvalidTranslations.test.js +30 -30
- package/dist/utils/findInvalidi18nTranslations.d.ts +2 -2
- package/dist/utils/findInvalidi18nTranslations.js +35 -35
- package/dist/utils/findInvalidi18nTranslations.test.js +72 -72
- package/dist/utils/findMissingKeys.d.ts +1 -1
- package/dist/utils/findMissingKeys.js +2 -2
- package/dist/utils/findMissingKeys.test.js +20 -20
- package/dist/utils/flattenTranslations.d.ts +1 -1
- package/dist/utils/flattenTranslations.js +3 -3
- package/dist/utils/flattenTranslations.test.js +13 -13
- package/dist/utils/i18NextParser.d.ts +6 -6
- package/dist/utils/i18NextParser.js +29 -29
- package/dist/utils/i18NextParser.test.js +104 -104
- package/dist/utils/nextIntlSrcParser.js +11 -11
- package/dist/utils/nextIntlSrcParser.test.js +156 -156
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -371,7 +371,7 @@ Aside from using the CLI, i18n-check also exposes a set of check functions that
|
|
|
371
371
|
Start by importing i18n-check:
|
|
372
372
|
|
|
373
373
|
```ts
|
|
374
|
-
import * as i18nCheck from
|
|
374
|
+
import * as i18nCheck from '@lingual/i18n-check';
|
|
375
375
|
```
|
|
376
376
|
|
|
377
377
|
### `i18nCheck.checkTranslations(source, targets [, options])`
|
|
@@ -379,10 +379,10 @@ import * as i18nCheck from "@lingual/i18n-check";
|
|
|
379
379
|
`checkTranslations` expects the base and comparison or target files and returns an object containing the missing and invalid keys. The optional `options` objects can be provided as a third argument to define the format style via the `format` property, this is useful if you want to validate `i18next` specific translations.
|
|
380
380
|
|
|
381
381
|
```ts
|
|
382
|
-
import { checkTranslations } from
|
|
382
|
+
import { checkTranslations } from '@lingual/i18n-check';
|
|
383
383
|
|
|
384
384
|
const options = {
|
|
385
|
-
format:
|
|
385
|
+
format: 'i18next',
|
|
386
386
|
};
|
|
387
387
|
|
|
388
388
|
const { invalidKeys, missingKeys } = checkTranslations(
|
|
@@ -395,11 +395,11 @@ const { invalidKeys, missingKeys } = checkTranslations(
|
|
|
395
395
|
Additionally the `options` object enables to also define which checks should run via the `checks` property, f.e. if you only want to check for missing or invalid keys only.
|
|
396
396
|
|
|
397
397
|
```ts
|
|
398
|
-
import { checkTranslations } from
|
|
398
|
+
import { checkTranslations } from '@lingual/i18n-check';
|
|
399
399
|
|
|
400
400
|
const options = {
|
|
401
|
-
format:
|
|
402
|
-
checks: [
|
|
401
|
+
format: 'icu',
|
|
402
|
+
checks: ['invalidKeys'],
|
|
403
403
|
};
|
|
404
404
|
|
|
405
405
|
const { invalidKeys } = checkTranslations(source, targets, options);
|
|
@@ -433,7 +433,7 @@ The result for `missingKeys` as well as `invalidKeys` is an object containing th
|
|
|
433
433
|
`checkMissingTranslations` checks for any missing keys in the target files. All files are compared against the source file.
|
|
434
434
|
|
|
435
435
|
```ts
|
|
436
|
-
import { checkMissingTranslations } from
|
|
436
|
+
import { checkMissingTranslations } from '@lingual/i18n-check';
|
|
437
437
|
|
|
438
438
|
const result = checkMissingTranslations(source, targets);
|
|
439
439
|
|
|
@@ -450,10 +450,10 @@ The result is an object containing the provided locales and their corresponding
|
|
|
450
450
|
`checkInvalidTranslations` checks if there are any invalid keys in the target files. All files are compared against the source file.
|
|
451
451
|
|
|
452
452
|
```ts
|
|
453
|
-
import { checkInvalidTranslations } from
|
|
453
|
+
import { checkInvalidTranslations } from '@lingual/i18n-check';
|
|
454
454
|
|
|
455
455
|
const options = {
|
|
456
|
-
format:
|
|
456
|
+
format: 'i18next',
|
|
457
457
|
};
|
|
458
458
|
|
|
459
459
|
const result = checkInvalidTranslations(source, targets, options);
|
|
@@ -470,7 +470,7 @@ The result is an object containing the provided locales and their corresponding
|
|
|
470
470
|
|
|
471
471
|
If you want to checkout and run the code, you need to run the `build` command first.
|
|
472
472
|
|
|
473
|
-
Run `
|
|
473
|
+
Run `pnpm run build` and then depending on the scenario one of the following commands.
|
|
474
474
|
|
|
475
475
|
Basic icu translation example:
|
|
476
476
|
|
|
@@ -516,14 +516,6 @@ To run the tests use one of the following commands:
|
|
|
516
516
|
pnpm test
|
|
517
517
|
```
|
|
518
518
|
|
|
519
|
-
```bash
|
|
520
|
-
yarn test
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
```bash
|
|
524
|
-
npm test
|
|
525
|
-
```
|
|
526
|
-
|
|
527
519
|
## Links
|
|
528
520
|
|
|
529
521
|
- [Introducing i18n-check](https://lingual.dev/blog/introducing-i18n-check/)
|
package/dist/bin/index.js
CHANGED
|
@@ -14,23 +14,23 @@ const __1 = require("..");
|
|
|
14
14
|
const errorReporters_1 = require("../errorReporters");
|
|
15
15
|
const flattenTranslations_1 = require("../utils/flattenTranslations");
|
|
16
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
-
const version = require(
|
|
17
|
+
const version = require('../../package.json').version;
|
|
18
18
|
commander_1.program
|
|
19
19
|
.version(version)
|
|
20
|
-
.option(
|
|
21
|
-
.option(
|
|
22
|
-
.option(
|
|
23
|
-
.option(
|
|
24
|
-
.option(
|
|
25
|
-
.option(
|
|
26
|
-
.option(
|
|
27
|
-
.option(
|
|
28
|
-
.option(
|
|
20
|
+
.option('-l, --locales <locales...>', 'name of the directory containing the locales to validate')
|
|
21
|
+
.option('-s, --source <locale>', 'the source locale to validate against')
|
|
22
|
+
.option('-f, --format <type>', 'define the specific format: i18next, react-intl or next-intl')
|
|
23
|
+
.option('-c, --check <checks...>', 'this option is deprecated - use -o or --only instead')
|
|
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
|
+
.option('-r, --reporter <style>', 'define the reporting style: standard or summary')
|
|
26
|
+
.option('-e, --exclude <exclude...>', 'define the file(s) and/or folders(s) that should be excluded from the check')
|
|
27
|
+
.option('-u, --unused <paths...>', 'define the source path(s) to find all unused and undefined keys')
|
|
28
|
+
.option('--parser-component-functions <components...>', 'a list of component names to parse when using the --unused option')
|
|
29
29
|
.parse();
|
|
30
30
|
const getCheckOptions = () => {
|
|
31
|
-
const checkOption = commander_1.program.getOptionValue(
|
|
32
|
-
if (commander_1.program.getOptionValue(
|
|
33
|
-
console.log(chalk_1.default.yellow(
|
|
31
|
+
const checkOption = commander_1.program.getOptionValue('only') || commander_1.program.getOptionValue('check');
|
|
32
|
+
if (commander_1.program.getOptionValue('check')) {
|
|
33
|
+
console.log(chalk_1.default.yellow('The --check option has been deprecated, use the --only option instead.'));
|
|
34
34
|
}
|
|
35
35
|
if (!checkOption) {
|
|
36
36
|
return errorReporters_1.CheckOptions;
|
|
@@ -43,33 +43,33 @@ const isSource = (fileInfo, srcPath) => {
|
|
|
43
43
|
};
|
|
44
44
|
const main = async () => {
|
|
45
45
|
const start = performance.now();
|
|
46
|
-
const srcPath = commander_1.program.getOptionValue(
|
|
47
|
-
const localePath = commander_1.program.getOptionValue(
|
|
48
|
-
const format = commander_1.program.getOptionValue(
|
|
49
|
-
const exclude = commander_1.program.getOptionValue(
|
|
50
|
-
const unusedSrcPath = commander_1.program.getOptionValue(
|
|
51
|
-
const componentFunctions = commander_1.program.getOptionValue(
|
|
46
|
+
const srcPath = commander_1.program.getOptionValue('source');
|
|
47
|
+
const localePath = commander_1.program.getOptionValue('locales');
|
|
48
|
+
const format = commander_1.program.getOptionValue('format');
|
|
49
|
+
const exclude = commander_1.program.getOptionValue('exclude');
|
|
50
|
+
const unusedSrcPath = commander_1.program.getOptionValue('unused');
|
|
51
|
+
const componentFunctions = commander_1.program.getOptionValue('parserComponentFunctions');
|
|
52
52
|
if (!srcPath) {
|
|
53
|
-
console.log(chalk_1.default.red(
|
|
53
|
+
console.log(chalk_1.default.red('Source not found. Please provide a valid source locale, i.e. -s en-US'));
|
|
54
54
|
(0, node_process_1.exit)(1);
|
|
55
55
|
}
|
|
56
56
|
if (!localePath || localePath.length === 0) {
|
|
57
|
-
console.log(chalk_1.default.red(
|
|
57
|
+
console.log(chalk_1.default.red('Locale file(s) not found. Please provide valid locale file(s), i.e. -locales translations/'));
|
|
58
58
|
(0, node_process_1.exit)(1);
|
|
59
59
|
}
|
|
60
60
|
const excludedPaths = exclude ?? [];
|
|
61
61
|
const localePathFolders = localePath;
|
|
62
62
|
const isMultiFolders = localePathFolders.length > 1;
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const srcFiles = [];
|
|
64
|
+
const targetFiles = [];
|
|
65
65
|
const pattern = isMultiFolders
|
|
66
|
-
? `{${localePath.join(
|
|
67
|
-
: `${localePath.join(
|
|
66
|
+
? `{${localePath.join(',').trim()}}/**/*.{json,yaml,yml}`
|
|
67
|
+
: `${localePath.join(',').trim()}/**/*.{json,yaml,yml}`;
|
|
68
68
|
const files = await (0, glob_1.glob)(pattern, {
|
|
69
|
-
ignore: [
|
|
69
|
+
ignore: ['node_modules/**'].concat(excludedPaths),
|
|
70
70
|
windowsPathsNoEscape: true,
|
|
71
71
|
});
|
|
72
|
-
console.log(
|
|
72
|
+
console.log('i18n translations checker');
|
|
73
73
|
console.log(chalk_1.default.gray(`Source: ${srcPath}`));
|
|
74
74
|
if (format) {
|
|
75
75
|
console.log(chalk_1.default.blackBright(`Selected format is: ${format}`));
|
|
@@ -81,8 +81,8 @@ const main = async () => {
|
|
|
81
81
|
const fileInfos = [];
|
|
82
82
|
files.sort().forEach((file) => {
|
|
83
83
|
const filePath = file.split(node_path_1.default.sep);
|
|
84
|
-
const name = filePath.pop() ??
|
|
85
|
-
const extension = name.split(
|
|
84
|
+
const name = filePath.pop() ?? '';
|
|
85
|
+
const extension = name.split('.').pop() ?? 'json';
|
|
86
86
|
fileInfos.push({
|
|
87
87
|
extension,
|
|
88
88
|
file,
|
|
@@ -92,11 +92,11 @@ const main = async () => {
|
|
|
92
92
|
});
|
|
93
93
|
fileInfos.forEach(({ extension, file, name, path }) => {
|
|
94
94
|
let rawContent;
|
|
95
|
-
if (extension ===
|
|
96
|
-
rawContent = js_yaml_1.default.load(node_fs_1.default.readFileSync(file,
|
|
95
|
+
if (extension === 'yaml') {
|
|
96
|
+
rawContent = js_yaml_1.default.load(node_fs_1.default.readFileSync(file, 'utf-8'));
|
|
97
97
|
}
|
|
98
98
|
else {
|
|
99
|
-
rawContent = JSON.parse(node_fs_1.default.readFileSync(file,
|
|
99
|
+
rawContent = JSON.parse(node_fs_1.default.readFileSync(file, 'utf-8'));
|
|
100
100
|
}
|
|
101
101
|
const content = (0, flattenTranslations_1.flattenTranslations)(rawContent);
|
|
102
102
|
if (isSource({ file, name, path }, srcPath)) {
|
|
@@ -107,12 +107,12 @@ const main = async () => {
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
|
-
const fullPath = path.join(
|
|
110
|
+
const fullPath = path.join('-');
|
|
111
111
|
const reference = fileInfos.find((fileInfo) => {
|
|
112
112
|
if (!isSource(fileInfo, srcPath)) {
|
|
113
113
|
return false;
|
|
114
114
|
}
|
|
115
|
-
if (fileInfo.path.join(
|
|
115
|
+
if (fileInfo.path.join('-') === fullPath) {
|
|
116
116
|
return true;
|
|
117
117
|
}
|
|
118
118
|
// Check if the folder path matches - ignoring the last folder
|
|
@@ -130,8 +130,8 @@ const main = async () => {
|
|
|
130
130
|
//
|
|
131
131
|
// Referencing: `path/to/locales/en-US/one.json`, `path/to/locales/de-DE/one.json`
|
|
132
132
|
// Non Referencing: `path/to/locales/en-US/one.json`, `path/to/other/locales/de-DE/one.json`
|
|
133
|
-
if (fileInfo.path.slice(0, fileInfo.path.length - 1).join(
|
|
134
|
-
path.slice(0, path.length - 1).join(
|
|
133
|
+
if (fileInfo.path.slice(0, fileInfo.path.length - 1).join('-') ===
|
|
134
|
+
path.slice(0, path.length - 1).join('-')) {
|
|
135
135
|
return fileInfo.name === name;
|
|
136
136
|
}
|
|
137
137
|
return false;
|
|
@@ -146,13 +146,13 @@ const main = async () => {
|
|
|
146
146
|
}
|
|
147
147
|
});
|
|
148
148
|
if (srcFiles.length === 0) {
|
|
149
|
-
console.log(chalk_1.default.red(
|
|
149
|
+
console.log(chalk_1.default.red('Source not found. Please provide a valid source locale, i.e. -s en-US'));
|
|
150
150
|
(0, node_process_1.exit)(1);
|
|
151
151
|
}
|
|
152
|
-
if ((options.checks.includes(
|
|
153
|
-
options.checks.includes(
|
|
152
|
+
if ((options.checks.includes('missingKeys') ||
|
|
153
|
+
options.checks.includes('invalidKeys')) &&
|
|
154
154
|
targetFiles.length === 0) {
|
|
155
|
-
console.log(chalk_1.default.red(
|
|
155
|
+
console.log(chalk_1.default.red('Locale file(s) not found. Please provide valid locale file(s), i.e. --locales translations/'));
|
|
156
156
|
(0, node_process_1.exit)(1);
|
|
157
157
|
}
|
|
158
158
|
try {
|
|
@@ -161,10 +161,10 @@ const main = async () => {
|
|
|
161
161
|
if (unusedSrcPath) {
|
|
162
162
|
const isMultiUnusedFolders = unusedSrcPath.length > 1;
|
|
163
163
|
const pattern = isMultiUnusedFolders
|
|
164
|
-
? `{${unusedSrcPath.join(
|
|
165
|
-
: `${unusedSrcPath.join(
|
|
164
|
+
? `{${unusedSrcPath.join(',').trim()}}/**/*.{ts,tsx}`
|
|
165
|
+
: `${unusedSrcPath.join(',').trim()}/**/*.{ts,tsx}`;
|
|
166
166
|
const filesToParse = (0, glob_1.globSync)(pattern, {
|
|
167
|
-
ignore: [
|
|
167
|
+
ignore: ['node_modules/**'],
|
|
168
168
|
windowsPathsNoEscape: true,
|
|
169
169
|
});
|
|
170
170
|
const unusedKeys = await (0, __1.checkUnusedKeys)(srcFiles, filesToParse, options, componentFunctions);
|
|
@@ -183,6 +183,7 @@ const main = async () => {
|
|
|
183
183
|
else {
|
|
184
184
|
(0, node_process_1.exit)(0);
|
|
185
185
|
}
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
186
187
|
}
|
|
187
188
|
catch (e) {
|
|
188
189
|
console.log(chalk_1.default.red("\nError: Can't validate translations. Check if the format is supported or specify the translation format i.e. -f i18next"));
|
|
@@ -190,10 +191,10 @@ const main = async () => {
|
|
|
190
191
|
}
|
|
191
192
|
};
|
|
192
193
|
const printTranslationResult = ({ missingKeys, invalidKeys, }) => {
|
|
193
|
-
const reporter = commander_1.program.getOptionValue(
|
|
194
|
-
const isSummary = reporter ===
|
|
194
|
+
const reporter = commander_1.program.getOptionValue('reporter');
|
|
195
|
+
const isSummary = reporter === 'summary';
|
|
195
196
|
if (missingKeys && Object.keys(missingKeys).length > 0) {
|
|
196
|
-
console.log(chalk_1.default.red(
|
|
197
|
+
console.log(chalk_1.default.red('\nFound missing keys!'));
|
|
197
198
|
if (isSummary) {
|
|
198
199
|
console.log(chalk_1.default.red((0, errorReporters_1.formatSummaryTable)(missingKeys)));
|
|
199
200
|
}
|
|
@@ -203,10 +204,10 @@ const printTranslationResult = ({ missingKeys, invalidKeys, }) => {
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
else if (missingKeys) {
|
|
206
|
-
console.log(chalk_1.default.green(
|
|
207
|
+
console.log(chalk_1.default.green('\nNo missing keys found!'));
|
|
207
208
|
}
|
|
208
209
|
if (invalidKeys && Object.keys(invalidKeys).length > 0) {
|
|
209
|
-
console.log(chalk_1.default.red(
|
|
210
|
+
console.log(chalk_1.default.red('\nFound invalid keys!'));
|
|
210
211
|
if (isSummary) {
|
|
211
212
|
console.log(chalk_1.default.red((0, errorReporters_1.formatSummaryTable)(invalidKeys)));
|
|
212
213
|
}
|
|
@@ -216,14 +217,14 @@ const printTranslationResult = ({ missingKeys, invalidKeys, }) => {
|
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
else if (invalidKeys) {
|
|
219
|
-
console.log(chalk_1.default.green(
|
|
220
|
+
console.log(chalk_1.default.green('\nNo invalid translations found!'));
|
|
220
221
|
}
|
|
221
222
|
};
|
|
222
223
|
const printUnusedKeysResult = ({ unusedKeys, }) => {
|
|
223
|
-
const reporter = commander_1.program.getOptionValue(
|
|
224
|
-
const isSummary = reporter ===
|
|
224
|
+
const reporter = commander_1.program.getOptionValue('reporter');
|
|
225
|
+
const isSummary = reporter === 'summary';
|
|
225
226
|
if (unusedKeys && hasKeys(unusedKeys)) {
|
|
226
|
-
console.log(chalk_1.default.red(
|
|
227
|
+
console.log(chalk_1.default.red('\nFound unused keys!'));
|
|
227
228
|
if (isSummary) {
|
|
228
229
|
console.log(chalk_1.default.red((0, errorReporters_1.formatSummaryTable)(unusedKeys)));
|
|
229
230
|
}
|
|
@@ -232,14 +233,14 @@ const printUnusedKeysResult = ({ unusedKeys, }) => {
|
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
else if (unusedKeys) {
|
|
235
|
-
console.log(chalk_1.default.green(
|
|
236
|
+
console.log(chalk_1.default.green('\nNo unused keys found!'));
|
|
236
237
|
}
|
|
237
238
|
};
|
|
238
239
|
const printUndefinedKeysResult = ({ undefinedKeys, }) => {
|
|
239
|
-
const reporter = commander_1.program.getOptionValue(
|
|
240
|
-
const isSummary = reporter ===
|
|
240
|
+
const reporter = commander_1.program.getOptionValue('reporter');
|
|
241
|
+
const isSummary = reporter === 'summary';
|
|
241
242
|
if (undefinedKeys && hasKeys(undefinedKeys)) {
|
|
242
|
-
console.log(chalk_1.default.red(
|
|
243
|
+
console.log(chalk_1.default.red('\nFound undefined keys!'));
|
|
243
244
|
if (isSummary) {
|
|
244
245
|
console.log(chalk_1.default.red((0, errorReporters_1.formatSummaryTable)(undefinedKeys)));
|
|
245
246
|
}
|
|
@@ -248,7 +249,7 @@ const printUndefinedKeysResult = ({ undefinedKeys, }) => {
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
else if (undefinedKeys) {
|
|
251
|
-
console.log(chalk_1.default.green(
|
|
252
|
+
console.log(chalk_1.default.green('\nNo undefined keys found!'));
|
|
252
253
|
}
|
|
253
254
|
};
|
|
254
255
|
const hasKeys = (checkResult) => {
|