@lingual/i18n-check 0.8.18 → 0.9.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +4 -20
- package/dist/utils/i18NextSrcParser.d.ts +35 -0
- package/dist/utils/i18NextSrcParser.js +718 -0
- package/package.json +1 -3
package/README.md
CHANGED
|
@@ -247,7 +247,7 @@ yarn i18n:check --locales translations/folderExamples -s en-US -i "some.path.to.
|
|
|
247
247
|
|
|
248
248
|
### --parser-component-functions
|
|
249
249
|
|
|
250
|
-
When using the `--unused` option, there will be situations where the i18next-parser will not be able to find components that wrap a `Trans` component.The component names for i18next-parser to match should be provided via the `--parser-component-functions` option. This option should
|
|
250
|
+
When using the `--unused` option, there will be situations where the i18next-parser will not be able to find components that wrap a `Trans` component. The component names for i18next-parser to match should be provided via the `--parser-component-functions` option. This option should only be used to define additional names for matching, as by default `Trans` will always be matched.
|
|
251
251
|
|
|
252
252
|
```bash
|
|
253
253
|
yarn i18n:check --locales translations/i18NextMessageExamples -s en-US -f i18next
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const findMissingKeys_1 = require("./utils/findMissingKeys");
|
|
|
8
8
|
const findInvalidTranslations_1 = require("./utils/findInvalidTranslations");
|
|
9
9
|
const findInvalidI18NextTranslations_1 = require("./utils/findInvalidI18NextTranslations");
|
|
10
10
|
const cli_lib_1 = require("@formatjs/cli-lib");
|
|
11
|
+
const i18NextSrcParser_1 = require("./utils/i18NextSrcParser");
|
|
11
12
|
const nextIntlSrcParser_1 = require("./utils/nextIntlSrcParser");
|
|
12
13
|
const fs_1 = __importDefault(require("fs"));
|
|
13
14
|
const path_1 = __importDefault(require("path"));
|
|
@@ -270,25 +271,6 @@ const isRecord = (data) => {
|
|
|
270
271
|
data !== undefined);
|
|
271
272
|
};
|
|
272
273
|
const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
|
|
273
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
274
|
-
// @ts-ignore
|
|
275
|
-
const { transform } = await import('i18next-parser');
|
|
276
|
-
const i18nextParser = new transform({
|
|
277
|
-
lexers: {
|
|
278
|
-
jsx: [
|
|
279
|
-
{
|
|
280
|
-
lexer: 'JsxLexer',
|
|
281
|
-
componentFunctions: componentFunctions.concat(['Trans']),
|
|
282
|
-
},
|
|
283
|
-
],
|
|
284
|
-
tsx: [
|
|
285
|
-
{
|
|
286
|
-
lexer: 'JsxLexer',
|
|
287
|
-
componentFunctions: componentFunctions.concat(['Trans']),
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
274
|
// Skip any parsed keys that have the `returnObjects` property set to true
|
|
293
275
|
// As these are used dynamically, they will be skipped to prevent
|
|
294
276
|
// these keys from being marked as unused.
|
|
@@ -296,7 +278,9 @@ const getI18NextKeysInCode = async (filesToParse, componentFunctions = []) => {
|
|
|
296
278
|
const skippableKeys = [];
|
|
297
279
|
filesToParse.forEach((file) => {
|
|
298
280
|
const rawContent = fs_1.default.readFileSync(file, 'utf-8');
|
|
299
|
-
const entries =
|
|
281
|
+
const entries = (0, i18NextSrcParser_1.getKeys)(file, {
|
|
282
|
+
componentFunctions: componentFunctions.concat(['Trans']),
|
|
283
|
+
}, rawContent);
|
|
300
284
|
// Intermediate solution to retrieve all keys from the parser.
|
|
301
285
|
// This will be built out to also include the namespace and check
|
|
302
286
|
// the key against the namespace corresponding file.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Based on the original [i18next-parser](https://github.com/i18next/i18next-parser)
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
type Options = {
|
|
7
|
+
attr: string;
|
|
8
|
+
componentFunctions: string[];
|
|
9
|
+
functions: string[];
|
|
10
|
+
namespaceFunctions: string[];
|
|
11
|
+
parseGenerics: boolean;
|
|
12
|
+
transSupportBasicHtmlNodes: boolean;
|
|
13
|
+
transIdentityFunctionsToIgnore: string[];
|
|
14
|
+
typeMap: Record<string, unknown>;
|
|
15
|
+
translationFunctionsWithArgs: Record<string, {
|
|
16
|
+
pos: number;
|
|
17
|
+
storeGlobally: boolean;
|
|
18
|
+
keyPrefix?: string;
|
|
19
|
+
ns?: string;
|
|
20
|
+
}>;
|
|
21
|
+
keyPrefix?: string;
|
|
22
|
+
defaultNamespace?: string;
|
|
23
|
+
transKeepBasicHtmlNodesFor: string[];
|
|
24
|
+
omitAttributes: string[];
|
|
25
|
+
};
|
|
26
|
+
type FoundKey = {
|
|
27
|
+
key: string;
|
|
28
|
+
ns?: string;
|
|
29
|
+
namespace?: string;
|
|
30
|
+
functionName?: string;
|
|
31
|
+
defaultValue?: string;
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
};
|
|
34
|
+
export declare const getKeys: (path: string, options: Partial<Options>, content: string) => FoundKey[];
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* Based on the original [i18next-parser](https://github.com/i18next/i18next-parser)
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.getKeys = void 0;
|
|
42
|
+
const ts = __importStar(require("typescript"));
|
|
43
|
+
const USE_TRANSLATION = 'useTranslation';
|
|
44
|
+
const WITH_TRANSLATION = 'withTranslation';
|
|
45
|
+
const TRANSLATION_TYPE = 'TFunction';
|
|
46
|
+
const getKeys = (path, options, content) => {
|
|
47
|
+
const sourceFile = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
|
|
48
|
+
const defaultOptions = {
|
|
49
|
+
attr: 'i18nKey',
|
|
50
|
+
componentFunctions: ['Trans'],
|
|
51
|
+
functions: ['t'],
|
|
52
|
+
namespaceFunctions: [USE_TRANSLATION, WITH_TRANSLATION],
|
|
53
|
+
parseGenerics: false,
|
|
54
|
+
transSupportBasicHtmlNodes: false,
|
|
55
|
+
transIdentityFunctionsToIgnore: [],
|
|
56
|
+
typeMap: {},
|
|
57
|
+
translationFunctionsWithArgs: {},
|
|
58
|
+
transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'],
|
|
59
|
+
omitAttributes: ['ns', 'defaults'],
|
|
60
|
+
};
|
|
61
|
+
const parserOptions = { ...defaultOptions, ...options };
|
|
62
|
+
parserOptions.omitAttributes = [
|
|
63
|
+
parserOptions.attr,
|
|
64
|
+
...parserOptions.omitAttributes,
|
|
65
|
+
];
|
|
66
|
+
let foundKeys = [];
|
|
67
|
+
const visit = (node) => {
|
|
68
|
+
if (ts.isVariableDeclaration(node)) {
|
|
69
|
+
extractFromVariableDeclaration(node, parserOptions);
|
|
70
|
+
}
|
|
71
|
+
if (ts.isCallExpression(node)) {
|
|
72
|
+
const keys = extractFromExpression(node, parserOptions);
|
|
73
|
+
if (keys) {
|
|
74
|
+
foundKeys = foundKeys.concat(keys);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (ts.isTaggedTemplateExpression(node)) {
|
|
78
|
+
const key = extractFromTaggedTemplateExpression(node, parserOptions);
|
|
79
|
+
if (key) {
|
|
80
|
+
foundKeys.push(key);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
84
|
+
const key = extractFromJsx(node, content, parserOptions);
|
|
85
|
+
if (key) {
|
|
86
|
+
foundKeys.push(key);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node)) {
|
|
90
|
+
extractFromFunction(node, parserOptions);
|
|
91
|
+
}
|
|
92
|
+
// Collect all keys defined in comments
|
|
93
|
+
const commentKeys = [];
|
|
94
|
+
ts.forEachLeadingCommentRange(content, node.getFullStart(), (pos, end, kind) => {
|
|
95
|
+
if (kind === ts.SyntaxKind.MultiLineCommentTrivia ||
|
|
96
|
+
kind === ts.SyntaxKind.SingleLineCommentTrivia) {
|
|
97
|
+
const text = content.slice(pos, end);
|
|
98
|
+
const functionPattern = '(?:' + parserOptions.functions.join('|').replace('.', '\\.') + ')';
|
|
99
|
+
const callPattern = '(?<=^|\\s|\\.)' + functionPattern + '\\(.*\\)';
|
|
100
|
+
const regexp = new RegExp(callPattern, 'g');
|
|
101
|
+
const expressions = text.match(regexp);
|
|
102
|
+
if (!expressions) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const foundKeys = [];
|
|
106
|
+
expressions.forEach((expression) => {
|
|
107
|
+
const expressionKeys = (0, exports.getKeys)('', {}, expression);
|
|
108
|
+
if (expressionKeys) {
|
|
109
|
+
commentKeys.push(...expressionKeys);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (foundKeys) {
|
|
113
|
+
commentKeys.push(...foundKeys);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
if (commentKeys) {
|
|
118
|
+
commentKeys.forEach((key) => {
|
|
119
|
+
if (!foundKeys.find((k) => k.key === key.key)) {
|
|
120
|
+
foundKeys.push(key);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
ts.forEachChild(node, visit);
|
|
125
|
+
};
|
|
126
|
+
ts.forEachChild(sourceFile, visit);
|
|
127
|
+
return foundKeys;
|
|
128
|
+
};
|
|
129
|
+
exports.getKeys = getKeys;
|
|
130
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L74
|
|
131
|
+
const extractFromJsx = (node, content, options) => {
|
|
132
|
+
const tagNode = ts.isJsxElement(node) ? node.openingElement : node;
|
|
133
|
+
const getKey = (node) => getPropertyValue(node, options.attr);
|
|
134
|
+
if (options.componentFunctions.includes(expressionToName(tagNode.tagName))) {
|
|
135
|
+
const entry = { key: getKey(tagNode) ?? '' };
|
|
136
|
+
const namespace = getPropertyValue(tagNode, 'ns');
|
|
137
|
+
if (namespace) {
|
|
138
|
+
entry.namespace = namespace;
|
|
139
|
+
}
|
|
140
|
+
tagNode.attributes.properties.forEach((property) => {
|
|
141
|
+
if (property.kind === ts.SyntaxKind.JsxSpreadAttribute) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const propertyName = property.name.getText();
|
|
145
|
+
if (options.omitAttributes.includes(propertyName)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (property.initializer) {
|
|
149
|
+
if (ts.isJsxExpression(property.initializer) &&
|
|
150
|
+
property.initializer.expression) {
|
|
151
|
+
if (property.initializer.expression.kind === ts.SyntaxKind.TrueKeyword) {
|
|
152
|
+
entry[propertyName] = true;
|
|
153
|
+
}
|
|
154
|
+
else if (property.initializer.expression.kind === ts.SyntaxKind.FalseKeyword) {
|
|
155
|
+
entry[propertyName] = false;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
entry[propertyName] = `{${cleanMultiLineCode(content.slice(property.initializer.expression.pos, property.initializer.expression.end))}}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (ts.isStringLiteral(property.initializer)) {
|
|
162
|
+
entry[propertyName] = property.initializer.text;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
entry[propertyName] = true;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
const nodeAsString = nodeToString(node, content, options);
|
|
170
|
+
const defaultsProp = getPropertyValue(tagNode, 'defaults');
|
|
171
|
+
let defaultValue = defaultsProp || nodeAsString;
|
|
172
|
+
if (entry.shouldUnescape !== true) {
|
|
173
|
+
defaultValue = unescape(defaultValue);
|
|
174
|
+
}
|
|
175
|
+
if (defaultValue !== '') {
|
|
176
|
+
entry.defaultValue = defaultValue;
|
|
177
|
+
if (!entry.key) {
|
|
178
|
+
entry.key = unescape(nodeAsString) || entry.defaultValue || '';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return entry.key ? entry : null;
|
|
182
|
+
}
|
|
183
|
+
else if (tagNode.tagName.getText() === 'Interpolate') {
|
|
184
|
+
const entry = { key: getKey(tagNode) ?? '' };
|
|
185
|
+
return entry.key ? entry : null;
|
|
186
|
+
}
|
|
187
|
+
else if (tagNode.tagName.getText() === 'Translation') {
|
|
188
|
+
const namespace = getPropertyValue(tagNode, 'ns');
|
|
189
|
+
if (namespace) {
|
|
190
|
+
options.defaultNamespace = namespace;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L77
|
|
195
|
+
const getPropertyValue = (node, attr) => {
|
|
196
|
+
const attribute = node.attributes.properties.find((attribute) => {
|
|
197
|
+
return attribute.name !== undefined && attribute.name.getText() === attr;
|
|
198
|
+
});
|
|
199
|
+
if (!attribute) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
if (!ts.isJsxAttribute(attribute) ||
|
|
203
|
+
!attribute.initializer ||
|
|
204
|
+
(ts.isJsxExpression(attribute.initializer) &&
|
|
205
|
+
attribute.initializer?.expression &&
|
|
206
|
+
ts.isIdentifier(attribute.initializer?.expression))) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
if (ts.isStringLiteral(attribute.initializer)) {
|
|
210
|
+
return attribute.initializer.text;
|
|
211
|
+
}
|
|
212
|
+
if (ts.isJsxExpression(attribute.initializer) &&
|
|
213
|
+
attribute.initializer?.expression &&
|
|
214
|
+
(ts.isStringLiteral(attribute.initializer?.expression) ||
|
|
215
|
+
ts.isNoSubstitutionTemplateLiteral(attribute.initializer.expression))) {
|
|
216
|
+
return attribute.initializer.expression.text;
|
|
217
|
+
}
|
|
218
|
+
return undefined;
|
|
219
|
+
};
|
|
220
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/javascript-lexer.js#L165
|
|
221
|
+
const extractFromTaggedTemplateExpression = (node, options) => {
|
|
222
|
+
const { tag, template } = node;
|
|
223
|
+
if (!options.functions.includes(tag.getText()) &&
|
|
224
|
+
!(ts.isPropertyAccessExpression(tag) &&
|
|
225
|
+
options.functions.includes(tag.name.text))) {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
if (ts.isNoSubstitutionTemplateLiteral(template)) {
|
|
229
|
+
return { key: template.text };
|
|
230
|
+
}
|
|
231
|
+
return undefined;
|
|
232
|
+
};
|
|
233
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/javascript-lexer.js#L75
|
|
234
|
+
const extractFromVariableDeclaration = (node, options) => {
|
|
235
|
+
if (ts.isIdentifier(node.name)) {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
const [firstElement] = node.name.elements;
|
|
239
|
+
if (!ts.isBindingElement(firstElement)) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
const firstElementName = firstElement?.propertyName ?? firstElement.name;
|
|
243
|
+
if (hasEscapedText(firstElementName) &&
|
|
244
|
+
firstElementName?.escapedText === 't' &&
|
|
245
|
+
hasEscapedText(firstElement.name) &&
|
|
246
|
+
options.functions.includes(firstElement?.name?.escapedText.toString()) &&
|
|
247
|
+
node.initializer &&
|
|
248
|
+
ts.isCallExpression(node.initializer) &&
|
|
249
|
+
ts.isIdentifier(node.initializer.expression) &&
|
|
250
|
+
options.namespaceFunctions.includes(
|
|
251
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
252
|
+
// @ts-ignore
|
|
253
|
+
node.initializer?.expression?.escapedText)) {
|
|
254
|
+
options.translationFunctionsWithArgs[firstElement.name.escapedText.toString()] = {
|
|
255
|
+
pos: node.initializer.pos,
|
|
256
|
+
storeGlobally: !(firstElement.propertyName &&
|
|
257
|
+
hasEscapedText(firstElement.propertyName) &&
|
|
258
|
+
firstElement.propertyName?.escapedText),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/javascript-lexer.js#L138
|
|
263
|
+
const extractFromFunction = (node, options) => {
|
|
264
|
+
const tFnParam = node.parameters &&
|
|
265
|
+
node.parameters.find((param) => param.name &&
|
|
266
|
+
ts.isIdentifier(param.name) &&
|
|
267
|
+
options.functions.includes(param.name.text));
|
|
268
|
+
if (tFnParam &&
|
|
269
|
+
tFnParam.type &&
|
|
270
|
+
ts.isTypeReferenceNode(tFnParam.type) &&
|
|
271
|
+
tFnParam.type.typeName &&
|
|
272
|
+
ts.isIdentifier(tFnParam.type.typeName) &&
|
|
273
|
+
tFnParam.type.typeName.text === TRANSLATION_TYPE) {
|
|
274
|
+
if (tFnParam.type.typeArguments && tFnParam.type.typeArguments.length > 0) {
|
|
275
|
+
const [firstArgument] = tFnParam.type.typeArguments;
|
|
276
|
+
if (ts.isLiteralTypeNode(firstArgument) &&
|
|
277
|
+
ts.isStringLiteral(firstArgument.literal)) {
|
|
278
|
+
options.defaultNamespace = firstArgument.literal.text;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/javascript-lexer.js#L189
|
|
284
|
+
const extractFromExpression = (node, options) => {
|
|
285
|
+
const entries = [{ key: '' }];
|
|
286
|
+
const functionDefinition = Object.entries(options.translationFunctionsWithArgs).find(([_name, translationFunc]) => translationFunc?.pos === node.pos);
|
|
287
|
+
let storeGlobally = functionDefinition?.[1].storeGlobally ?? true;
|
|
288
|
+
const isNamespaceFunction = (ts.isIdentifier(node.expression) &&
|
|
289
|
+
node.expression.escapedText &&
|
|
290
|
+
options.namespaceFunctions.includes(node.expression.escapedText)) ||
|
|
291
|
+
options.namespaceFunctions.includes(expressionToName(node.expression));
|
|
292
|
+
if (isNamespaceFunction && node.arguments.length > 0) {
|
|
293
|
+
storeGlobally =
|
|
294
|
+
storeGlobally ||
|
|
295
|
+
(hasEscapedText(node.expression) &&
|
|
296
|
+
node.expression.escapedText === WITH_TRANSLATION);
|
|
297
|
+
const [namespaceArgument, optionsArgument] = node.arguments;
|
|
298
|
+
const namespaces = ts.isArrayLiteralExpression(namespaceArgument)
|
|
299
|
+
? namespaceArgument.elements
|
|
300
|
+
: [namespaceArgument];
|
|
301
|
+
const namespace = namespaces.find((namespace) => ts.isStringLiteral(namespace) ||
|
|
302
|
+
(ts.isIdentifier(namespace) && namespace.text === 'undefined'));
|
|
303
|
+
if (!namespace) {
|
|
304
|
+
// No namespace found - technically a warning could be logged here
|
|
305
|
+
}
|
|
306
|
+
else if (ts.isStringLiteral(namespace)) {
|
|
307
|
+
if (storeGlobally) {
|
|
308
|
+
options.defaultNamespace = namespace.text;
|
|
309
|
+
}
|
|
310
|
+
entries[0].ns = namespace.text;
|
|
311
|
+
}
|
|
312
|
+
if (optionsArgument && ts.isObjectLiteralExpression(optionsArgument)) {
|
|
313
|
+
const keyPrefixNode = optionsArgument.properties.find((p) => p.name &&
|
|
314
|
+
hasEscapedText(p.name) &&
|
|
315
|
+
p.name?.escapedText === 'keyPrefix');
|
|
316
|
+
if (keyPrefixNode != null && ts.isPropertyAssignment(keyPrefixNode)) {
|
|
317
|
+
if (storeGlobally) {
|
|
318
|
+
options.keyPrefix = keyPrefixNode.initializer.getText();
|
|
319
|
+
}
|
|
320
|
+
entries[0].keyPrefix = keyPrefixNode.initializer.getText();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const isTranslationFunction = (ts.isStringLiteral(node.expression) &&
|
|
325
|
+
node.expression.text &&
|
|
326
|
+
options.functions.includes(node.expression.text)) ||
|
|
327
|
+
(hasName(node.expression) &&
|
|
328
|
+
node.expression.name &&
|
|
329
|
+
hasText(node.expression.name) &&
|
|
330
|
+
options.functions.includes(node.expression.name.text)) ||
|
|
331
|
+
options.functions.includes(expressionToName(node.expression));
|
|
332
|
+
if (isTranslationFunction) {
|
|
333
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
334
|
+
// @ts-ignore
|
|
335
|
+
const keyArgument = node.arguments.shift();
|
|
336
|
+
if (!keyArgument) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
if (ts.isStringLiteral(keyArgument) ||
|
|
340
|
+
ts.isNoSubstitutionTemplateLiteral(keyArgument)) {
|
|
341
|
+
entries[0].key = keyArgument.text;
|
|
342
|
+
}
|
|
343
|
+
else if (ts.isBinaryExpression(keyArgument)) {
|
|
344
|
+
const concatenatedString = concatenateString(keyArgument);
|
|
345
|
+
if (!concatenatedString) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
entries[0].key = concatenatedString;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
if (options.parseGenerics && node.typeArguments) {
|
|
354
|
+
const nodeTypeArguments = copyNodeArray(node.typeArguments);
|
|
355
|
+
const typeArgument = nodeTypeArguments.shift();
|
|
356
|
+
const parseTypeArgument = (typeNode) => {
|
|
357
|
+
if (!typeNode) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
361
|
+
for (const member of typeNode.members) {
|
|
362
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
363
|
+
// @ts-ignore
|
|
364
|
+
entries[0][member.name?.text] = '';
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
else if (ts.isTypeReferenceNode(typeNode) &&
|
|
368
|
+
ts.isIdentifier(typeNode.typeName)) {
|
|
369
|
+
const typeName = typeNode.typeName.text;
|
|
370
|
+
if (typeName in options.typeMap) {
|
|
371
|
+
Object.assign(entries[0], options.typeMap[typeName]);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else if ((ts.isUnionTypeNode(typeNode) ||
|
|
375
|
+
ts.isIntersectionTypeNode(typeNode)) &&
|
|
376
|
+
Array.isArray(typeNode.types)) {
|
|
377
|
+
typeNode.types.forEach((type) => parseTypeArgument(type));
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
parseTypeArgument(typeArgument);
|
|
381
|
+
}
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
383
|
+
// @ts-ignore
|
|
384
|
+
let optionsArgument = node.arguments.shift();
|
|
385
|
+
if (optionsArgument &&
|
|
386
|
+
(ts.isStringLiteral(optionsArgument) ||
|
|
387
|
+
ts.isNoSubstitutionTemplateLiteral(optionsArgument))) {
|
|
388
|
+
entries[0].defaultValue = optionsArgument.text;
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
390
|
+
// @ts-ignore
|
|
391
|
+
optionsArgument = node.arguments.shift();
|
|
392
|
+
}
|
|
393
|
+
else if (optionsArgument && ts.isBinaryExpression(optionsArgument)) {
|
|
394
|
+
const concatenatedString = concatenateString(optionsArgument);
|
|
395
|
+
if (!concatenatedString) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
entries[0].defaultValue = concatenatedString;
|
|
399
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
400
|
+
// @ts-ignore
|
|
401
|
+
optionsArgument = node.arguments.shift();
|
|
402
|
+
}
|
|
403
|
+
if (optionsArgument && ts.isObjectLiteralExpression(optionsArgument)) {
|
|
404
|
+
for (const optionProperty of optionsArgument.properties) {
|
|
405
|
+
if (ts.isSpreadAssignment(optionProperty)) {
|
|
406
|
+
// Skip as this can't be processed. A warning could be logged at some point.
|
|
407
|
+
}
|
|
408
|
+
else if (ts.isPropertyAssignment(optionProperty)) {
|
|
409
|
+
const propertyName = getNodeText(optionProperty.name);
|
|
410
|
+
if (optionProperty.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
|
411
|
+
entries[0][propertyName] = true;
|
|
412
|
+
}
|
|
413
|
+
else if (optionProperty.initializer.kind === ts.SyntaxKind.FalseKeyword) {
|
|
414
|
+
entries[0][propertyName] = false;
|
|
415
|
+
}
|
|
416
|
+
else if (ts.isCallExpression(optionProperty.initializer)) {
|
|
417
|
+
const nestedEntries = extractFromExpression(optionProperty.initializer, options);
|
|
418
|
+
if (nestedEntries) {
|
|
419
|
+
entries.push(...nestedEntries);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
entries[0][propertyName] =
|
|
423
|
+
getNodeText(optionProperty.initializer) || '';
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
entries[0][propertyName] =
|
|
428
|
+
getNodeText(optionProperty.initializer) || '';
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
entries[0][getNodeText(optionProperty.name)] = '';
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (entries[0].ns) {
|
|
437
|
+
if (typeof entries[0].ns === 'string') {
|
|
438
|
+
entries[0].namespace = entries[0].ns;
|
|
439
|
+
// Need to double check if this is even needed
|
|
440
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
441
|
+
// @ts-ignore
|
|
442
|
+
}
|
|
443
|
+
else if (Array.isArray(entries[0].ns) && entries[0].ns.length) {
|
|
444
|
+
entries[0].namespace = entries[0].ns[0];
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
entries[0].functionName = hasEscapedText(node.expression)
|
|
448
|
+
? node.expression.escapedText.toString()
|
|
449
|
+
: node.expression.getText();
|
|
450
|
+
return entries
|
|
451
|
+
.map((entry) => {
|
|
452
|
+
const namespace = entry.ns ??
|
|
453
|
+
options.translationFunctionsWithArgs?.[entry.functionName ?? '']
|
|
454
|
+
?.ns ??
|
|
455
|
+
options.defaultNamespace;
|
|
456
|
+
return namespace
|
|
457
|
+
? {
|
|
458
|
+
...entry,
|
|
459
|
+
namespace,
|
|
460
|
+
}
|
|
461
|
+
: entry;
|
|
462
|
+
})
|
|
463
|
+
.map(({ functionName, ...key }) => {
|
|
464
|
+
const keyPrefix = options.translationFunctionsWithArgs?.[functionName ?? '']
|
|
465
|
+
?.keyPrefix ?? options.keyPrefix;
|
|
466
|
+
return keyPrefix
|
|
467
|
+
? {
|
|
468
|
+
...key,
|
|
469
|
+
keyPrefix,
|
|
470
|
+
}
|
|
471
|
+
: key;
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
const isTranslationFunctionCreation = hasEscapedText(node.expression) &&
|
|
475
|
+
node.expression.escapedText &&
|
|
476
|
+
options.namespaceFunctions.includes(node.expression.escapedText);
|
|
477
|
+
if (isTranslationFunctionCreation) {
|
|
478
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
479
|
+
// @ts-ignore
|
|
480
|
+
options.translationFunctionsWithArgs[functionDefinition?.[0] ?? ''] =
|
|
481
|
+
entries[0];
|
|
482
|
+
}
|
|
483
|
+
return null;
|
|
484
|
+
};
|
|
485
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/javascript-lexer.js#L419
|
|
486
|
+
const concatenateString = (binaryExpression, string = '') => {
|
|
487
|
+
if (!ts.isPlusToken(binaryExpression.operatorToken)) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (ts.isBinaryExpression(binaryExpression.left)) {
|
|
491
|
+
string += concatenateString(binaryExpression.left, string);
|
|
492
|
+
}
|
|
493
|
+
else if (ts.isStringLiteral(binaryExpression.left)) {
|
|
494
|
+
string += binaryExpression.left.text;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (ts.isBinaryExpression(binaryExpression.right)) {
|
|
500
|
+
string += concatenateString(binaryExpression.right, string);
|
|
501
|
+
}
|
|
502
|
+
else if (ts.isStringLiteral(binaryExpression.right)) {
|
|
503
|
+
string += binaryExpression.right.text;
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
return string;
|
|
509
|
+
};
|
|
510
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L186
|
|
511
|
+
const nodeToString = (node, content, options) => {
|
|
512
|
+
if (ts.isJsxSelfClosingElement(node)) {
|
|
513
|
+
return '';
|
|
514
|
+
}
|
|
515
|
+
const elements = parseElements(node.children, content, options);
|
|
516
|
+
const elementsToString = (elements) => elements
|
|
517
|
+
.map((element, index) => {
|
|
518
|
+
switch (element.type) {
|
|
519
|
+
case 'js':
|
|
520
|
+
case 'text':
|
|
521
|
+
return element.content;
|
|
522
|
+
case 'tag': {
|
|
523
|
+
const useTagName = element.isBasic &&
|
|
524
|
+
options.transSupportBasicHtmlNodes &&
|
|
525
|
+
options.transKeepBasicHtmlNodesFor.includes(element.name);
|
|
526
|
+
const elementName = useTagName ? element.name : index;
|
|
527
|
+
const childrenString = elementsToString(element.children);
|
|
528
|
+
return childrenString || !(useTagName && element.selfClosing)
|
|
529
|
+
? `<${elementName}>${childrenString}</${elementName}>`
|
|
530
|
+
: `<${elementName} />`;
|
|
531
|
+
}
|
|
532
|
+
default:
|
|
533
|
+
// Do nothing
|
|
534
|
+
}
|
|
535
|
+
})
|
|
536
|
+
.join('');
|
|
537
|
+
return elementsToString(elements);
|
|
538
|
+
};
|
|
539
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L226
|
|
540
|
+
const parseElements = (elements, content, options) => {
|
|
541
|
+
if (!elements) {
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
return elements
|
|
545
|
+
.map((elem) => {
|
|
546
|
+
if (ts.isJsxText(elem)) {
|
|
547
|
+
return {
|
|
548
|
+
type: 'text',
|
|
549
|
+
content: cleanMultiLineCode(elem.text),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
else if (ts.isJsxElement(elem) || ts.isJsxSelfClosingElement(elem)) {
|
|
553
|
+
const element = ts.isJsxElement(elem) ? elem.openingElement : elem;
|
|
554
|
+
const name = hasEscapedText(element.tagName)
|
|
555
|
+
? element.tagName.escapedText.toString()
|
|
556
|
+
: element.tagName.getText();
|
|
557
|
+
const isBasic = !element.attributes.properties.length;
|
|
558
|
+
const hasDynamicChildren = element.attributes.properties.find((prop) => ts.isJsxAttribute(prop) &&
|
|
559
|
+
hasEscapedText(prop.name) &&
|
|
560
|
+
prop.name.escapedText === 'i18nIsDynamicList');
|
|
561
|
+
return {
|
|
562
|
+
type: 'tag',
|
|
563
|
+
children: hasDynamicChildren || !ts.isJsxElement(elem)
|
|
564
|
+
? []
|
|
565
|
+
: parseElements(elem.children, content, options),
|
|
566
|
+
name,
|
|
567
|
+
isBasic,
|
|
568
|
+
selfClosing: ts.isJsxSelfClosingElement(elem),
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
else if (ts.isJsxExpression(elem)) {
|
|
572
|
+
if (!elem.expression) {
|
|
573
|
+
return {
|
|
574
|
+
type: 'text',
|
|
575
|
+
content: '',
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
//Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L265
|
|
579
|
+
while (hasExpression(elem) &&
|
|
580
|
+
elem.expression &&
|
|
581
|
+
ts.isAsExpression(elem.expression)) {
|
|
582
|
+
elem = elem.expression;
|
|
583
|
+
}
|
|
584
|
+
if (hasExpression(elem) &&
|
|
585
|
+
ts.isCallExpression(elem.expression) &&
|
|
586
|
+
ts.isIdentifier(elem.expression.expression) &&
|
|
587
|
+
options.transIdentityFunctionsToIgnore.includes(elem.expression.expression.escapedText.toString()) &&
|
|
588
|
+
elem.expression.arguments.length >= 1) {
|
|
589
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L291
|
|
590
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
591
|
+
// @ts-ignore
|
|
592
|
+
elem = { expression: elem.expression.arguments[0] };
|
|
593
|
+
}
|
|
594
|
+
if (hasExpression(elem) && ts.isStringLiteral(elem.expression)) {
|
|
595
|
+
return {
|
|
596
|
+
type: 'text',
|
|
597
|
+
content: elem.expression.text,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
else if (hasExpression(elem) &&
|
|
601
|
+
ts.isObjectLiteralExpression(elem.expression)) {
|
|
602
|
+
const nonFormatProperties = elem.expression.properties.filter((prop) => prop.name?.getText() !== 'format');
|
|
603
|
+
const formatProperty = elem.expression.properties.find((prop) => prop.name?.getText() === 'format');
|
|
604
|
+
if (nonFormatProperties.length > 1) {
|
|
605
|
+
return {
|
|
606
|
+
type: 'text',
|
|
607
|
+
content: '',
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const nonFormatPropertyName = nonFormatProperties[0]?.name
|
|
611
|
+
? getNodeText(nonFormatProperties[0].name)
|
|
612
|
+
: '';
|
|
613
|
+
const value = formatProperty && ts.isPropertyAssignment(formatProperty)
|
|
614
|
+
? `${nonFormatPropertyName}, ${getNodeText(formatProperty.initializer)}`
|
|
615
|
+
: nonFormatPropertyName;
|
|
616
|
+
return {
|
|
617
|
+
type: 'js',
|
|
618
|
+
content: `{{${value}}}`,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
if (hasExpression(elem)) {
|
|
622
|
+
const slicedExpression = content.slice(elem.expression.pos, elem.expression.end);
|
|
623
|
+
return {
|
|
624
|
+
type: 'js',
|
|
625
|
+
content: `{${slicedExpression}}`,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
.filter((elem) => elem !== undefined)
|
|
631
|
+
.filter((elem) => elem.type !== 'text' || elem.content);
|
|
632
|
+
};
|
|
633
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/jsx-lexer.js#L220
|
|
634
|
+
const cleanMultiLineCode = (text) => {
|
|
635
|
+
return text
|
|
636
|
+
.replace(/(^(\n|\r)\s*)|((\n|\r)\s*$)/g, '')
|
|
637
|
+
.replace(/(\n|\r)\s*/g, ' ');
|
|
638
|
+
};
|
|
639
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/lexers/javascript-lexer.js#L447
|
|
640
|
+
const expressionToName = (expression) => {
|
|
641
|
+
if (!expression) {
|
|
642
|
+
return '';
|
|
643
|
+
}
|
|
644
|
+
if (ts.isStringLiteral(expression) || ts.isIdentifier(expression)) {
|
|
645
|
+
return expression.text;
|
|
646
|
+
}
|
|
647
|
+
else if (hasExpression(expression)) {
|
|
648
|
+
return [
|
|
649
|
+
expressionToName(expression.expression),
|
|
650
|
+
expressionToName(expression.name),
|
|
651
|
+
]
|
|
652
|
+
.filter((s) => s && s.length > 0)
|
|
653
|
+
.join('.');
|
|
654
|
+
}
|
|
655
|
+
return '';
|
|
656
|
+
};
|
|
657
|
+
const hasExpression = (node) => {
|
|
658
|
+
return ('expression' in node &&
|
|
659
|
+
!!node.expression &&
|
|
660
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
661
|
+
ts.isExpression(node.expression));
|
|
662
|
+
};
|
|
663
|
+
const hasEscapedText = (node) => {
|
|
664
|
+
return ts.isIdentifier(node) || ts.isPrivateIdentifier(node);
|
|
665
|
+
};
|
|
666
|
+
const hasName = (node) => {
|
|
667
|
+
return 'name' in node && node.name !== undefined;
|
|
668
|
+
};
|
|
669
|
+
function hasText(node) {
|
|
670
|
+
const result = node &&
|
|
671
|
+
(ts.isStringLiteral(node) ||
|
|
672
|
+
ts.isNumericLiteral(node) ||
|
|
673
|
+
ts.isBigIntLiteral(node) ||
|
|
674
|
+
ts.isNoSubstitutionTemplateLiteral(node) ||
|
|
675
|
+
ts.isRegularExpressionLiteral(node) ||
|
|
676
|
+
ts.isIdentifier(node));
|
|
677
|
+
return result;
|
|
678
|
+
}
|
|
679
|
+
const getNodeText = (node) => {
|
|
680
|
+
if ('text' in node) {
|
|
681
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
682
|
+
return node.text;
|
|
683
|
+
}
|
|
684
|
+
return node.getText();
|
|
685
|
+
};
|
|
686
|
+
const copyNodeArray = (nodes) => {
|
|
687
|
+
return Array.from(nodes);
|
|
688
|
+
};
|
|
689
|
+
// Uses the original unescape code from i18next-parser
|
|
690
|
+
// to ensure that the found keys reflect the original parser implementation
|
|
691
|
+
// Taken from: https://github.com/i18next/i18next-parser/blob/6c10a2b66ebadb8250039d078ad2a53e52753a6e/src/helpers.js#L317
|
|
692
|
+
const unescape = (text) => {
|
|
693
|
+
const matchHtmlEntity = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g;
|
|
694
|
+
const htmlEntities = {
|
|
695
|
+
'&': '&',
|
|
696
|
+
'&': '&',
|
|
697
|
+
'<': '<',
|
|
698
|
+
'<': '<',
|
|
699
|
+
'>': '>',
|
|
700
|
+
'>': '>',
|
|
701
|
+
''': "'",
|
|
702
|
+
''': "'",
|
|
703
|
+
'"': '"',
|
|
704
|
+
'"': '"',
|
|
705
|
+
' ': ' ',
|
|
706
|
+
' ': ' ',
|
|
707
|
+
'©': '©',
|
|
708
|
+
'©': '©',
|
|
709
|
+
'®': '®',
|
|
710
|
+
'®': '®',
|
|
711
|
+
'…': '…',
|
|
712
|
+
'…': '…',
|
|
713
|
+
'/': '/',
|
|
714
|
+
'/': '/',
|
|
715
|
+
};
|
|
716
|
+
const unescapeHtmlEntity = (m) => htmlEntities[m];
|
|
717
|
+
return text.replace(matchHtmlEntity, unescapeHtmlEntity);
|
|
718
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingual/i18n-check",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-beta.1",
|
|
4
4
|
"description": "i18n translation messages check",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"chalk": "^4.1.2",
|
|
29
29
|
"commander": "^12.1.0",
|
|
30
30
|
"glob": "12.0.0",
|
|
31
|
-
"i18next-parser": "^9.3.0",
|
|
32
31
|
"js-yaml": "^4.1.1",
|
|
33
32
|
"typescript": "^5.9.3"
|
|
34
33
|
},
|
|
@@ -36,7 +35,6 @@
|
|
|
36
35
|
"@eslint/js": "^9.39.1",
|
|
37
36
|
"@types/js-yaml": "^4.0.9",
|
|
38
37
|
"@types/node": "^22.19.1",
|
|
39
|
-
"@types/vinyl": "^2.0.12",
|
|
40
38
|
"braces": "^3.0.3",
|
|
41
39
|
"eslint": "^9.39.1",
|
|
42
40
|
"globals": "^16.5.0",
|