@lingual/i18n-check 0.3.1 → 0.5.0

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
@@ -63,7 +63,7 @@ node_modules/.bin/i18n-check
63
63
 
64
64
  ## General Usage
65
65
 
66
- For i18n-check to work you need to provide it at a minimum the source locale (`--source, -s`) for the primary language and the path to the locale translation files (`--locales, -l`).
66
+ For i18n-check to work you need to provide it at a minimum the source locale (`--source, -s`) for the primary language and the path to the locale translation files (`--locales, -l`). Currently supported file formats are JSON and YAML.
67
67
 
68
68
  Example:
69
69
 
@@ -106,7 +106,7 @@ Additionally the `i18next` format is supported and can be set via the `-f` or `-
106
106
 
107
107
  There are i18n libraries that have their own specific format, which might not be based on ICU and therefore can not be validated against currently. On a side-note: there might be future support for more specific formats.
108
108
 
109
- Hint: If you want to use the `--unused` flag, you should provide react-intl as the format. Also see the [`unused` section](#--unused) for more details.
109
+ Hint: If you want to use the `--unused` flag, you should provide `react-intl` or `i18-next` as the format. Also see the [`unused` section](#--unused) for more details.
110
110
 
111
111
  ```bash
112
112
  yarn i18n:check --locales translations/i18NextMessageExamples -s en-US -f i18next
@@ -138,14 +138,20 @@ yarn i18n:check --locales translations/messageExamples -s en-US -c missingKeys,i
138
138
 
139
139
  ### --unused, -u
140
140
 
141
- This feature is currently only supported for react-intl and is useful if you need to know which keys exist in your translation files but not in your codebase. Via the `-u` or `--unused` option you provide a source path to the code, which will be parsed to find all unused keys in the primary target language.
141
+ This feature is currently only supported for `react-intl` and `i18next` based React applications and is useful when you need to know which keys exist in your translation files but not in your codebase. Via the `-u` or `--unused` option you provide a source path to the code, which will be parsed to find all unused keys in the primary target language.
142
142
 
