@lingual/i18n-check 0.8.1 → 0.8.3

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/dist/index.js CHANGED
@@ -10,9 +10,10 @@ const findInvalidi18nTranslations_1 = require("./utils/findInvalidi18nTranslatio
10
10
  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
- const ParseFormats = ["react-intl", "i18next", "next-intl"];
14
- const checkInvalidTranslations = (source, targets, options = { format: "icu" }) => {
15
- return options.format === "i18next"
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'
16
17
  ? (0, findInvalidi18nTranslations_1.findInvalid18nTranslations)(source, targets)
17
18
  : (0, findInvalidTranslations_1.findInvalidTranslations)(source, targets);
18
19
  };
@@ -21,23 +22,21 @@ const checkMissingTranslations = (source, targets) => {
21
22
  return (0, findMissingKeys_1.findMissingKeys)(source, targets);
22
23
  };
23
24
  exports.checkMissingTranslations = checkMissingTranslations;
24
- const checkTranslations = (source, targets, options = { format: "icu", checks: ["invalidKeys", "missingKeys"] }) => {
25
- const { checks = ["invalidKeys", "missingKeys"] } = options;
26
- let missingKeys = {};
27
- let invalidKeys = {};
28
- const hasMissingKeys = checks.includes("missingKeys");
29
- 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 hasMissingKeys = checks.includes('missingKeys');
30
+ const hasInvalidKeys = checks.includes('invalidKeys');
30
31
  source.forEach(({ name, content }) => {
31
- const files = targets
32
+ const files = Object.fromEntries(targets
32
33
  .filter(({ reference }) => reference === name)
33
- .reduce((obj, { name: key, content }) => {
34
- return Object.assign(obj, { [key]: content });
35
- }, {});
34
+ .map(({ name, content }) => [name, content]));
36
35
  if (hasMissingKeys) {
37
- Object.assign(missingKeys, (0, exports.checkMissingTranslations)(content, files));
36
+ merge(missingKeys, (0, exports.checkMissingTranslations)(content, files));
38
37
  }
39
38
  if (hasInvalidKeys) {
40
- Object.assign(invalidKeys, (0, exports.checkInvalidTranslations)(content, files, options));
39
+ merge(invalidKeys, (0, exports.checkInvalidTranslations)(content, files, options));
41
40
  }
42
41
  });
43
42
  return {
@@ -46,29 +45,34 @@ const checkTranslations = (source, targets, options = { format: "icu", checks: [
46
45
  };
47
46
  };
48
47
  exports.checkTranslations = checkTranslations;
48
+ function merge(left, right) {
49
+ for (const [k, v] of Object.entries(right)) {
50
+ left[k] = (left?.[k] ?? []).concat(v);
51
+ }
52
+ }
49
53
  const checkUnusedKeys = async (translationFiles, filesToParse, options = {
50
- format: "react-intl",
54
+ format: 'react-intl',
51
55
  checks: [],
52
56
  }, componentFunctions = []) => {
53
57
  if (!options.format || !ParseFormats.includes(options.format)) {
54
58
  return undefined;
55
59
  }
56
- if (!options.checks || !options.checks.includes("unused")) {
60
+ if (!options.checks || !options.checks.includes('unused')) {
57
61
  return undefined;
58
62
  }
59
- if (options.format === "react-intl") {
63
+ if (options.format === 'react-intl') {
60
64
  return findUnusedReactIntlTranslations(translationFiles, filesToParse);
61
65
  }
62
- else if (options.format === "i18next") {
66
+ else if (options.format === 'i18next') {
63
67
  return findUnusedI18NextTranslations(translationFiles, filesToParse, componentFunctions);
64
68
  }
65
- else if (options.format === "next-intl") {
69
+ else if (options.format === 'next-intl') {
66
70
  return findUnusedNextIntlTranslations(translationFiles, filesToParse);
67
71
  }
68
72
  };
69
73
  exports.checkUnusedKeys = checkUnusedKeys;
70
74
  const findUnusedReactIntlTranslations = async (translationFiles, filesToParse) => {
71
- let unusedKeys = {};
75
+ const unusedKeys = {};
72
76
  const extracted = await (0, cli_lib_1.extract)(filesToParse, {});
73
77
  const extractedResultSet = new Set(Object.keys(JSON.parse(extracted)));
74
78
  translationFiles.forEach(({ name, content }) => {
@@ -79,14 +83,14 @@ const findUnusedReactIntlTranslations = async (translationFiles, filesToParse) =
79
83
  found.push(keyInSource);
80
84
  }
81
85
  }
82
- Object.assign(unusedKeys, { [name]: found });
86
+ unusedKeys[name] = found;
83
87
  });
84
88
  return unusedKeys;
85
89
  };
86
90
  const findUnusedI18NextTranslations = async (source, filesToParse, componentFunctions = []) => {
87
- let unusedKeys = {};
91
+ const unusedKeys = {};
88
92
  const { extractedResult, skippableKeys } = await getI18NextKeysInCode(filesToParse, componentFunctions);
89
- const extractedResultSet = new Set(extractedResult.map(({ key }) => key));
93
+ const extractedResultSet = new Set(extractedResult.map(({ key, namespace }) => namespace ? `${namespace}.${key}` : key));
90
94
  source.forEach(({ name, content }) => {
91
95
  const keysInSource = Object.keys(content);
92
96
  const found = [];
@@ -97,16 +101,19 @@ const findUnusedI18NextTranslations = async (source, filesToParse, componentFunc
97
101
  if (isSkippable !== undefined) {
98
102
  continue;
99
103
  }
100
- if (!extractedResultSet.has(keyInSource)) {
104
+ // find the file name
105
+ const [fileName] = (name.split(path_1.default.sep).pop() ?? "").split(".");
106
+ if (!extractedResultSet.has(`${fileName}.${keyInSource}`) &&
107
+ !extractedResultSet.has(keyInSource)) {
101
108
  found.push(keyInSource);
102
109
  }
103
110
  }
104
- Object.assign(unusedKeys, { [name]: found });
111
+ unusedKeys[name] = found;
105
112
  });
106
113
  return unusedKeys;
107
114
  };
108
115
  const findUnusedNextIntlTranslations = async (translationFiles, filesToParse) => {
109
- let unusedKeys = {};
116
+ const unusedKeys = {};
110
117
  const extracted = (0, nextIntlSrcParser_1.extract)(filesToParse);
111
118
  const dynamicNamespaces = extracted.flatMap((namespace) => {
112
119
  if (namespace.meta.dynamic) {
@@ -127,8 +134,8 @@ const findUnusedNextIntlTranslations = async (translationFiles, filesToParse) =>
127
134
  // Check if key is part of a dynamic namespace
128
135
  // Skip the key if it is part of the dynamic namespace
129
136
  const isDynamicNamespace = dynamicNamespaces.find((dynamicNamespace) => {
130
- const keyInSourceNamespaces = keyInSource.split(".");
131
- return dynamicNamespace.split(".").every((namePart, index) => {
137
+ const keyInSourceNamespaces = keyInSource.split('.');
138
+ return dynamicNamespace.split('.').every((namePart, index) => {
132
139
  return namePart === keyInSourceNamespaces[index];
133
140
  });
134
141
  });
@@ -139,27 +146,27 @@ const findUnusedNextIntlTranslations = async (translationFiles, filesToParse) =>
139
146
  found.push(keyInSource);
140
147
  }
141
148
  }
142
- Object.assign(unusedKeys, { [name]: found });
149
+ unusedKeys[name] = found;
143
150
  });
144
151
  return unusedKeys;
145
152
  };
146
153
  const checkUndefinedKeys = async (source, filesToParse, options = {
147
- format: "react-intl",
154
+ format: 'react-intl',
148
155
  checks: [],
149
156
  }, componentFunctions = []) => {
150
157
  if (!options.format || !ParseFormats.includes(options.format)) {
151
158
  return undefined;
152
159
  }
153
- if (!options.checks || !options.checks.includes("undefined")) {
160
+ if (!options.checks || !options.checks.includes('undefined')) {
154
161
  return undefined;
155
162
  }
156
- if (options.format === "react-intl") {
163
+ if (options.format === 'react-intl') {
157
164
  return findUndefinedReactIntlKeys(source, filesToParse);
158
165
  }
159
- else if (options.format === "i18next") {
166
+ else if (options.format === 'i18next') {
160
167
  return findUndefinedI18NextKeys(source, filesToParse, componentFunctions);
161
168
  }
162
- else if (options.format === "next-intl") {
169
+ else if (options.format === 'next-intl') {
163
170
  return findUndefinedNextIntlKeys(source, filesToParse);
164
171
  }
165
172
  };
@@ -171,11 +178,14 @@ const findUndefinedReactIntlKeys = async (translationFiles, filesToParse) => {
171
178
  const extractedResult = await (0, cli_lib_1.extract)(filesToParse, {
172
179
  extractSourceLocation: true,
173
180
  });
174
- let undefinedKeys = {};
181
+ const undefinedKeys = {};
175
182
  Object.entries(JSON.parse(extractedResult)).forEach(([key, meta]) => {
176
183
  if (!sourceKeys.has(key)) {
177
- // @ts-ignore
178
- const file = meta.file;
184
+ const data = meta;
185
+ if (!('file' in data) || typeof data.file !== 'string') {
186
+ return;
187
+ }
188
+ const file = path_1.default.normalize(data.file);
179
189
  if (!undefinedKeys[file]) {
180
190
  undefinedKeys[file] = [];
181
191
  }
@@ -189,7 +199,7 @@ const findUndefinedI18NextKeys = async (source, filesToParse, componentFunctions
189
199
  const sourceKeys = new Set(source.flatMap(({ content }) => {
190
200
  return Object.keys(content);
191
201
  }));
192
- let undefinedKeys = {};
202
+ const undefinedKeys = {};
193
203
  extractedResult.forEach(({ file, key }) => {
194
204
  const isSkippable = skippableKeys.find((skippableKey) => {
195
205
  return key.includes(skippableKey);
@@ -208,10 +218,9 @@ const findUndefinedNextIntlKeys = async (translationFiles, filesToParse) => {
208
218
  return Object.keys(content);
209
219
  }));
210
220
  const extractedResult = (0, nextIntlSrcParser_1.extract)(filesToParse);
211
- let undefinedKeys = {};
221
+ const undefinedKeys = {};
212
222
  extractedResult.forEach(({ key, meta }) => {
213
223
  if (!meta.dynamic && !sourceKeys.has(key)) {
214
- // @ts-ignore
215
224
  const file = meta.file;
216
225
  if (!undefinedKeys[file]) {
217
226
  undefinedKeys[file] = [];
@@ -222,26 +231,27 @@ const findUndefinedNextIntlKeys = async (translationFiles, filesToParse) => {
222
231
  return undefinedKeys;
223
232
  };
224
233
  const isRecord = (data) => {
225
- return (typeof data === "object" &&
234
+ return (typeof data === 'object' &&
226
235
  !Array.isArray(data) &&
227
236
  data !== null &&
228
237
  data !== undefined);
229
238
  };
230
239
  const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
240
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
231
241
  // @ts-ignore
232
- const { transform } = await import("i18next-parser");
242
+ const { transform } = await import('i18next-parser');
233
243
  const i18nextParser = new transform({
234
244
  lexers: {
235
245
  jsx: [
236
246
  {
237
- lexer: "JsxLexer",
238
- componentFunctions: componentFunctions.concat(["Trans"]),
247
+ lexer: 'JsxLexer',
248
+ componentFunctions: componentFunctions.concat(['Trans']),
239
249
  },
240
250
  ],
241
251
  tsx: [
242
252
  {
243
- lexer: "JsxLexer",
244
- componentFunctions: componentFunctions.concat(["Trans"]),
253
+ lexer: 'JsxLexer',
254
+ componentFunctions: componentFunctions.concat(['Trans']),
245
255
  },
246
256
  ],
247
257
  },
@@ -249,32 +259,46 @@ const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
249
259
  // Skip any parsed keys that have the `returnObjects` property set to true
250
260
  // As these are used dynamically, they will be skipped to prevent
251
261
  // these keys from being marked as unused.
252
- let extractedResult = [];
262
+ const extractedResult = [];
253
263
  const skippableKeys = [];
254
264
  filesToParse.forEach((file) => {
255
- const rawContent = fs_1.default.readFileSync(file, "utf-8");
265
+ const rawContent = fs_1.default.readFileSync(file, 'utf-8');
256
266
  const entries = i18nextParser.parser.parse(rawContent, file);
257
267
  // Intermediate solution to retrieve all keys from the parser.
258
268
  // This will be built out to also include the namespace and check
259
269
  // the key against the namespace corresponding file.
260
270
  // The current implementation considers the key as used no matter the namespace.
261
271
  for (const entry of entries) {
272
+ // check for namespace, i.e. `namespace:some.key`
273
+ const [namespace, ...keyParts] = entry.key.split(":");
274
+ // If there is a namespace make sure to assign the namespace
275
+ // and update the key name
276
+ // Ensure that the assumed key is not the default value
277
+ if (keyParts.length > 0 && entry.key !== entry.defaultValue) {
278
+ entry.namespace = namespace;
279
+ // rebuild the key without the namespace
280
+ entry.key = keyParts.join(":");
281
+ }
262
282
  if (entry.returnObjects) {
263
283
  skippableKeys.push(entry.key);
264
284
  }
265
285
  else {
266
- extractedResult.push({ file, key: entry.key });
286
+ extractedResult.push({
287
+ file,
288
+ key: entry.key,
289
+ namespace: entry.namespace,
290
+ });
267
291
  }
268
292
  }
269
293
  });
270
294
  return { extractedResult, skippableKeys };
271
295
  };
272
- function flatten(object, prefix = null, result = {}) {
273
- for (let key in object) {
274
- let propName = prefix ? `${prefix}.${key}` : key;
296
+ function _flatten(object, prefix = null, result = {}) {
297
+ for (const key in object) {
298
+ const propName = prefix ? `${prefix}.${key}` : key;
275
299
  const data = object[key];
276
300
  if (isRecord(data)) {
277
- flatten(data, propName, result);
301
+ _flatten(data, propName, result);
278
302
  }
279
303
  else {
280
304
  result[propName] = data;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export type Translation = Record<string, unknown>;
2
2
  export type CheckResult = Record<string, string[]>;
3
+ export type InvalidTranslationEntry = {
4
+ key: string;
5
+ msg: string;
6
+ };
7
+ export type InvalidTranslationsResult = Record<string, InvalidTranslationEntry[]>;
3
8
  export type TranslationFile = {
4
9
  reference: string | null;
5
10
  name: string;
@@ -1,8 +1,5 @@
1
- import { MessageFormatElement } from "@formatjs/icu-messageformat-parser";
2
- import { Translation } from "../types";
3
- export declare const findInvalidTranslations: (source: Translation, files: Record<string, Translation>) => {};
4
- export declare const compareTranslationFiles: (a: Translation, b: Translation) => {
5
- key: string;
6
- msg: string;
7
- }[];
1
+ import { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
2
+ import { InvalidTranslationEntry, InvalidTranslationsResult, Translation } from '../types';
3
+ export declare const findInvalidTranslations: (source: Translation, files: Record<string, Translation>) => InvalidTranslationsResult;
4
+ export declare const compareTranslationFiles: (a: Translation, b: Translation) => InvalidTranslationEntry[];
8
5
  export declare const hasDiff: (a: MessageFormatElement[], b: MessageFormatElement[]) => boolean;
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hasDiff = exports.compareTranslationFiles = exports.findInvalidTranslations = void 0;
4
4
  const icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser");
5
5
  const findInvalidTranslations = (source, files) => {
6
- let differences = {};
6
+ const differences = {};
7
7
  if (Object.keys(files).length === 0) {
8
8
  return differences;
9
9
  }
10
10
  for (const [lang, file] of Object.entries(files)) {
11
11
  const result = (0, exports.compareTranslationFiles)(source, file);
12
12
  if (result.length > 0) {
13
- differences = Object.assign(differences, { [lang]: result });
13
+ differences[lang] = result;
14
14
  }
15
15
  }
16
16
  return differences;
@@ -27,7 +27,7 @@ const sortParsedKeys = (a, b) => {
27
27
  return a.type - b.type;
28
28
  };
29
29
  const compareTranslationFiles = (a, b) => {
30
- let diffs = [];
30
+ const diffs = [];
31
31
  for (const key in a) {
32
32
  if (b[key] === undefined) {
33
33
  continue;
@@ -62,8 +62,9 @@ const hasDiff = (a, b) => {
62
62
  ((0, icu_messageformat_parser_1.isPoundElement)(formatElementA) && (0, icu_messageformat_parser_1.isPoundElement)(formatElementB))) {
63
63
  return false;
64
64
  }
65
- // @ts-ignore
66
- if (formatElementA.value !== formatElementB.value) {
65
+ if (!(0, icu_messageformat_parser_1.isPoundElement)(formatElementA) &&
66
+ !(0, icu_messageformat_parser_1.isPoundElement)(formatElementB) &&
67
+ formatElementA.value !== formatElementB.value) {
67
68
  return true;
68
69
  }
69
70
  if ((0, icu_messageformat_parser_1.isTagElement)(formatElementA) && (0, icu_messageformat_parser_1.isTagElement)(formatElementB)) {
@@ -72,7 +73,7 @@ const hasDiff = (a, b) => {
72
73
  if ((0, icu_messageformat_parser_1.isSelectElement)(formatElementA) && (0, icu_messageformat_parser_1.isSelectElement)(formatElementB)) {
73
74
  const optionsA = Object.keys(formatElementA.options).sort();
74
75
  const optionsB = Object.keys(formatElementB.options).sort();
75
- if (optionsA.join("-") !== optionsB.join("-")) {
76
+ if (optionsA.join('-') !== optionsB.join('-')) {
76
77
  return true;
77
78
  }
78
79
  return optionsA.some((key) => {
@@ -133,7 +134,7 @@ const getErrorMessage = (a, b) => {
133
134
  }
134
135
  if ((0, icu_messageformat_parser_1.isSelectElement)(formatElementA) && (0, icu_messageformat_parser_1.isSelectElement)(formatElementB)) {
135
136
  const optionsA = Object.keys(formatElementA.options).sort();
136
- let elementErrors = [];
137
+ const elementErrors = [];
137
138
  optionsA.forEach((key) => {
138
139
  if (formatElementB.options[key]) {
139
140
  elementErrors.push(getErrorMessage(formatElementA.options[key].value, formatElementB.options[key].value));
@@ -141,12 +142,12 @@ const getErrorMessage = (a, b) => {
141
142
  });
142
143
  acc.push(`Error in select: ${elementErrors
143
144
  .flatMap((elementError) => elementError)
144
- .join(", ")}`);
145
+ .join(', ')}`);
145
146
  return acc;
146
147
  }
147
148
  if ((0, icu_messageformat_parser_1.isPluralElement)(formatElementA) && (0, icu_messageformat_parser_1.isPluralElement)(formatElementB)) {
148
149
  const optionsA = Object.keys(formatElementA.options).sort();
149
- let elementErrors = [];
150
+ const elementErrors = [];
150
151
  optionsA.forEach((key) => {
151
152
  if (formatElementB.options[key]) {
152
153
  elementErrors.push(getErrorMessage(formatElementA.options[key].value, formatElementB.options[key].value));
@@ -154,7 +155,7 @@ const getErrorMessage = (a, b) => {
154
155
  });
155
156
  acc.push(`Error in plural: ${elementErrors
156
157
  .flatMap((elementError) => elementError)
157
- .join(", ")}`);
158
+ .join(', ')}`);
158
159
  return acc;
159
160
  }
160
161
  return acc;
@@ -166,19 +167,19 @@ const getErrorMessage = (a, b) => {
166
167
  acc.push(`Unexpected ${typeLookup[formatElementB.type]} element`);
167
168
  return acc;
168
169
  }, [])
169
- .join(", ");
170
- return [...errors, unexpectedElements].join(", ");
170
+ .join(', ');
171
+ return [...errors, unexpectedElements].join(', ');
171
172
  }
172
- return errors.join(", ");
173
+ return errors.join(', ');
173
174
  };
174
175
  const typeLookup = {
175
- 0: "literal",
176
- 1: "argument",
177
- 2: "number",
178
- 3: "date",
179
- 4: "time",
180
- 5: "select",
181
- 6: "plural",
182
- 7: "pound",
183
- 8: "tag",
176
+ 0: 'literal',
177
+ 1: 'argument',
178
+ 2: 'number',
179
+ 3: 'date',
180
+ 4: 'time',
181
+ 5: 'select',
182
+ 6: 'plural',
183
+ 7: 'pound',
184
+ 8: 'tag',
184
185
  };
@@ -2,81 +2,81 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const findInvalidTranslations_1 = require("./findInvalidTranslations");
4
4
  const flattenTranslations_1 = require("./flattenTranslations");
5
- const sourceFile = require("../../translations/messageExamples/en-us.json");
6
- const secondaryFile = require("../../translations/messageExamples/de-de.json");
7
- describe("findInvalidTranslations:compareTranslationFiles", () => {
8
- it("should return empty array if files are identical", () => {
5
+ const sourceFile = require('../../translations/messageExamples/en-us.json');
6
+ const secondaryFile = require('../../translations/messageExamples/de-de.json');
7
+ describe('findInvalidTranslations:compareTranslationFiles', () => {
8
+ it('should return empty array if files are identical', () => {
9
9
  expect((0, findInvalidTranslations_1.compareTranslationFiles)((0, flattenTranslations_1.flattenTranslations)(sourceFile), (0, flattenTranslations_1.flattenTranslations)(sourceFile))).toEqual([]);
10
10
  });
11
- it("should return the invalid keys in the target file", () => {
11
+ it('should return the invalid keys in the target file', () => {
12
12
  expect((0, findInvalidTranslations_1.compareTranslationFiles)((0, flattenTranslations_1.flattenTranslations)({
13
13
  ...sourceFile,
14
- "ten.eleven.twelve": "ten eleven twelve",
15
- }), (0, flattenTranslations_1.flattenTranslations)(secondaryFile))).toEqual([{ key: "multipleVariables", msg: "Unexpected date element" }]);
14
+ 'ten.eleven.twelve': 'ten eleven twelve',
15
+ }), (0, flattenTranslations_1.flattenTranslations)(secondaryFile))).toEqual([{ key: 'multipleVariables', msg: 'Unexpected date element' }]);
16
16
  });
17
- it("should return empty array if placeholders are identical but in different positions", () => {
17
+ it('should return empty array if placeholders are identical but in different positions', () => {
18
18
  expect((0, findInvalidTranslations_1.compareTranslationFiles)({
19
- basic: "added {this} and {that} should work.",
19
+ basic: 'added {this} and {that} should work.',
20
20
  }, {
21
- basic: "It is {this} with different position {that}",
21
+ basic: 'It is {this} with different position {that}',
22
22
  })).toEqual([]);
23
23
  });
24
24
  });
25
- describe("findInvalidTranslations", () => {
26
- it("should return an empty object if all files have no invalid keys", () => {
25
+ describe('findInvalidTranslations', () => {
26
+ it('should return an empty object if all files have no invalid keys', () => {
27
27
  expect((0, findInvalidTranslations_1.findInvalidTranslations)(sourceFile, { de: sourceFile })).toEqual({});
28
28
  });
29
- it("should return an object containing the keys for the missing language", () => {
30
- expect((0, findInvalidTranslations_1.findInvalidTranslations)({ ...sourceFile, "ten.eleven.twelve": "ten eleven twelve" }, { de: secondaryFile })).toEqual({
31
- de: [{ key: "multipleVariables", msg: "Unexpected date element" }],
29
+ it('should return an object containing the keys for the missing language', () => {
30
+ expect((0, findInvalidTranslations_1.findInvalidTranslations)({ ...sourceFile, 'ten.eleven.twelve': 'ten eleven twelve' }, { de: secondaryFile })).toEqual({
31
+ de: [{ key: 'multipleVariables', msg: 'Unexpected date element' }],
32
32
  });
33
33
  });
34
- it("should return an object containing the keys for every language with missing key", () => {
35
- expect((0, findInvalidTranslations_1.findInvalidTranslations)({ ...sourceFile, "ten.eleven.twelve": "ten eleven twelve" }, {
34
+ it('should return an object containing the keys for every language with missing key', () => {
35
+ expect((0, findInvalidTranslations_1.findInvalidTranslations)({ ...sourceFile, 'ten.eleven.twelve': 'ten eleven twelve' }, {
36
36
  de: secondaryFile,
37
37
  fr: {
38
- "four.five.six": "four five six",
39
- "seven.eight.nine": "seven eight nine",
40
- "message.text-format": "yo,<p><b>John</b></p>!",
38
+ 'four.five.six': 'four five six',
39
+ 'seven.eight.nine': 'seven eight nine',
40
+ 'message.text-format': 'yo,<p><b>John</b></p>!',
41
41
  },
42
42
  })).toEqual({
43
- de: [{ key: "multipleVariables", msg: "Unexpected date element" }],
43
+ de: [{ key: 'multipleVariables', msg: 'Unexpected date element' }],
44
44
  fr: [
45
45
  {
46
- key: "message.text-format",
46
+ key: 'message.text-format',
47
47
  msg: 'Expected tag to contain "b" but received "p"',
48
48
  },
49
49
  ],
50
50
  });
51
51
  });
52
- it("should allow for different types of keys per locale", () => {
52
+ it('should allow for different types of keys per locale', () => {
53
53
  expect((0, findInvalidTranslations_1.findInvalidTranslations)(sourceFile, {
54
54
  de: {
55
55
  ...secondaryFile,
56
- "message.plural": "{count, plural, other {# of {total} items}}",
56
+ 'message.plural': '{count, plural, other {# of {total} items}}',
57
57
  },
58
58
  })).toEqual({
59
59
  de: [
60
60
  {
61
- key: "multipleVariables",
62
- msg: "Unexpected date element",
61
+ key: 'multipleVariables',
62
+ msg: 'Unexpected date element',
63
63
  },
64
64
  ],
65
65
  });
66
66
  });
67
- it("should fail if a variable is changed in one of the translations", () => {
67
+ it('should fail if a variable is changed in one of the translations', () => {
68
68
  expect((0, findInvalidTranslations_1.findInvalidTranslations)(sourceFile, {
69
69
  de: {
70
70
  ...secondaryFile,
71
- "message.plural": "{count, plural, other {# of {cargado} items}}",
71
+ 'message.plural': '{count, plural, other {# of {cargado} items}}',
72
72
  },
73
73
  })).toEqual({
74
74
  de: [
75
75
  {
76
- key: "message.plural",
76
+ key: 'message.plural',
77
77
  msg: 'Error in plural: Expected argument to contain "total" but received "cargado"',
78
78
  },
79
- { key: "multipleVariables", msg: "Unexpected date element" },
79
+ { key: 'multipleVariables', msg: 'Unexpected date element' },
80
80
  ],
81
81
  });
82
82
  });
@@ -4,8 +4,8 @@
4
4
  *
5
5
  *
6
6
  */
7
- import { MessageFormatElement } from "./i18NextParser";
8
- import { Translation } from "../types";
9
- export declare const findInvalid18nTranslations: (source: Translation, targets: Record<string, Translation>) => {};
10
- export declare const compareTranslationFiles: (a: Translation, b: Translation) => unknown[];
7
+ import { MessageFormatElement } from './i18NextParser';
8
+ import { InvalidTranslationEntry, InvalidTranslationsResult, Translation } from '../types';
9
+ export declare const findInvalid18nTranslations: (source: Translation, targets: Record<string, Translation>) => InvalidTranslationsResult;
10
+ export declare const compareTranslationFiles: (a: Translation, b: Translation) => InvalidTranslationEntry[];
11
11
  export declare const hasDiff: (a: MessageFormatElement[], b: MessageFormatElement[]) => boolean;