@lingual/i18n-check 0.5.0 → 0.5.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/README.md CHANGED
@@ -112,28 +112,28 @@ Hint: If you want to use the `--unused` flag, you should provide `react-intl` or
112
112
  yarn i18n:check --locales translations/i18NextMessageExamples -s en-US -f i18next
113
113
  ```
114
114
 
115
- ### --check, -c
115
+ ### --only, -o
116
116
 
117
- By default i18n-check will perform a validation against any **missing** and/or **invalid** keys. There are situations where only a specific check should run. By using the `-c` or `--check` option you can specify a specific check to run.
117
+ By default i18n-check will perform a validation against any **missing** and/or **invalid** keys. There are situations where only a specific check should run. By using the `-o` or `--only` option you can specify a specific check to run.
118
118
 
119
119
  The available options are `missingKeys`, which will check against any missing keys in the target files and `invalidKeys` will check for invalid keys, where the target translations has a different type then the one defined in the source file.
120
120
 
121
121
  Check for missing keys:
122
122
 
123
123
  ```bash
124
- yarn i18n:check --locales translations/messageExamples -s en-US -c missingKeys
124
+ yarn i18n:check --locales translations/messageExamples -s en-US -o missingKeys
125
125
  ```
126
126
 
127
127
  Check for invalid keys:
128
128
 
129
129
  ```bash
130
- yarn i18n:check --locales translations/messageExamples -s en-US -c invalidKeys
130
+ yarn i18n:check --locales translations/messageExamples -s en-US -o invalidKeys
131
131
  ```
132
132
 
133
133
  Check for missing and invalid keys (which is the default):
134
134
 
135
135
  ```bash