143
- It is important to note that you must also provide the `-f` or `--format` option with `react-intl` as value. See the [`format` section](#--format) for more information.
143
+ It is important to note that you must also provide the `-f` or `--format` option with `react-intl` or `i18next` as value. See the [`format` section](#--format) for more information.
144
144
 
145
145
  ```bash
146
146
  yarn i18n:check --locales translations/messageExamples -s en-US -u client/ -f react-intl
147
147
  ```
148
148
 
149
+ or
150
+
151
+ ```bash
152
+ yarn i18n:check --locales translations/messageExamples -s en-US -u client/ -f i18next
153
+ ```
154
+
149
155
  ### --reporter, -r
150
156
 
151
157
  The standard reporting prints out all the missing or invalid keys.
@@ -208,7 +214,7 @@ yarn i18n:check --locales locales -s locales/en-us.json
208
214
 
209
215
  ### Folder per locale
210
216
 
211
- If the locales are **organised as folders** containing a single json file:
217
+ If the locales are **organised as folders** containing a single JSON/YAML file:
212
218
 
213
219
  ```
214
220
  locales/
@@ -226,7 +232,7 @@ yarn i18n:check --locales locales -s en-US
226
232
 
227
233
  ### Folder per locale with multiple files
228
234
 
229
- If the locales are **organised as folders** containing multiple json files:
235
+ If the locales are **organised as folders** containing multiple JSON/YAML files:
230
236
 
231
237
  ```
232
238
  locales/
@@ -248,7 +254,7 @@ yarn i18n:check --locales locales -s en-US
248
254
 
249
255
  #### Multiple folders containing locales
250
256
 
251
- If the locales are **organised as folders** containing multiple json files:
257
+ If the locales are **organised as folders** containing multiple JSON/YAML files:
252
258
 
253
259
  ```
254
260
  - spaceOne
package/dist/bin/index.js CHANGED
@@ -13,11 +13,12 @@ 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 glob_1 = require("glob");
17
16
  const chalk_1 = __importDefault(require("chalk"));
17
+ const commander_1 = require("commander");
18
18
  const fs_1 = __importDefault(require("fs"));
19
+ const glob_1 = require("glob");
20
+ const js_yaml_1 = __importDefault(require("js-yaml"));
19
21
  const process_1 = require("process");
20
- const commander_1 = require("commander");
21
22
  const __1 = require("..");
22
23
  const errorReporters_1 = require("../errorReporters");
23
24
  const flattenTranslations_1 = require("../utils/flattenTranslations");
@@ -25,7 +26,7 @@ commander_1.program
25
26
  .version("0.3.0")
26
27
  .option("-l, --locales <locales...>", "name of the directory containing the locales to validate")
27
28
  .option("-s, --source [source locale]", "the source locale to validate against")
28
- .option("-f, --format [format type]", "define the specific format, i.e. i18next")
29
+ .option("-f, --format [format type]", "define the specific format: i18next or react-intl")
29
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")
30
31
  .option("-r, --reporter [error reporting style]", "define the reporting style: standard or summary")
31
32
  .option("-e, --exclude <exclude...>", "define the file(s) and/or folders(s) that should be excluded from the check")
@@ -65,8 +66,8 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
65
66
  let srcFiles = [];
66
67
  let localeFiles = [];
67
68
  const pattern = isMultiFolders
68
- ? `{${localePath.join(",").trim()}}/**/*.json`
69
- : `${localePath.join(",").trim()}/**/*.json`;
69
+ ? `{${localePath.join(",").trim()}}/**/*.{json,yaml,yml}`
70
+ : `${localePath.join(",").trim()}/**/*.{json,yaml,yml}`;
70
71
  const files = yield (0, glob_1.glob)(pattern, {
71
72
  ignore: ["node_modules/**"].concat(excludedPaths),
72
73
  });
@@ -81,17 +82,25 @@ const main = () => __awaiter(void 0, void 0, void 0, function* () {
81
82
  };
82
83
  const fileInfos = [];
83
84
  files.sort().forEach((file) => {
84
- var _a;
85
+ var _a, _b;
85
86
  const path = file.split("/");
86
87
  const name = (_a = path.pop()) !== null && _a !== void 0 ? _a : "";
88
+ const extension = (_b = name.split(".").pop()) !== null && _b !== void 0 ? _b : "json";
87
89
  fileInfos.push({
90
+ extension,
88
91
  file,
89
- path,
90
92
  name,
93
+ path,
91
94
  });
92
95
  });
93
- fileInfos.forEach(({ file, name, path }) => {
94
- const rawContent = JSON.parse(fs_1.default.readFileSync(file, "utf-8"));
96
+ fileInfos.forEach(({ extension, file, name, path }) => {
97
+ let rawContent;
98
+ if (extension === "yaml") {
99
+ rawContent = js_yaml_1.default.load(fs_1.default.readFileSync(file, "utf-8"));
100
+ }
101
+ else {
102
+ rawContent = JSON.parse(fs_1.default.readFileSync(file, "utf-8"));
103
+ }
95
104
  const content = (0, flattenTranslations_1.flattenTranslations)(rawContent);
96
105
  if (isSource({ file, name, path }, srcPath)) {
97
106
  srcFiles.push({
@@ -2,10 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const child_process_1 = require("child_process");
4
4
  describe("CLI", () => {
5
- it("should return the missing keys for single folder translations", (done) => {
6
- (0, child_process_1.exec)("node dist/bin/index.js -s en-US -l translations/flattenExamples", (_error, stdout, _stderr) => {
7
- const result = stdout.split("Done")[0];
8
- expect(result).toEqual(`i18n translations checker
5
+ describe("JSON", () => {
6
+ it("should return the missing keys for single folder translations", (done) => {
7
+ (0, child_process_1.exec)("node dist/bin/index.js -s en-US -l translations/flattenExamples", (_error, stdout, _stderr) => {
8
+ const result = stdout.split("Done")[0];
9
+ expect(result).toEqual(`i18n translations checker
9
10
  Source: en-US
10
11
 
11
12
  Found missing keys!
@@ -19,13 +20,13 @@ Found missing keys!
19
20
  No invalid translations found!
20
21
 
21
22
  `);
22
- done();
23
+ done();
24
+ });
23
25
  });
24
- });
25
- it("should return the missing/invalid keys for folder per locale with single file", (done) => {
26
- (0, child_process_1.exec)("node dist/bin/index.js -l translations/folderExample/ -s en-US", (_error, stdout, _stderr) => {
27
- const result = stdout.split("Done")[0];
28
- expect(result).toEqual(`i18n translations checker
26
+ it("should return the missing/invalid keys for folder per locale with single file", (done) => {
27
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/folderExample/ -s en-US", (_error, stdout, _stderr) => {
28
+ const result = stdout.split("Done")[0];
29
+ expect(result).toEqual(`i18n translations checker
29
30
  Source: en-US
30
31
 
31
32
  Found missing keys!
@@ -43,13 +44,13 @@ Found invalid keys!
43
44
  └───────────────────────────────────────────────┴──────────────────┘
44
45
 
45
46
  `);
46
- done();
47
+ done();
48
+ });
47
49
  });
48
- });
49
- it("should return the missing/invalid keys for folder per locale with multiple files", (done) => {
50
- (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFilesFolderExample/ -s en-US", (_error, stdout, _stderr) => {
51
- const result = stdout.split("Done")[0];
52
- expect(result).toEqual(`i18n translations checker
50
+ it("should return the missing/invalid keys for folder per locale with multiple files", (done) => {
51
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFilesFolderExample/ -s en-US", (_error, stdout, _stderr) => {
52
+ const result = stdout.split("Done")[0];
53
+ expect(result).toEqual(`i18n translations checker
53
54
  Source: en-US
54
55
 
55
56
  Found missing keys!
@@ -69,13 +70,13 @@ Found invalid keys!
69
70
  └────────────────────────────────────────────────────────────┴─────────────────────┘
70
71
 
71
72
  `);
72
- done();
73
+ done();
74
+ });
73
75
  });
74
- });
75
- it("should return the missing/invalid keys for folder containing multiple locale folders", (done) => {
76
- (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFoldersExample -s en-US", (_error, stdout, _stderr) => {
77
- const result = stdout.split("Done")[0];
78
- expect(result).toEqual(`i18n translations checker
76
+ it("should return the missing/invalid keys for folder containing multiple locale folders", (done) => {
77
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFoldersExample -s en-US", (_error, stdout, _stderr) => {
78
+ const result = stdout.split("Done")[0];
79
+ expect(result).toEqual(`i18n translations checker
79
80
  Source: en-US
80
81
 
81
82
  Found missing keys!
@@ -99,13 +100,13 @@ Found invalid keys!
99
100
  └─────────────────────────────────────────────────────────────────────────┴───────────────────────┘
100
101
 
101
102
  `);
102
- done();
103
+ done();
104
+ });
103
105
  });
104
- });
105
- it("should return the missing/invalid keys for multiple locale folders", (done) => {
106
- (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFoldersExample/spaceOne translations/multipleFoldersExample/spaceTwo -s en-US", (_error, stdout, _stderr) => {
107
- const result = stdout.split("Done")[0];
108
- expect(result).toEqual(`i18n translations checker
106
+ it("should return the missing/invalid keys for multiple locale folders", (done) => {
107
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFoldersExample/spaceOne translations/multipleFoldersExample/spaceTwo -s en-US", (_error, stdout, _stderr) => {
108
+ const result = stdout.split("Done")[0];
109
+ expect(result).toEqual(`i18n translations checker
109
110
  Source: en-US
110
111
 
111
112
  Found missing keys!
@@ -129,13 +130,13 @@ Found invalid keys!
129
130
  └─────────────────────────────────────────────────────────────────────────┴───────────────────────┘
130
131
 
131
132
  `);
132
- done();
133
+ done();
134
+ });
133
135
  });
134
- });
135
- it("should return the missing/invalid keys for all files in the provided locale folders", (done) => {
136
- (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples", (_error, stdout, _stderr) => {
137
- const result = stdout.split("Done")[0];
138
- expect(result).toEqual(`i18n translations checker
136
+ it("should return the missing/invalid keys for all files in the provided locale folders", (done) => {
137
+ (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples", (_error, stdout, _stderr) => {
138
+ const result = stdout.split("Done")[0];
139
+ expect(result).toEqual(`i18n translations checker
139
140
  Source: en-US
140
141
 
141
142
  Found missing keys!
@@ -160,13 +161,13 @@ Found invalid keys!
160
161
  └───────────────────────────────────────────┴─────────────────────┘
161
162
 
162
163
  `);
163
- done();
164
+ done();
165
+ });
164
166
  });
165
- });
166
- it("should return the missing/invalid keys for all files with source matching folder and source matching file", (done) => {
167
- (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFilesFolderExample translations/flattenExamples -s en-US", (_error, stdout, _stderr) => {
168
- const result = stdout.split("Done")[0];
169
- expect(result).toEqual(`i18n translations checker
167
+ it("should return the missing/invalid keys for all files with source matching folder and source matching file", (done) => {
168
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/multipleFilesFolderExample translations/flattenExamples -s en-US", (_error, stdout, _stderr) => {
169
+ const result = stdout.split("Done")[0];
170
+ expect(result).toEqual(`i18n translations checker
170
171
  Source: en-US
171
172
 
172
173
  Found missing keys!
@@ -188,13 +189,13 @@ Found invalid keys!
188
189
  └────────────────────────────────────────────────────────────┴─────────────────────┘
189
190
 
190
191
  `);
191
- done();
192
+ done();
193
+ });
192
194
  });
193
- });
194
- it("should ignore the excluded file", (done) => {
195
- (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples --exclude translations/flattenExamples/de-de.json", (_error, stdout, _stderr) => {
196
- const result = stdout.split("Done")[0];
197
- expect(result).toEqual(`i18n translations checker
195
+ it("should ignore the excluded file", (done) => {
196
+ (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples --exclude translations/flattenExamples/de-de.json", (_error, stdout, _stderr) => {
197
+ const result = stdout.split("Done")[0];
198
+ expect(result).toEqual(`i18n translations checker
198
199
  Source: en-US
199
200
 
200
201
  Found missing keys!
@@ -217,13 +218,13 @@ Found invalid keys!
217
218
  └───────────────────────────────────────────┴─────────────────────┘
218
219
 
219
220
  `);
220
- done();
221
+ done();
222
+ });
221
223
  });
222
- });
223
- it("should ignore the excluded folder", (done) => {
224
- (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples --exclude translations/flattenExamples/*", (_error, stdout, _stderr) => {
225
- const result = stdout.split("Done")[0];
226
- expect(result).toEqual(`i18n translations checker
224
+ it("should ignore the excluded folder", (done) => {
225
+ (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples --exclude translations/flattenExamples/*", (_error, stdout, _stderr) => {
226
+ const result = stdout.split("Done")[0];
227
+ expect(result).toEqual(`i18n translations checker
227
228
  Source: en-US
228
229
 
229
230
  Found missing keys!
@@ -246,13 +247,13 @@ Found invalid keys!
246
247
  └───────────────────────────────────────────┴─────────────────────┘
247
248
 
248
249
  `);
249
- done();
250
+ done();
251
+ });
250
252
  });
251
- });
252
- it("should ignore the excluded multiple files", (done) => {
253
- (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples --exclude translations/flattenExamples/de-de.json translations/messageExamples/de-de.json", (_error, stdout, _stderr) => {
254
- const result = stdout.split("Done")[0];
255
- expect(result).toEqual(`i18n translations checker
253
+ it("should ignore the excluded multiple files", (done) => {
254
+ (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/flattenExamples translations/messageExamples --exclude translations/flattenExamples/de-de.json translations/messageExamples/de-de.json", (_error, stdout, _stderr) => {
255
+ const result = stdout.split("Done")[0];
256
+ expect(result).toEqual(`i18n translations checker
256
257
  Source: en-US
257
258
 
258
259
  No missing keys found!
@@ -260,7 +261,222 @@ No missing keys found!
260
261
  No invalid translations found!
261
262
 
262
263
  `);
263
- done();
264
+ done();
265
+ });
266
+ });
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) => {
269
+ const result = stdout.split("Done")[0];
270
+ expect(result).toEqual(`i18n translations checker
271
+ Source: en
272
+ Selected format is: i18next
273
+
274
+ No missing keys found!
275
+
276
+ No invalid translations found!
277
+
278
+ Found unused keys!
279
+ ┌──────────────────────────────────────────────────────────────────────┬──────────────────┐
280
+ │ file │ key │
281
+ ├──────────────────────────────────────────────────────────────────────┼──────────────────┤
282
+ │ translations/codeExamples/reacti18next/locales/en/translation.json │ format.ebook │
283
+ │ translations/codeExamples/reacti18next/locales/en/translation.json │ nonExistentKey │
284
+ └──────────────────────────────────────────────────────────────────────┴──────────────────┘
285
+
286
+ `);
287
+ done();
288
+ });
289
+ });
290
+ });
291
+ describe("YAML", () => {
292
+ it("should return the missing keys for single folder translations", (done) => {
293
+ (0, child_process_1.exec)("node dist/bin/index.js -s en-US -l translations/yaml/flattenExamples", (_error, stdout, _stderr) => {
294
+ const result = stdout.split("Done")[0];
295
+ expect(result).toEqual(`i18n translations checker
296
+ Source: en-US
297
+
298
+ Found missing keys!
299
+ ┌────────────────────────────────────────────────┬────────────────────────────────┐
300
+ │ file │ key │
301
+ ├────────────────────────────────────────────────┼────────────────────────────────┤
302
+ │ translations/yaml/flattenExamples/de-de.yaml │ other.nested.three │
303
+ │ translations/yaml/flattenExamples/de-de.yaml │ other.nested.deep.more.final │
304
+ └────────────────────────────────────────────────┴────────────────────────────────┘
305
+
306
+ No invalid translations found!
307
+
308
+ `);
309
+ done();
310
+ });
311
+ });
312
+ it("should return the missing/invalid keys for folder per locale with single file", (done) => {
313
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/yaml/folderExample/ -s en-US", (_error, stdout, _stderr) => {
314
+ const result = stdout.split("Done")[0];
315
+ expect(result).toEqual(`i18n translations checker
316
+ Source: en-US
317
+
318
+ Found missing keys!
319
+ ┌────────────────────────────────────────────────────┬───────────────────────┐
320
+ │ file │ key │
321
+ ├────────────────────────────────────────────────────┼───────────────────────┤
322
+ │ translations/yaml/folderExample/de-DE/index.yaml │ message.text-format │
323
+ └────────────────────────────────────────────────────┴───────────────────────┘
324
+
325
+ Found invalid keys!
326
+ ┌────────────────────────────────────────────────────┬──────────────────┐
327
+ │ file │ key │
328
+ ├────────────────────────────────────────────────────┼──────────────────┤
329
+ │ translations/yaml/folderExample/de-DE/index.yaml │ message.select │
330
+ └────────────────────────────────────────────────────┴──────────────────┘
331
+
332
+ `);
333
+ done();
334
+ });
335
+ });
336
+ it("should return the missing/invalid keys for folder per locale with multiple files", (done) => {
337
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/yaml/multipleFilesFolderExample/ -s en-US", (_error, stdout, _stderr) => {
338
+ const result = stdout.split("Done")[0];
339
+ expect(result).toEqual(`i18n translations checker
340
+ Source: en-US
341
+
342
+ Found missing keys!
343
+ ┌───────────────────────────────────────────────────────────────┬───────────────────────┐
344
+ │ file │ key │
345
+ ├───────────────────────────────────────────────────────────────┼───────────────────────┤
346
+ │ translations/yaml/multipleFilesFolderExample/de-DE/one.yaml │ message.text-format │
347
+ │ translations/yaml/multipleFilesFolderExample/de-DE/two.yaml │ test.drive.four │
348
+ └───────────────────────────────────────────────────────────────┴───────────────────────┘
349
+
350
+ Found invalid keys!
351
+ ┌─────────────────────────────────────────────────────────────────┬─────────────────────┐
352
+ │ file │ key │
353
+ ├─────────────────────────────────────────────────────────────────┼─────────────────────┤
354
+ │ translations/yaml/multipleFilesFolderExample/de-DE/one.yaml │ message.select │
355
+ │ translations/yaml/multipleFilesFolderExample/de-DE/three.yaml │ multipleVariables │
356
+ └─────────────────────────────────────────────────────────────────┴─────────────────────┘
357
+
358
+ `);
359
+ done();
360
+ });
361
+ });
362
+ it("should return the missing/invalid keys for folder containing multiple locale folders", (done) => {
363
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/yaml/multipleFoldersExample -s en-US", (_error, stdout, _stderr) => {
364
+ const result = stdout.split("Done")[0];
365
+ expect(result).toEqual(`i18n translations checker
366
+ Source: en-US
367
+
368
+ Found missing keys!
369
+ ┌────────────────────────────────────────────────────────────────────────────┬───────────────────────┐
370
+ │ file │ key │
371
+ ├────────────────────────────────────────────────────────────────────────────┼───────────────────────┤
372
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/one.yaml │ message.text-format │
373
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/two.yaml │ test.drive.four │
374
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/one.yaml │ message.plural │
375
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/two.yaml │ test.drive.two │
376
+ └────────────────────────────────────────────────────────────────────────────┴───────────────────────┘
377
+
378
+ Found invalid keys!
379
+ ┌──────────────────────────────────────────────────────────────────────────────┬───────────────────────┐
380
+ │ file │ key │
381
+ ├──────────────────────────────────────────────────────────────────────────────┼───────────────────────┤
382
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/one.yaml │ message.select │
383
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/three.yaml │ multipleVariables │
384
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/one.yaml │ message.text-format │
385
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/three.yaml │ numberFormat │
386
+ └──────────────────────────────────────────────────────────────────────────────┴───────────────────────┘
387
+
388
+ `);
389
+ done();
390
+ });
391
+ });
392
+ it("should return the missing/invalid keys for multiple locale folders", (done) => {
393
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/yaml/multipleFoldersExample/spaceOne translations/yaml/multipleFoldersExample/spaceTwo -s en-US", (_error, stdout, _stderr) => {
394
+ const result = stdout.split("Done")[0];
395
+ expect(result).toEqual(`i18n translations checker
396
+ Source: en-US
397
+
398
+ Found missing keys!
399
+ ┌────────────────────────────────────────────────────────────────────────────┬───────────────────────┐
400
+ │ file │ key │
401
+ ├────────────────────────────────────────────────────────────────────────────┼───────────────────────┤
402
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/one.yaml │ message.text-format │
403
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/two.yaml │ test.drive.four │
404
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/one.yaml │ message.plural │
405
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/two.yaml │ test.drive.two │
406
+ └────────────────────────────────────────────────────────────────────────────┴───────────────────────┘
407
+
408
+ Found invalid keys!
409
+ ┌──────────────────────────────────────────────────────────────────────────────┬───────────────────────┐
410
+ │ file │ key │
411
+ ├──────────────────────────────────────────────────────────────────────────────┼───────────────────────┤
412
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/one.yaml │ message.select │
413
+ │ translations/yaml/multipleFoldersExample/spaceOne/locales/de-DE/three.yaml │ multipleVariables │
414
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/one.yaml │ message.text-format │
415
+ │ translations/yaml/multipleFoldersExample/spaceTwo/locales/de-DE/three.yaml │ numberFormat │
416
+ └──────────────────────────────────────────────────────────────────────────────┴───────────────────────┘
417
+
418
+ `);
419
+ done();
420
+ });
421
+ });
422
+ it("should return the missing/invalid keys for all files in the provided locale folders", (done) => {
423
+ (0, child_process_1.exec)("node dist/bin/index.js --source en-US --locales translations/yaml/flattenExamples translations/yaml/messageExamples", (_error, stdout, _stderr) => {
424
+ const result = stdout.split("Done")[0];
425
+ expect(result).toEqual(`i18n translations checker
426
+ Source: en-US
427
+
428
+ Found missing keys!
429
+ ┌────────────────────────────────────────────────┬────────────────────────────────┐
430
+ │ file │ key │
431
+ ├────────────────────────────────────────────────┼────────────────────────────────┤
432
+ │ translations/yaml/flattenExamples/de-de.yaml │ other.nested.three │
433
+ │ translations/yaml/flattenExamples/de-de.yaml │ other.nested.deep.more.final │
434
+ │ translations/yaml/messageExamples/de-de.yaml │ richText │
435
+ │ translations/yaml/messageExamples/de-de.yaml │ yo │
436
+ │ translations/yaml/messageExamples/de-de.yaml │ nesting1 │
437
+ │ translations/yaml/messageExamples/de-de.yaml │ nesting2 │
438
+ │ translations/yaml/messageExamples/de-de.yaml │ nesting3 │
439
+ │ translations/yaml/messageExamples/de-de.yaml │ key1 │
440
+ └────────────────────────────────────────────────┴────────────────────────────────┘
441
+
442
+ Found invalid keys!
443
+ ┌────────────────────────────────────────────────┬─────────────────────┐
444
+ │ file │ key │
445
+ ├────────────────────────────────────────────────┼─────────────────────┤
446
+ │ translations/yaml/messageExamples/de-de.yaml │ multipleVariables │
447
+ └────────────────────────────────────────────────┴─────────────────────┘
448
+
449
+ `);
450
+ done();
451
+ });
452
+ });
453
+ it("should return the missing/invalid keys for all files with source matching folder and source matching file", (done) => {
454
+ (0, child_process_1.exec)("node dist/bin/index.js -l translations/yaml/multipleFilesFolderExample translations/yaml/flattenExamples -s en-US", (_error, stdout, _stderr) => {
455
+ const result = stdout.split("Done")[0];
456
+ expect(result).toEqual(`i18n translations checker
457
+ Source: en-US
458
+
459
+ Found missing keys!
460
+ ┌───────────────────────────────────────────────────────────────┬────────────────────────────────┐
461
+ │ file │ key │
462
+ ├───────────────────────────────────────────────────────────────┼────────────────────────────────┤
463
+ │ translations/yaml/flattenExamples/de-de.yaml │ other.nested.three │
464
+ │ translations/yaml/flattenExamples/de-de.yaml │ other.nested.deep.more.final │
465
+ │ translations/yaml/multipleFilesFolderExample/de-DE/one.yaml │ message.text-format │
466
+ │ translations/yaml/multipleFilesFolderExample/de-DE/two.yaml │ test.drive.four │
467
+ └───────────────────────────────────────────────────────────────┴────────────────────────────────┘
468
+
469
+ Found invalid keys!
470
+ ┌─────────────────────────────────────────────────────────────────┬─────────────────────┐
471
+ │ file │ key │
472
+ ├─────────────────────────────────────────────────────────────────┼─────────────────────┤
473
+ │ translations/yaml/multipleFilesFolderExample/de-DE/one.yaml │ message.select │
474
+ │ translations/yaml/multipleFilesFolderExample/de-DE/three.yaml │ multipleVariables │
475
+ └─────────────────────────────────────────────────────────────────┴─────────────────────┘
476
+
477
+ `);
478
+ done();
479
+ });
264
480
  });
265
481
  });
266
482
  });
package/dist/index.js CHANGED
@@ -8,6 +8,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.checkUnusedKeys = exports.checkTranslations = exports.checkMissingTranslations = exports.checkInvalidTranslations = void 0;
13
16
  const findMissingKeys_1 = require("./utils/findMissingKeys");
@@ -15,6 +18,8 @@ const findInvalidTranslations_1 = require("./utils/findInvalidTranslations");
15
18
  const findInvalidi18nTranslations_1 = require("./utils/findInvalidi18nTranslations");
16
19
  const glob_1 = require("glob");
17
20
  const cli_lib_1 = require("@formatjs/cli-lib");
21
+ const fs_1 = __importDefault(require("fs"));
22
+ const vinyl_1 = __importDefault(require("vinyl"));
18
23
  const checkInvalidTranslations = (source, targets, options = { format: "icu" }) => {
19
24
  return options.format === "i18next"
20
25
  ? (0, findInvalidi18nTranslations_1.findInvalid18nTranslations)(source, targets)
@@ -58,7 +63,7 @@ const checkUnusedKeys = (source_1, codebaseSrc_1, ...args_1) => __awaiter(void 0
58
63
  }
59
64
  return options.format === "react-intl"
60
65
  ? findUnusedReactIntlTranslations(source, codebaseSrc)
61
- : undefined;
66
+ : findUnusedi18NextTranslations(source, codebaseSrc);
62
67
  });
63
68
  exports.checkUnusedKeys = checkUnusedKeys;
64
69
  const findUnusedReactIntlTranslations = (source, codebaseSrc) => __awaiter(void 0, void 0, void 0, function* () {
@@ -81,3 +86,55 @@ const findUnusedReactIntlTranslations = (source, codebaseSrc) => __awaiter(void
81
86
  });
82
87
  return unusedKeys;
83
88
  });
89
+ const findUnusedi18NextTranslations = (source, codebaseSrc) => __awaiter(void 0, void 0, void 0, function* () {
90
+ let unusedKeys = {};
91
+ // find any unused keys in a react-i18next code base
92
+ const unusedKeysFiles = (0, glob_1.globSync)(`${codebaseSrc}/**/*.tsx`, {
93
+ ignore: ["node_modules/**"],
94
+ });
95
+ let extractedResult = [];
96
+ // @ts-ignore
97
+ const { transform } = yield import("i18next-parser");
98
+ unusedKeysFiles.forEach((file) => {
99
+ const rawContent = fs_1.default.readFileSync(file);
100
+ const i18nextParser = new transform();
101
+ i18nextParser.once("data", (file) => {
102
+ extractedResult = extractedResult.concat(Object.keys(flatten(JSON.parse(file.contents))));
103
+ });
104
+ i18nextParser.end(new vinyl_1.default({
105
+ contents: rawContent,
106
+ path: file,
107
+ }));
108
+ });
109
+ const extractedResultSet = new Set(extractedResult);
110
+ source.forEach(({ name, content }) => {
111
+ const keysInSource = Object.keys(content);
112
+ const found = [];
113
+ for (const keyInSource of keysInSource) {
114
+ if (!extractedResultSet.has(keyInSource)) {
115
+ found.push(keyInSource);
116
+ }
117
+ }
118
+ Object.assign(unusedKeys, { [name]: found });
119
+ });
120
+ return unusedKeys;
121
+ });
122
+ const isRecord = (data) => {
123
+ return (typeof data === "object" &&
124
+ !Array.isArray(data) &&
125
+ data !== null &&
126
+ data !== undefined);
127
+ };
128
+ function flatten(object, prefix = null, result = {}) {
129
+ for (let key in object) {
130
+ let propName = prefix ? `${prefix}.${key}` : key;
131
+ const data = object[key];
132
+ if (isRecord(data)) {
133
+ flatten(data, propName, result);
134
+ }
135
+ else {
136
+ result[propName] = data;
137
+ }
138
+ }
139
+ return result;
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingual/i18n-check",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "i18n translation messages check",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -19,18 +19,23 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "@formatjs/cli-lib": "^6.4.2",
22
- "@formatjs/icu-messageformat-parser": "^2.7.8",
22
+ "@formatjs/icu-messageformat-parser": "^2.9.7",
23
23
  "chalk": "^4.1.2",
24
24
  "commander": "^12.1.0",
25
25
  "glob": "^11.0.0",
26
- "typescript": "^5.6.2"
26
+ "i18next-parser": "^9.0.2",
27
+ "js-yaml": "^4.1.0",
28
+ "vinyl": "^3.0.0"
27
29
  },
28
30
  "devDependencies": {
29
- "@types/jest": "^29.5.13",
30
- "@types/node": "^22.5.5",
31
+ "@types/jest": "^29.5.14",
32
+ "@types/js-yaml": "^4.0.9",
33
+ "@types/node": "^22.10.1",
34
+ "@types/vinyl": "^2.0.12",
31
35
  "braces": "^3.0.3",
32
36
  "jest": "^29.7.0",
33
- "ts-jest": "^29.2.5"
37
+ "ts-jest": "^29.2.5",
38
+ "typescript": "^5.7.3"
34
39
  },
35
40
  "repository": {
36
41
  "type": "git",