@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 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) => {
@@ -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 hasMissingKeys = checks.includes('missingKeys');
30
- const hasInvalidKeys = checks.includes('invalidKeys');
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 (hasMissingKeys) {
36
- merge(missingKeys, (0, exports.checkMissingTranslations)(content, files));
35
+ if (hasMissingKeysCheck) {
36
+ const filteredContent = filterKeys(content, options.ignore ?? []);
37
+ merge(missingKeys, (0, exports.checkMissingTranslations)(filteredContent, files));
37
38
  }
38
- if (hasInvalidKeys) {
39
+ if (hasInvalidKeysCheck) {
39
40
  merge(invalidKeys, (0, exports.checkInvalidTranslations)(content, files, options));
40
41
  }
41
42
  });
42
43
  return {
43
- missingKeys: hasMissingKeys ? missingKeys : undefined,
44
- invalidKeys: hasInvalidKeys ? invalidKeys : undefined,
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() ?? "").split(".");
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingual/i18n-check",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "i18n translation messages check",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",