@lingual/i18n-check 0.8.2 → 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.
@@ -1,4 +1,4 @@
1
- import { CheckResult, InvalidTranslationsResult } from "./types";
1
+ import { CheckResult, InvalidTranslationsResult } from './types';
2
2
  export type StandardReporter = {
3
3
  file: string;
4
4
  key: string;
@@ -6,16 +6,16 @@ exports.formatTable = formatTable;
6
6
  exports.formatCheckResultTable = formatCheckResultTable;
7
7
  exports.formatInvalidTranslationsResultTable = formatInvalidTranslationsResultTable;
8
8
  exports.CheckOptions = [
9
- "invalidKeys",
10
- "missingKeys",
11
- "unused",
12
- "undefined",
9
+ 'invalidKeys',
10
+ 'missingKeys',
11
+ 'unused',
12
+ 'undefined',
13
13
  ];
14
14
  exports.contextMapping = {
15
- invalidKeys: "invalid",
16
- missingKeys: "missing",
17
- unused: "unused",
18
- undefined: "undefined",
15
+ invalidKeys: 'invalid',
16
+ missingKeys: 'missing',
17
+ unused: 'unused',
18
+ undefined: 'undefined',
19
19
  };
20
20
  function formatSummaryTable(result) {
21
21
  return formatTable(getSummaryRows(result));
@@ -25,9 +25,9 @@ const getSummaryRows = (checkResult) => {
25
25
  for (const [file, keys] of Object.entries(checkResult)) {
26
26
  rows.push([truncate(file), String(keys.length)]);
27
27
  }
28
- return [[["file", "total"]], rows];
28
+ return [[['file', 'total']], rows];
29
29
  };
30
- function formatTable(rowGroups, lineSep = "\n") {
30
+ function formatTable(rowGroups, lineSep = '\n') {
31
31
  // +2 for whitespace padding left and right
32
32
  const padding = 2;
33
33
  const colWidths = [];
@@ -39,40 +39,40 @@ function formatTable(rowGroups, lineSep = "\n") {
39
39
  }
40
40
  }
41
41
  const lines = [];
42
- lines.push(formatSeparatorRow(colWidths, "┌┬┐"));
42
+ lines.push(formatSeparatorRow(colWidths, '┌┬┐'));
43
43
  for (const rows of rowGroups) {
44
44
  for (const row of rows) {
45
45
  lines.push(formatRow(row, colWidths));
46
46
  }
47
- lines.push(formatSeparatorRow(colWidths, "├┼┤"));
47
+ lines.push(formatSeparatorRow(colWidths, '├┼┤'));
48
48
  }
49
- lines[lines.length - 1] = formatSeparatorRow(colWidths, "└┴┘");
49
+ lines[lines.length - 1] = formatSeparatorRow(colWidths, '└┴┘');
50
50
  return lines.join(lineSep);
51
51
  }
52
52
  function formatSeparatorRow(widths, [left, middle, right]) {
53
- return (left + widths.map((width) => "".padEnd(width, "")).join(middle) + right);
53
+ return (left + widths.map((width) => ''.padEnd(width, '')).join(middle) + right);
54
54
  }
55
55
  function formatRow(values, widths) {
56
56
  return (`│` +
57
57
  values
58
- .map((val, index) => ` ${val} `.padEnd(widths[index], " "))
59
- .join("") +
58
+ .map((val, index) => ` ${val} `.padEnd(widths[index], ' '))
59
+ .join('') +
60
60
  `│`);
61
61
  }
62
62
  const truncate = (chars, len = 80) => chars.length > 80 ? `${chars.substring(0, len)}...` : chars;
63
63
  function formatCheckResultTable(result) {
64
64
  return formatTable([
65
- [["file", "key"]],
65
+ [['file', 'key']],
66
66
  Object.entries(result).flatMap(([file, keys]) => keys.map((key) => [truncate(file), truncate(key)])),
67
67
  ]);
68
68
  }
69
69
  function formatInvalidTranslationsResultTable(result) {
70
70
  return formatTable([
71
- [["info", "result"]],
71
+ [['info', 'result']],
72
72
  ...Object.entries(result).flatMap(([file, errors]) => errors.map(({ key, msg }) => [
73
- ["file", truncate(file)],
74
- ["key", truncate(key)],
75
- ["msg", truncate(msg, 120)],
73
+ ['file', truncate(file)],
74
+ ['key', truncate(key)],
75
+ ['msg', truncate(msg, 120)],
76
76
  ])),
77
77
  ]);
78
78
  }
@@ -1,28 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const errorReporters_1 = require("./errorReporters");
4
- describe("formatTable", () => {
5
- test("single col and row", () => {
6
- expect((0, errorReporters_1.formatTable)([[["lorem ipsum"]]])).toEqual(`
4
+ describe('formatTable', () => {
5
+ test('single col and row', () => {
6
+ expect((0, errorReporters_1.formatTable)([[['lorem ipsum']]])).toEqual(`
7
7
  ┌─────────────┐
8
8
  │ lorem ipsum │
9
9
  └─────────────┘
10
10
  `.trim());
11
11
  });
12
- test("single col and two rows", () => {
13
- expect((0, errorReporters_1.formatTable)([[["lorem ipsum"], ["foo bar"]]])).toEqual(`
12
+ test('single col and two rows', () => {
13
+ expect((0, errorReporters_1.formatTable)([[['lorem ipsum'], ['foo bar']]])).toEqual(`
14
14
  ┌─────────────┐
15
15
  │ lorem ipsum │
16
16
  │ foo bar │
17
17
  └─────────────┘
18
18
  `.trim());
19
19
  });
20
- test("with two columns and two row groups", () => {
20
+ test('with two columns and two row groups', () => {
21
21
  expect((0, errorReporters_1.formatTable)([
22
- [["col1", "col2"]],
22
+ [['col1', 'col2']],
23
23
  [
24
- ["lorem ipsum dolor", "foobar"],
25
- ["baz", "more text"],
24
+ ['lorem ipsum dolor', 'foobar'],
25
+ ['baz', 'more text'],
26
26
  ],
27
27
  ])).toEqual(`
28
28
  ┌───────────────────┬───────────┐
@@ -33,14 +33,14 @@ describe("formatTable", () => {
33
33
  └───────────────────┴───────────┘
34
34
  `.trim());
35
35
  });
36
- test("with two columns and three row groups", () => {
36
+ test('with two columns and three row groups', () => {
37
37
  expect((0, errorReporters_1.formatTable)([
38
- [["one", "two"]],
38
+ [['one', 'two']],
39
39
  [
40
- ["lorem ipsum dolor", "foobar"],
41
- ["baz", "more text"],
40
+ ['lorem ipsum dolor', 'foobar'],
41
+ ['baz', 'more text'],
42
42
  ],
43
- [["hello world", "here is more text for testing"]],
43
+ [['hello world', 'here is more text for testing']],
44
44
  ])).toEqual(`
45
45
  ┌───────────────────┬───────────────────────────────┐
46
46
  │ one │ two │
@@ -53,10 +53,10 @@ describe("formatTable", () => {
53
53
  `.trim());
54
54
  });
55
55
  });
56
- describe("formatCheckResultTable", () => {
57
- test("with one file and two keys", () => {
56
+ describe('formatCheckResultTable', () => {
57
+ test('with one file and two keys', () => {
58
58
  expect((0, errorReporters_1.formatCheckResultTable)({
59
- "some/file.json": ["key.one", "key.two"],
59
+ 'some/file.json': ['key.one', 'key.two'],
60
60
  })).toEqual(`
61
61
  ┌────────────────┬─────────┐
62
62
  │ file │ key │
@@ -66,10 +66,10 @@ describe("formatCheckResultTable", () => {
66
66
  └────────────────┴─────────┘
67
67
  `.trim());
68
68
  });
69
- test("with two files and three keys", () => {
69
+ test('with two files and three keys', () => {
70
70
  expect((0, errorReporters_1.formatCheckResultTable)({
71
- "some/de.json": ["key.one", "key.two"],
72
- "some/en.json": ["key.three"],
71
+ 'some/de.json': ['key.one', 'key.two'],
72
+ 'some/en.json': ['key.three'],
73
73
  })).toEqual(`
74
74
  ┌──────────────┬───────────┐
75
75
  │ file │ key │
@@ -81,10 +81,10 @@ describe("formatCheckResultTable", () => {
81
81
  `.trim());
82
82
  });
83
83
  });
84
- describe("formatInvalidTranslationsResultTable", () => {
85
- test("with one file and one key", () => {
84
+ describe('formatInvalidTranslationsResultTable', () => {
85
+ test('with one file and one key', () => {
86
86
  expect((0, errorReporters_1.formatInvalidTranslationsResultTable)({
87
- "some/en.json": [{ key: "key.one", msg: "key one error msg" }],
87
+ 'some/en.json': [{ key: 'key.one', msg: 'key one error msg' }],
88
88
  })).toEqual(`
89
89
  ┌──────┬───────────────────┐
90
90
  │ info │ result │
@@ -95,13 +95,13 @@ describe("formatInvalidTranslationsResultTable", () => {
95
95
  └──────┴───────────────────┘
96
96
  `.trim());
97
97
  });
98
- test("with two files and three keys", () => {
98
+ test('with two files and three keys', () => {
99
99
  expect((0, errorReporters_1.formatInvalidTranslationsResultTable)({
100
- "some/en-US.json": [
101
- { key: "key.one", msg: "key one error msg" },
102
- { key: "key.two", msg: "another msg" },
100
+ 'some/en-US.json': [
101
+ { key: 'key.one', msg: 'key one error msg' },
102
+ { key: 'key.two', msg: 'another msg' },
103
103
  ],
104
- "some/de.json": [{ key: "key.three", msg: "key three msg" }],
104
+ 'some/de.json': [{ key: 'key.three', msg: 'key three msg' }],
105
105
  })).toEqual(`
106
106
  ┌──────┬───────────────────┐
107
107
  │ info │ result │
@@ -121,10 +121,10 @@ describe("formatInvalidTranslationsResultTable", () => {
121
121
  `.trim());
122
122
  });
123
123
  });
124
- describe("formatSummaryTable", () => {
125
- test("with CheckResult with single file and key", () => {
124
+ describe('formatSummaryTable', () => {
125
+ test('with CheckResult with single file and key', () => {
126
126
  expect((0, errorReporters_1.formatSummaryTable)({
127
- "some/file.json": ["key.one"],
127
+ 'some/file.json': ['key.one'],
128
128
  })).toEqual(`
129
129
  ┌────────────────┬───────┐
130
130
  │ file │ total │
@@ -133,10 +133,10 @@ describe("formatSummaryTable", () => {
133
133
  └────────────────┴───────┘
134
134
  `.trim());
135
135
  });
136
- test("with CheckResult with two files and three keys", () => {
136
+ test('with CheckResult with two files and three keys', () => {
137
137
  expect((0, errorReporters_1.formatSummaryTable)({
138
- "some/de.json": ["key.one", "key.two"],
139
- "some/en.json": ["key.three"],
138
+ 'some/de.json': ['key.one', 'key.two'],
139
+ 'some/en.json': ['key.three'],
140
140
  })).toEqual(`
141
141
  ┌──────────────┬───────┐
142
142
  │ file │ total │
@@ -146,13 +146,13 @@ describe("formatSummaryTable", () => {
146
146
  └──────────────┴───────┘
147
147
  `.trim());
148
148
  });
149
- test("with InvalidTranslationsResult with two files and three keys", () => {
149
+ test('with InvalidTranslationsResult with two files and three keys', () => {
150
150
  expect((0, errorReporters_1.formatSummaryTable)({
151
- "some/en-US.json": [
152
- { key: "key.one", msg: "key one error msg" },
153
- { key: "key.two", msg: "another msg" },
151
+ 'some/en-US.json': [
152
+ { key: 'key.one', msg: 'key one error msg' },
153
+ { key: 'key.two', msg: 'another msg' },
154
154
  ],
155
- "some/de.json": [{ key: "key.three", msg: "key three msg" }],
155
+ 'some/de.json': [{ key: 'key.three', msg: 'key three msg' }],
156
156
  })).toEqual(`
157
157
  ┌─────────────────┬───────┐
158
158
  │ file │ total │
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { CheckResult, InvalidTranslationsResult, Translation, TranslationFile } from "./types";
2
- import { Context } from "./errorReporters";
1
+ import { CheckResult, InvalidTranslationsResult, Translation, TranslationFile } from './types';
2
+ import { Context } from './errorReporters';
3
3
  export type Options = {
4
- format?: "icu" | "i18next" | "react-intl" | "next-intl";
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
@@ -11,9 +11,9 @@ const cli_lib_1 = require("@formatjs/cli-lib");
11
11
  const nextIntlSrcParser_1 = require("./utils/nextIntlSrcParser");
12
12
  const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
- const ParseFormats = ["react-intl", "i18next", "next-intl"];
15
- const checkInvalidTranslations = (source, targets, options = { format: "icu" }) => {
16
- return options.format === "i18next"
14
+ const ParseFormats = ['react-intl', 'i18next', 'next-intl'];
15
+ const checkInvalidTranslations = (source, targets, options = { format: 'icu' }) => {
16
+ return options.format === 'i18next'
17
17
  ? (0, findInvalidi18nTranslations_1.findInvalid18nTranslations)(source, targets)
18
18
  : (0, findInvalidTranslations_1.findInvalidTranslations)(source, targets);
19
19
  };
@@ -22,51 +22,52 @@ const checkMissingTranslations = (source, targets) => {
22
22
  return (0, findMissingKeys_1.findMissingKeys)(source, targets);
23
23
  };
24
24
  exports.checkMissingTranslations = checkMissingTranslations;
25
- const checkTranslations = (source, targets, options = { format: "icu", checks: ["invalidKeys", "missingKeys"] }) => {
26
- const { checks = ["invalidKeys", "missingKeys"] } = options;
27
- let missingKeys = {};
28
- let invalidKeys = {};
29
- const hasMissingKeys = checks.includes("missingKeys");
30
- const hasInvalidKeys = checks.includes("invalidKeys");
25
+ const checkTranslations = (source, targets, options = { format: 'icu', checks: ['invalidKeys', 'missingKeys'] }) => {
26
+ const { checks = ['invalidKeys', 'missingKeys'] } = options;
27
+ const missingKeys = {};
28
+ const 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;
48
49
  function merge(left, right) {
49
- for (let [k, v] of Object.entries(right)) {
50
+ for (const [k, v] of Object.entries(right)) {
50
51
  left[k] = (left?.[k] ?? []).concat(v);
51
52
  }
52
53
  }
53
54
  const checkUnusedKeys = async (translationFiles, filesToParse, options = {
54
- format: "react-intl",
55
+ format: 'react-intl',
55
56
  checks: [],
56
57
  }, componentFunctions = []) => {
57
58
  if (!options.format || !ParseFormats.includes(options.format)) {
58
59
  return undefined;
59
60
  }
60
- if (!options.checks || !options.checks.includes("unused")) {
61
+ if (!options.checks || !options.checks.includes('unused')) {
61
62
  return undefined;
62
63
  }
63
- if (options.format === "react-intl") {
64
+ if (options.format === 'react-intl') {
64
65
  return findUnusedReactIntlTranslations(translationFiles, filesToParse);
65
66
  }
66
- else if (options.format === "i18next") {
67
+ else if (options.format === 'i18next') {
67
68
  return findUnusedI18NextTranslations(translationFiles, filesToParse, componentFunctions);
68
69
  }
69
- else if (options.format === "next-intl") {
70
+ else if (options.format === 'next-intl') {
70
71
  return findUnusedNextIntlTranslations(translationFiles, filesToParse);
71
72
  }
72
73
  };
@@ -90,7 +91,7 @@ const findUnusedReactIntlTranslations = async (translationFiles, filesToParse) =
90
91
  const findUnusedI18NextTranslations = async (source, filesToParse, componentFunctions = []) => {
91
92
  const unusedKeys = {};
92
93
  const { extractedResult, skippableKeys } = await getI18NextKeysInCode(filesToParse, componentFunctions);
93
- const extractedResultSet = new Set(extractedResult.map(({ key }) => key));
94
+ const extractedResultSet = new Set(extractedResult.map(({ key, namespace }) => namespace ? `${namespace}.${key}` : key));
94
95
  source.forEach(({ name, content }) => {
95
96
  const keysInSource = Object.keys(content);
96
97
  const found = [];
@@ -101,7 +102,10 @@ const findUnusedI18NextTranslations = async (source, filesToParse, componentFunc
101
102
  if (isSkippable !== undefined) {
102
103
  continue;
103
104
  }
104
- if (!extractedResultSet.has(keyInSource)) {
105
+ // find the file name
106
+ const [fileName] = (name.split(path_1.default.sep).pop() ?? '').split('.');
107
+ if (!extractedResultSet.has(`${fileName}.${keyInSource}`) &&
108
+ !extractedResultSet.has(keyInSource)) {
105
109
  found.push(keyInSource);
106
110
  }
107
111
  }
@@ -131,8 +135,8 @@ const findUnusedNextIntlTranslations = async (translationFiles, filesToParse) =>
131
135
  // Check if key is part of a dynamic namespace
132
136
  // Skip the key if it is part of the dynamic namespace
133
137
  const isDynamicNamespace = dynamicNamespaces.find((dynamicNamespace) => {
134
- const keyInSourceNamespaces = keyInSource.split(".");
135
- return dynamicNamespace.split(".").every((namePart, index) => {
138
+ const keyInSourceNamespaces = keyInSource.split('.');
139
+ return dynamicNamespace.split('.').every((namePart, index) => {
136
140
  return namePart === keyInSourceNamespaces[index];
137
141
  });
138
142
  });
@@ -148,22 +152,22 @@ const findUnusedNextIntlTranslations = async (translationFiles, filesToParse) =>
148
152
  return unusedKeys;
149
153
  };
150
154
  const checkUndefinedKeys = async (source, filesToParse, options = {
151
- format: "react-intl",
155
+ format: 'react-intl',
152
156
  checks: [],
153
157
  }, componentFunctions = []) => {
154
158
  if (!options.format || !ParseFormats.includes(options.format)) {
155
159
  return undefined;
156
160
  }
157
- if (!options.checks || !options.checks.includes("undefined")) {
161
+ if (!options.checks || !options.checks.includes('undefined')) {
158
162
  return undefined;
159
163
  }
160
- if (options.format === "react-intl") {
164
+ if (options.format === 'react-intl') {
161
165
  return findUndefinedReactIntlKeys(source, filesToParse);
162
166
  }
163
- else if (options.format === "i18next") {
167
+ else if (options.format === 'i18next') {
164
168
  return findUndefinedI18NextKeys(source, filesToParse, componentFunctions);
165
169
  }
166
- else if (options.format === "next-intl") {
170
+ else if (options.format === 'next-intl') {
167
171
  return findUndefinedNextIntlKeys(source, filesToParse);
168
172
  }
169
173
  };
@@ -175,11 +179,11 @@ const findUndefinedReactIntlKeys = async (translationFiles, filesToParse) => {
175
179
  const extractedResult = await (0, cli_lib_1.extract)(filesToParse, {
176
180
  extractSourceLocation: true,
177
181
  });
178
- let undefinedKeys = {};
182
+ const undefinedKeys = {};
179
183
  Object.entries(JSON.parse(extractedResult)).forEach(([key, meta]) => {
180
184
  if (!sourceKeys.has(key)) {
181
185
  const data = meta;
182
- if (!("file" in data) || typeof data.file !== "string") {
186
+ if (!('file' in data) || typeof data.file !== 'string') {
183
187
  return;
184
188
  }
185
189
  const file = path_1.default.normalize(data.file);
@@ -196,7 +200,7 @@ const findUndefinedI18NextKeys = async (source, filesToParse, componentFunctions
196
200
  const sourceKeys = new Set(source.flatMap(({ content }) => {
197
201
  return Object.keys(content);
198
202
  }));
199
- let undefinedKeys = {};
203
+ const undefinedKeys = {};
200
204
  extractedResult.forEach(({ file, key }) => {
201
205
  const isSkippable = skippableKeys.find((skippableKey) => {
202
206
  return key.includes(skippableKey);
@@ -215,10 +219,9 @@ const findUndefinedNextIntlKeys = async (translationFiles, filesToParse) => {
215
219
  return Object.keys(content);
216
220
  }));
217
221
  const extractedResult = (0, nextIntlSrcParser_1.extract)(filesToParse);
218
- let undefinedKeys = {};
222
+ const undefinedKeys = {};
219
223
  extractedResult.forEach(({ key, meta }) => {
220
224
  if (!meta.dynamic && !sourceKeys.has(key)) {
221
- // @ts-ignore
222
225
  const file = meta.file;
223
226
  if (!undefinedKeys[file]) {
224
227
  undefinedKeys[file] = [];
@@ -229,26 +232,27 @@ const findUndefinedNextIntlKeys = async (translationFiles, filesToParse) => {
229
232
  return undefinedKeys;
230
233
  };
231
234
  const isRecord = (data) => {
232
- return (typeof data === "object" &&
235
+ return (typeof data === 'object' &&
233
236
  !Array.isArray(data) &&
234
237
  data !== null &&
235
238
  data !== undefined);
236
239
  };
237
240
  const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
241
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
238
242
  // @ts-ignore
239
- const { transform } = await import("i18next-parser");
243
+ const { transform } = await import('i18next-parser');
240
244
  const i18nextParser = new transform({
241
245
  lexers: {
242
246
  jsx: [
243
247
  {
244
- lexer: "JsxLexer",
245
- componentFunctions: componentFunctions.concat(["Trans"]),
248
+ lexer: 'JsxLexer',
249
+ componentFunctions: componentFunctions.concat(['Trans']),
246
250
  },
247
251
  ],
248
252
  tsx: [
249
253
  {
250
- lexer: "JsxLexer",
251
- componentFunctions: componentFunctions.concat(["Trans"]),
254
+ lexer: 'JsxLexer',
255
+ componentFunctions: componentFunctions.concat(['Trans']),
252
256
  },
253
257
  ],
254
258
  },
@@ -256,32 +260,63 @@ const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
256
260
  // Skip any parsed keys that have the `returnObjects` property set to true
257
261
  // As these are used dynamically, they will be skipped to prevent
258
262
  // these keys from being marked as unused.
259
- let extractedResult = [];
263
+ const extractedResult = [];
260
264
  const skippableKeys = [];
261
265
  filesToParse.forEach((file) => {
262
- const rawContent = fs_1.default.readFileSync(file, "utf-8");
266
+ const rawContent = fs_1.default.readFileSync(file, 'utf-8');
263
267
  const entries = i18nextParser.parser.parse(rawContent, file);
264
268
  // Intermediate solution to retrieve all keys from the parser.
265
269
  // This will be built out to also include the namespace and check
266
270
  // the key against the namespace corresponding file.
267
271
  // The current implementation considers the key as used no matter the namespace.
268
272
  for (const entry of entries) {
273
+ // check for namespace, i.e. `namespace:some.key`
274
+ const [namespace, ...keyParts] = entry.key.split(':');
275
+ // If there is a namespace make sure to assign the namespace
276
+ // and update the key name
277
+ // Ensure that the assumed key is not the default value
278
+ if (keyParts.length > 0 && entry.key !== entry.defaultValue) {
279
+ entry.namespace = namespace;
280
+ // rebuild the key without the namespace
281
+ entry.key = keyParts.join(':');
282
+ }
269
283
  if (entry.returnObjects) {
270
284
  skippableKeys.push(entry.key);
271
285
  }
272
286
  else {
273
- extractedResult.push({ file, key: entry.key });
287
+ extractedResult.push({
288
+ file,
289
+ key: entry.key,
290
+ namespace: entry.namespace,
291
+ });
274
292
  }
275
293
  }
276
294
  });
277
295
  return { extractedResult, skippableKeys };
278
296
  };
279
- function flatten(object, prefix = null, result = {}) {
280
- for (let key in object) {
281
- let propName = prefix ? `${prefix}.${key}` : key;
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
+ };
314
+ function _flatten(object, prefix = null, result = {}) {
315
+ for (const key in object) {
316
+ const propName = prefix ? `${prefix}.${key}` : key;
282
317
  const data = object[key];
283
318
  if (isRecord(data)) {
284
- flatten(data, propName, result);
319
+ _flatten(data, propName, result);
285
320
  }
286
321
  else {
287
322
  result[propName] = data;
@@ -1,5 +1,5 @@
1
- import { MessageFormatElement } from "@formatjs/icu-messageformat-parser";
2
- import { InvalidTranslationEntry, InvalidTranslationsResult, Translation } from "../types";
1
+ import { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
2
+ import { InvalidTranslationEntry, InvalidTranslationsResult, Translation } from '../types';
3
3
  export declare const findInvalidTranslations: (source: Translation, files: Record<string, Translation>) => InvalidTranslationsResult;
4
4
  export declare const compareTranslationFiles: (a: Translation, b: Translation) => InvalidTranslationEntry[];
5
5
  export declare const hasDiff: (a: MessageFormatElement[], b: MessageFormatElement[]) => boolean;