136
- yarn i18n:check --locales translations/messageExamples -s en-US -c missingKeys,invalidKeys
136
+ yarn i18n:check --locales translations/messageExamples -s en-US -o missingKeys invalidKeys
137
137
  ```
138
138
 
139
139
  ### --unused, -u
@@ -192,6 +192,20 @@ yarn i18n:check --locales translations/folderExamples -s en-US -e translations/f
192
192
  The `--exclude` option also accepts a mix of files and folders, which follows the same pattern as above, i.e.
193
193
  `-e translations/folderExamples/fr/* translations/messageExamples/it.json`
194
194
 
195
+ ### --parser-component-functions
196
+
197
+ 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.
198
+
199
+ ```bash
200
+ yarn i18n:check --locales translations/i18NextMessageExamples -s en-US -f i18next
201
+ -u src --parser-component-functions WrappedTransComponent
202
+ ```
203
+
204
+ ```bash
205
+ yarn i18n:check --locales translations/i18NextMessageExamples -s en-US -f i18next
206
+ -u src --parser-component-functions WrappedTransComponent AnotherWrappedTransComponent
207
+ ```
208
+
195
209
  ## Examples
196
210
 
197
211
  i18n-check is able to load and validate against different locale folder structures. Depending on how the locale files are organized, there are different configuration options.
@@ -206,7 +220,7 @@ locales/
206
220
  de-de.json
207
221
  ```
208
222
 
209
- Use the `t` or `target` option to define the directory that should be checked for target files. With the `s` or `source` option you can specify the base/reference file to compare the target files against.
223
+ Use the `-l` or `--locales` option to define the directory that should be checked for target files. With the `s` or `source` option you can specify the base/reference file to compare the target files against.
210
224
 
211
225
  ```bash
212
226
  yarn i18n:check --locales locales -s locales/en-us.json
package/dist/bin/index.js CHANGED
@@ -13,33 +13,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
+ const node_fs_1 = __importDefault(require("node:fs"));
17
+ const node_process_1 = require("node:process");
16
18
  const chalk_1 = __importDefault(require("chalk"));
17
19
  const commander_1 = require("commander");
18
- const fs_1 = __importDefault(require("fs"));
19
20
  const glob_1 = require("glob");
20
21
  const js_yaml_1 = __importDefault(require("js-yaml"));
21
- const process_1 = require("process");
22
22
  const __1 = require("..");
23
23
  const errorReporters_1 = require("../errorReporters");
24
24
  const flattenTranslations_1 = require("../utils/flattenTranslations");
25
+ const version = require("../../package.json").version;
25
26
  commander_1.program
26
- .version("0.3.0")
27
+ .version(version)
27
28
  .option("-l, --locales <locales...>", "name of the directory containing the locales to validate")
28
- .option("-s, --source [source locale]", "the source locale to validate against")
29
- .option("-f, --format [format type]", "define the specific format: i18next or react-intl")
30
- .option("-c, --check [checks]", "define the specific checks you want to run: invalid, missing. By default the check will validate against missing and invalid keys, i.e. --check invalidKeys,missingKeys")
31
- .option("-r, --reporter [error reporting style]", "define the reporting style: standard or summary")
29
+ .option("-s, --source <locale>", "the source locale to validate against")
30
+ .option("-f, --format <type>", "define the specific format: i18next or react-intl")
31
+ .option("-c, --check <checks...>", "this option is deprecated - use -o or --only instead")
32
+ .option("-o, --only <only...>", "define the specific checks you want to run: invalid, missing. By default the check will validate against missing and invalid keys, i.e. --only invalidKeys,missingKeys")
33
+ .option("-r, --reporter <style>", "define the reporting style: standard or summary")
32
34
  .option("-e, --exclude <exclude...>", "define the file(s) and/or folders(s) that should be excluded from the check")
33
- .option("-u, --unused [folder]", "define the source path to find all unused keys")
35
+ .option("-u, --unused <path>", "define the source path to find all unused keys")
36
+ .option("--parser-component-functions <components...>", "a list of component names to parse when using the --unused option")
34
37
  .parse();
35
38
  const getCheckOptions = () => {
36
- const checkOption = commander_1.program.getOptionValue("check");
39
+ const checkOption = commander_1.program.getOptionValue("only") || commander_1.program.getOptionValue("check");
40
+ if (commander_1.program.getOptionValue("check")) {
41
+ console.log(chalk_1.default.yellow("The --check option has been deprecated, use the --only option instead."));
42
+ }
37
43
  if (!checkOption) {
38
44
  return ["invalidKeys", "missingKeys"];
39
45
  }
40
- const checks = checkOption
41
- .split(",")
42
- .filter((check) => ["invalidKeys", "missingKeys"].includes(check.trim()));
46
+ const checks = checkOption.filter((check) => ["invalidKeys", "missingKeys"].includes(check.trim()));
43
47
  return checks.length > 0 ? checks : ["invalidKeys", "missingKeys"];
44
48
  };
45
49
  const isSource = (fileInfo, srcPath) => {
@@ -52,13 +56,14 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
52
56
  const format = commander_1.program.getOptionValue("format");
53
57
  const exclude = commander_1.program.getOptionValue("exclude");
54
58
  const unusedSrcPath = commander_1.program.getOptionValue("unused");
59
+ const componentFunctions = commander_1.program.getOptionValue("parserComponentFunctions");
55
60
  if (!srcPath) {
56
61
  console.log(chalk_1.default.red("Source not found. Please provide a valid source locale, i.e. -s en-US"));
57
- (0, process_1.exit)(1);
62
+ (0, node_process_1.exit)(1);
58
63
  }
59
64
  if (!localePath || localePath.length === 0) {
60
65
  console.log(chalk_1.default.red("Locale file(s) not found. Please provide valid locale file(s), i.e. -locales translations/"));
61
- (0, process_1.exit)(1);
66
+ (0, node_process_1.exit)(1);
62
67
  }
63
68
  const excludedPaths = exclude !== null && exclude !== void 0 ? exclude : [];
64
69
  const localePathFolders = localePath;
@@ -96,10 +101,10 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
96
101
  fileInfos.forEach(({ extension, file, name, path }) => {
97
102
  let rawContent;
98
103
  if (extension === "yaml") {
99
- rawContent = js_yaml_1.default.load(fs_1.default.readFileSync(file, "utf-8"));
104
+ rawContent = js_yaml_1.default.load(node_fs_1.default.readFileSync(file, "utf-8"));
100
105
  }
101
106
  else {
102
- rawContent = JSON.parse(fs_1.default.readFileSync(file, "utf-8"));
107
+ rawContent = JSON.parse(node_fs_1.default.readFileSync(file, "utf-8"));
103
108
  }
104
109
  const content = (0, flattenTranslations_1.flattenTranslations)(rawContent);
105
110
  if (isSource({ file, name, path }, srcPath)) {
@@ -150,32 +155,32 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
150
155
  });
151
156
  if (srcFiles.length === 0) {
152
157
  console.log(chalk_1.default.red("Source not found. Please provide a valid source locale, i.e. -s en-US"));
153
- (0, process_1.exit)(1);
158
+ (0, node_process_1.exit)(1);
154
159
  }
155
160
  if (localeFiles.length === 0) {
156
161
  console.log(chalk_1.default.red("Locale file(s) not found. Please provide valid locale file(s), i.e. --locales translations/"));
157
- (0, process_1.exit)(1);
162
+ (0, node_process_1.exit)(1);
158
163
  }
159
164
  try {
160
165
  const result = (0, __1.checkTranslations)(srcFiles, localeFiles, options);
161
166
  printTranslationResult(result);
162
167
  if (unusedSrcPath) {
163
- const unusedKeys = yield (0, __1.checkUnusedKeys)(srcFiles, unusedSrcPath, options);
168
+ const unusedKeys = yield (0, __1.checkUnusedKeys)(srcFiles, unusedSrcPath, options, componentFunctions);
164
169
  printUnusedKeysResult({ unusedKeys });
165
170
  }
166
171
  const end = performance.now();
167
172
  console.log(chalk_1.default.green(`\nDone in ${Math.round(((end - start) * 100) / 1000) / 100}s.`));
168
173
  if ((result.missingKeys && Object.keys(result.missingKeys).length > 0) ||
169
174
  (result.invalidKeys && Object.keys(result.invalidKeys).length > 0)) {
170
- (0, process_1.exit)(1);
175
+ (0, node_process_1.exit)(1);
171
176
  }
172
177
  else {
173
- (0, process_1.exit)(0);
178
+ (0, node_process_1.exit)(0);
174
179
  }
175
180
  }
176
181
  catch (e) {
177
182
  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"));
178
- (0, process_1.exit)(1);
183
+ (0, node_process_1.exit)(1);
179
184
  }
180
185
  });
181
186
  const printTranslationResult = ({ missingKeys, invalidKeys, }) => {
@@ -265,7 +265,7 @@ No invalid translations found!
265
265
  });
266
266
  });
267
267
  it("should find unused keys for react-i18next applications", (done) => {
268
- (0, child_process_1.exec)("node dist/bin/index.js --source en --locales translations/codeExamples/reacti18next/locales -f i18next -u translations/codeExamples/reacti18next/src", (_error, stdout, _stderr) => {
268
+ (0, child_process_1.exec)("node dist/bin/index.js --source en --locales translations/codeExamples/reacti18next/locales -f i18next -u translations/codeExamples/reacti18next/src --parser-component-functions WrappedTransComponent", (_error, stdout, _stderr) => {
269
269
  const result = stdout.split("Done")[0];
270
270
  expect(result).toEqual(`i18n translations checker
271
271
  Source: en
package/dist/index.d.ts CHANGED
@@ -10,4 +10,4 @@ export declare const checkTranslations: (source: TranslationFile[], targets: Tra
10
10
  missingKeys: CheckResult | undefined;
11
11
  invalidKeys: CheckResult | undefined;
12
12
  };
13
- export declare const checkUnusedKeys: (source: TranslationFile[], codebaseSrc: string, options?: Options) => Promise<CheckResult | undefined>;
13
+ export declare const checkUnusedKeys: (source: TranslationFile[], codebaseSrc: string, options?: Options, componentFunctions?: never[]) => Promise<CheckResult | undefined>;
package/dist/index.js CHANGED
@@ -57,22 +57,22 @@ const checkTranslations = (source, targets, options = { format: "icu", checks: [
57
57
  exports.checkTranslations = checkTranslations;
58
58
  const checkUnusedKeys = (source_1, codebaseSrc_1, ...args_1) => __awaiter(void 0, [source_1, codebaseSrc_1, ...args_1], void 0, function* (source, codebaseSrc, options = {
59
59
  format: "react-intl",
60
- }) {
60
+ }, componentFunctions = []) {
61
61
  if (!options.format || !["react-intl", "i18next"].includes(options.format)) {
62
62
  return undefined;
63
63
  }
64
64
  return options.format === "react-intl"
65
65
  ? findUnusedReactIntlTranslations(source, codebaseSrc)
66
- : findUnusedi18NextTranslations(source, codebaseSrc);
66
+ : findUnusedI18NextTranslations(source, codebaseSrc, componentFunctions);
67
67
  });
68
68
  exports.checkUnusedKeys = checkUnusedKeys;
69
69
  const findUnusedReactIntlTranslations = (source, codebaseSrc) => __awaiter(void 0, void 0, void 0, function* () {
70
70
  let unusedKeys = {};
71
71
  // find any unused keys in a react-intl code base
72
- const unsuedKeysFiles = (0, glob_1.globSync)(codebaseSrc, {
72
+ const unusedKeysFiles = (0, glob_1.globSync)(codebaseSrc, {
73
73
  ignore: ["node_modules/**"],
74
74
  });
75
- const extracted = yield (0, cli_lib_1.extract)(unsuedKeysFiles, {});
75
+ const extracted = yield (0, cli_lib_1.extract)(unusedKeysFiles, {});
76
76
  const extractedResultSet = new Set(Object.keys(JSON.parse(extracted)));
77
77
  source.forEach(({ name, content }) => {
78
78
  const keysInSource = Object.keys(content);
@@ -86,7 +86,7 @@ const findUnusedReactIntlTranslations = (source, codebaseSrc) => __awaiter(void
86
86
  });
87
87
  return unusedKeys;
88
88
  });
89
- const findUnusedi18NextTranslations = (source, codebaseSrc) => __awaiter(void 0, void 0, void 0, function* () {
89
+ const findUnusedI18NextTranslations = (source_1, codebaseSrc_1, ...args_1) => __awaiter(void 0, [source_1, codebaseSrc_1, ...args_1], void 0, function* (source, codebaseSrc, componentFunctions = []) {
90
90
  let unusedKeys = {};
91
91
  // find any unused keys in a react-i18next code base
92
92
  const unusedKeysFiles = (0, glob_1.globSync)(`${codebaseSrc}/**/*.tsx`, {
@@ -97,7 +97,22 @@ const findUnusedi18NextTranslations = (source, codebaseSrc) => __awaiter(void 0,
97
97
  const { transform } = yield import("i18next-parser");
98
98
  unusedKeysFiles.forEach((file) => {
99
99
  const rawContent = fs_1.default.readFileSync(file);
100
- const i18nextParser = new transform();
100
+ const i18nextParser = new transform({
101
+ lexers: {
102
+ jsx: [
103
+ {
104
+ lexer: "JsxLexer",
105
+ componentFunctions: componentFunctions.concat(["Trans"]),
106
+ },
107
+ ],
108
+ tsx: [
109
+ {
110
+ lexer: "JsxLexer",
111
+ componentFunctions: componentFunctions.concat(["Trans"]),
112
+ },
113
+ ],
114
+ },
115
+ });
101
116
  i18nextParser.once("data", (file) => {
102
117
  extractedResult = extractedResult.concat(Object.keys(flatten(JSON.parse(file.contents))));
103
118
  });
@@ -11,6 +11,11 @@ describe("findInvalid18nTranslations:compareTranslationFiles", () => {
11
11
  it("should return the invalid keys in the target file", () => {
12
12
  expect((0, findInvalidi18nTranslations_1.compareTranslationFiles)((0, flattenTranslations_1.flattenTranslations)(Object.assign(Object.assign({}, sourceFile), { "ten.eleven.twelve": "ten eleven twelve" })), (0, flattenTranslations_1.flattenTranslations)(targetFile))).toEqual(["key_with_broken_de", "intlNumber_broken_de"]);
13
13
  });
14
+ it("should return an empty array if the strings contain paranthesis that have different content", () => {
15
+ expect((0, findInvalidi18nTranslations_1.compareTranslationFiles)((0, flattenTranslations_1.flattenTranslations)({
16
+ keyText: "Key(s)",
17
+ }), (0, flattenTranslations_1.flattenTranslations)({ keyText: "Taste(n)" }))).toEqual([]);
18
+ });
14
19
  it("should return empty array if placeholders are identical but in different positions", () => {
15
20
  expect((0, findInvalidi18nTranslations_1.compareTranslationFiles)({
16
21
  basic: "added {{this}} and {{that}} should work.",
@@ -2,7 +2,7 @@
2
2
  // Based on https://github.com/i18next/i18next-translation-parser/blob/v1.0.0/src/parse.js
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.parse = void 0;
5
- const REGEXP = new RegExp("({{[^}]+}}|\\$t{[^}]+}|\\$t\\([^\\)]+\\)|\\([0-9\\-inf]+\\)|<[^>]+>)", "g");
5
+ const REGEXP = new RegExp("({{[^}]+}}|\\$t{[^}]+}|\\$t\\([^\\)]+\\)|\\([0-9\\-inf]+\\)(?=\\[)|<[^>]+>)", "g");
6
6
  const DOUBLE_BRACE = "{{";
7
7
  const $_T_BRACE = "$t{";
8
8
  const $_T_PARENTHESIS = "$t(";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingual/i18n-check",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "i18n translation messages check",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -40,5 +40,8 @@
40
40
  "repository": {
41
41
  "type": "git",
42
42
  "url": "https://github.com/lingualdev/i18n-check.git"
43
+ },
44
+ "engines": {
45
+ "node": ">=20"
43
46
  }
44
47
  }