@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 +13 -7
- package/dist/bin/index.js +18 -9
- package/dist/bin/index.test.js +275 -59
- package/dist/index.js +58 -1
- package/package.json +11 -6
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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({
|
package/dist/bin/index.test.js
CHANGED
|
@@ -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
|
-
|
|
6
|
-
(
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
23
|
+
done();
|
|
24
|
+
});
|
|
23
25
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
47
|
+
done();
|
|
48
|
+
});
|
|
47
49
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
73
|
+
done();
|
|
74
|
+
});
|
|
73
75
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
103
|
+
done();
|
|
104
|
+
});
|
|
103
105
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
133
|
+
done();
|
|
134
|
+
});
|
|
133
135
|
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
164
|
+
done();
|
|
165
|
+
});
|
|
164
166
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
192
|
+
done();
|
|
193
|
+
});
|
|
192
194
|
});
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
221
|
+
done();
|
|
222
|
+
});
|
|
221
223
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
250
|
+
done();
|
|
251
|
+
});
|
|
250
252
|
});
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
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
|
-
:
|
|
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
|
+
"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
|
|
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
|
-
"
|
|
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.
|
|
30
|
-
"@types/
|
|
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",
|