i18next-cli 1.34.0 → 1.35.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 +1 -1
- package/dist/cjs/cli.js +271 -1
- package/dist/cjs/config.js +211 -1
- package/dist/cjs/extractor/core/ast-visitors.js +372 -1
- package/dist/cjs/extractor/core/extractor.js +245 -1
- package/dist/cjs/extractor/core/key-finder.js +132 -1
- package/dist/cjs/extractor/core/translation-manager.js +745 -1
- package/dist/cjs/extractor/parsers/ast-utils.js +85 -1
- package/dist/cjs/extractor/parsers/call-expression-handler.js +942 -1
- package/dist/cjs/extractor/parsers/comment-parser.js +375 -1
- package/dist/cjs/extractor/parsers/expression-resolver.js +362 -1
- package/dist/cjs/extractor/parsers/jsx-handler.js +492 -1
- package/dist/cjs/extractor/parsers/jsx-parser.js +355 -1
- package/dist/cjs/extractor/parsers/scope-manager.js +408 -1
- package/dist/cjs/extractor/plugin-manager.js +106 -1
- package/dist/cjs/heuristic-config.js +99 -1
- package/dist/cjs/index.js +28 -1
- package/dist/cjs/init.js +174 -1
- package/dist/cjs/linter.js +431 -1
- package/dist/cjs/locize.js +269 -1
- package/dist/cjs/migrator.js +196 -1
- package/dist/cjs/rename-key.js +354 -1
- package/dist/cjs/status.js +336 -1
- package/dist/cjs/syncer.js +120 -1
- package/dist/cjs/types-generator.js +165 -1
- package/dist/cjs/utils/default-value.js +43 -1
- package/dist/cjs/utils/file-utils.js +136 -1
- package/dist/cjs/utils/funnel-msg-tracker.js +75 -1
- package/dist/cjs/utils/logger.js +36 -1
- package/dist/cjs/utils/nested-object.js +124 -1
- package/dist/cjs/utils/validation.js +71 -1
- package/dist/esm/cli.js +269 -1
- package/dist/esm/config.js +206 -1
- package/dist/esm/extractor/core/ast-visitors.js +370 -1
- package/dist/esm/extractor/core/extractor.js +241 -1
- package/dist/esm/extractor/core/key-finder.js +130 -1
- package/dist/esm/extractor/core/translation-manager.js +743 -1
- package/dist/esm/extractor/parsers/ast-utils.js +80 -1
- package/dist/esm/extractor/parsers/call-expression-handler.js +940 -1
- package/dist/esm/extractor/parsers/comment-parser.js +373 -1
- package/dist/esm/extractor/parsers/expression-resolver.js +360 -1
- package/dist/esm/extractor/parsers/jsx-handler.js +490 -1
- package/dist/esm/extractor/parsers/jsx-parser.js +334 -1
- package/dist/esm/extractor/parsers/scope-manager.js +406 -1
- package/dist/esm/extractor/plugin-manager.js +103 -1
- package/dist/esm/heuristic-config.js +97 -1
- package/dist/esm/index.js +11 -1
- package/dist/esm/init.js +172 -1
- package/dist/esm/linter.js +425 -1
- package/dist/esm/locize.js +265 -1
- package/dist/esm/migrator.js +194 -1
- package/dist/esm/rename-key.js +352 -1
- package/dist/esm/status.js +334 -1
- package/dist/esm/syncer.js +118 -1
- package/dist/esm/types-generator.js +163 -1
- package/dist/esm/utils/default-value.js +41 -1
- package/dist/esm/utils/file-utils.js +131 -1
- package/dist/esm/utils/funnel-msg-tracker.js +72 -1
- package/dist/esm/utils/logger.js +34 -1
- package/dist/esm/utils/nested-object.js +120 -1
- package/dist/esm/utils/validation.js +68 -1
- package/package.json +2 -2
- package/types/extractor/core/ast-visitors.d.ts.map +1 -1
- package/types/extractor/parsers/call-expression-handler.d.ts +3 -2
- package/types/extractor/parsers/call-expression-handler.d.ts.map +1 -1
- package/types/locize.d.ts.map +1 -1
|
@@ -1 +1,355 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var astUtils = require('./ast-utils.js');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var reactI18next = require('react-i18next');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
25
|
+
|
|
26
|
+
function getStringLiteralFromExpression(expression) {
|
|
27
|
+
if (!expression)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (expression.type === 'StringLiteral') {
|
|
30
|
+
return expression.value;
|
|
31
|
+
}
|
|
32
|
+
if (expression.type === 'TemplateLiteral' && astUtils.isSimpleTemplateLiteral(expression)) {
|
|
33
|
+
return expression.quasis[0].cooked;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
function getStringLiteralFromAttribute(attr) {
|
|
38
|
+
if (attr.value?.type === 'StringLiteral') {
|
|
39
|
+
return attr.value.value;
|
|
40
|
+
}
|
|
41
|
+
if (attr.value?.type === 'JSXExpressionContainer') {
|
|
42
|
+
return getStringLiteralFromExpression(attr.value.expression);
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extracts translation keys from JSX Trans components.
|
|
48
|
+
*
|
|
49
|
+
* This function handles various Trans component patterns:
|
|
50
|
+
* - Explicit i18nKey prop: `<Trans i18nKey="my.key">content</Trans>`
|
|
51
|
+
* - Implicit keys from children: `<Trans>Hello World</Trans>`
|
|
52
|
+
* - Namespace specification: `<Trans ns="common">content</Trans>`
|
|
53
|
+
* - Default values: `<Trans defaults="Default text">content</Trans>`
|
|
54
|
+
* - Pluralization: `<Trans count={count}>content</Trans>`
|
|
55
|
+
* - HTML preservation: `<Trans>Hello <strong>world</strong></Trans>`
|
|
56
|
+
*
|
|
57
|
+
* @param node - The JSX element node to process
|
|
58
|
+
* @param config - The toolkit configuration containing extraction settings
|
|
59
|
+
* @returns Extracted key information or null if no valid key found
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Input JSX:
|
|
64
|
+
* // <Trans i18nKey="welcome.title" ns="home" defaults="Welcome!">
|
|
65
|
+
* // Welcome to our <strong>amazing</strong> app!
|
|
66
|
+
* // </Trans>
|
|
67
|
+
*
|
|
68
|
+
* const result = extractFromTransComponent(jsxNode, config)
|
|
69
|
+
* // Returns: {
|
|
70
|
+
* // key: 'welcome.title',
|
|
71
|
+
* // keyExpression: { ... },
|
|
72
|
+
* // ns: 'home',
|
|
73
|
+
* // defaultValue: 'Welcome!',
|
|
74
|
+
* // hasCount: false
|
|
75
|
+
* // }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
function extractFromTransComponent(node, config) {
|
|
79
|
+
const i18nKeyAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
|
|
80
|
+
attr.name.type === 'Identifier' &&
|
|
81
|
+
attr.name.value === 'i18nKey');
|
|
82
|
+
const defaultsAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
|
|
83
|
+
attr.name.type === 'Identifier' &&
|
|
84
|
+
attr.name.value === 'defaults');
|
|
85
|
+
const countAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
|
|
86
|
+
attr.name.type === 'Identifier' &&
|
|
87
|
+
attr.name.value === 'count');
|
|
88
|
+
const valuesAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'values');
|
|
89
|
+
// Find the 'count' property in the 'values' object if count={...} is not defined
|
|
90
|
+
let valuesCountProperty;
|
|
91
|
+
if (!countAttr &&
|
|
92
|
+
valuesAttr?.type === 'JSXAttribute' &&
|
|
93
|
+
valuesAttr.value?.type === 'JSXExpressionContainer' &&
|
|
94
|
+
valuesAttr.value.expression.type === 'ObjectExpression') {
|
|
95
|
+
valuesCountProperty = astUtils.getObjectPropValueExpression(valuesAttr.value.expression, 'count');
|
|
96
|
+
}
|
|
97
|
+
const hasCount = !!countAttr || !!valuesCountProperty;
|
|
98
|
+
const tOptionsAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
|
|
99
|
+
attr.name.type === 'Identifier' &&
|
|
100
|
+
attr.name.value === 'tOptions');
|
|
101
|
+
const optionsNode = (tOptionsAttr?.type === 'JSXAttribute' && tOptionsAttr.value?.type === 'JSXExpressionContainer' && tOptionsAttr.value.expression.type === 'ObjectExpression')
|
|
102
|
+
? tOptionsAttr.value.expression
|
|
103
|
+
: undefined;
|
|
104
|
+
// Find isOrdinal prop on the <Trans> component
|
|
105
|
+
const ordinalAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
|
|
106
|
+
attr.name.type === 'Identifier' &&
|
|
107
|
+
attr.name.value === 'ordinal');
|
|
108
|
+
const isOrdinal = !!ordinalAttr;
|
|
109
|
+
const contextAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
|
|
110
|
+
attr.name.type === 'Identifier' &&
|
|
111
|
+
attr.name.value === 'context');
|
|
112
|
+
let contextExpression = (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'JSXExpressionContainer')
|
|
113
|
+
? contextAttr.value.expression
|
|
114
|
+
: (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'StringLiteral')
|
|
115
|
+
? contextAttr.value
|
|
116
|
+
: undefined;
|
|
117
|
+
// 1. Prioritize direct props for 'ns' and 'context'
|
|
118
|
+
const nsAttr = node.opening.attributes?.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'ns');
|
|
119
|
+
let ns;
|
|
120
|
+
if (nsAttr?.type === 'JSXAttribute') {
|
|
121
|
+
ns = getStringLiteralFromAttribute(nsAttr);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
ns = undefined;
|
|
125
|
+
}
|
|
126
|
+
// 2. If not found, fall back to looking inside tOptions
|
|
127
|
+
if (optionsNode) {
|
|
128
|
+
if (ns === undefined) {
|
|
129
|
+
ns = astUtils.getObjectPropValue(optionsNode, 'ns');
|
|
130
|
+
}
|
|
131
|
+
if (contextExpression === undefined) {
|
|
132
|
+
contextExpression = astUtils.getObjectPropValueExpression(optionsNode, 'context');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const serialized = serializeJSXChildren(node.children, config);
|
|
136
|
+
// Handle default value properly
|
|
137
|
+
let defaultValue;
|
|
138
|
+
const defaultAttributeLiteral = defaultsAttr?.type === 'JSXAttribute' ? getStringLiteralFromAttribute(defaultsAttr) : undefined;
|
|
139
|
+
if (defaultAttributeLiteral !== undefined) {
|
|
140
|
+
// Explicit defaults attribute takes precedence
|
|
141
|
+
defaultValue = defaultAttributeLiteral;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Use the configured default value or fall back to empty string
|
|
145
|
+
const configuredDefault = config.extract.defaultValue;
|
|
146
|
+
if (typeof configuredDefault === 'string') {
|
|
147
|
+
defaultValue = configuredDefault;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// For function-based defaults or undefined, use empty string as placeholder
|
|
151
|
+
// The translation manager will handle function resolution with proper context
|
|
152
|
+
defaultValue = '';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let keyExpression;
|
|
156
|
+
let processedKeyValue;
|
|
157
|
+
if (i18nKeyAttr?.type === 'JSXAttribute') {
|
|
158
|
+
if (i18nKeyAttr.value?.type === 'StringLiteral') {
|
|
159
|
+
keyExpression = i18nKeyAttr.value;
|
|
160
|
+
processedKeyValue = keyExpression.value;
|
|
161
|
+
// Validate that the key is not empty
|
|
162
|
+
if (!processedKeyValue || processedKeyValue.trim() === '') {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
// Handle namespace prefix removal when both ns and i18nKey are provided
|
|
166
|
+
if (ns && keyExpression.type === 'StringLiteral') {
|
|
167
|
+
const nsSeparator = config.extract.nsSeparator ?? ':';
|
|
168
|
+
const keyValue = keyExpression.value;
|
|
169
|
+
// If the key starts with the namespace followed by the separator, remove the prefix
|
|
170
|
+
if (nsSeparator && keyValue.startsWith(`${ns}${nsSeparator}`)) {
|
|
171
|
+
processedKeyValue = keyValue.slice(`${ns}${nsSeparator}`.length);
|
|
172
|
+
// Validate processed key is not empty
|
|
173
|
+
if (!processedKeyValue || processedKeyValue.trim() === '') {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
// Create a new StringLiteral with the namespace prefix removed
|
|
177
|
+
keyExpression = {
|
|
178
|
+
...keyExpression,
|
|
179
|
+
value: processedKeyValue
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else if (i18nKeyAttr.value?.type === 'JSXExpressionContainer' &&
|
|
185
|
+
i18nKeyAttr.value.expression.type !== 'JSXEmptyExpression') {
|
|
186
|
+
keyExpression = i18nKeyAttr.value.expression;
|
|
187
|
+
}
|
|
188
|
+
if (!keyExpression)
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
// If no explicit defaults provided and we have a processed key, use it as default value
|
|
192
|
+
// This matches the behavior of other similar tests in the codebase
|
|
193
|
+
if (!defaultsAttr && processedKeyValue && !serialized.trim()) {
|
|
194
|
+
defaultValue = processedKeyValue;
|
|
195
|
+
}
|
|
196
|
+
else if (!defaultsAttr && serialized.trim()) {
|
|
197
|
+
defaultValue = serialized;
|
|
198
|
+
}
|
|
199
|
+
// Determine if tOptions contained explicit defaultValue* properties
|
|
200
|
+
const optionsHasDefaultProps = (opts) => {
|
|
201
|
+
if (!opts || !Array.isArray(opts.properties))
|
|
202
|
+
return false;
|
|
203
|
+
for (const p of opts.properties) {
|
|
204
|
+
if (p && p.type === 'KeyValueProperty' && p.key) {
|
|
205
|
+
const keyName = (p.key.type === 'Identifier' && p.key.value) || (p.key.type === 'StringLiteral' && p.key.value);
|
|
206
|
+
if (typeof keyName === 'string' && keyName.startsWith('defaultValue'))
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
};
|
|
212
|
+
const explicitDefault = defaultAttributeLiteral !== undefined || optionsHasDefaultProps(optionsNode);
|
|
213
|
+
return {
|
|
214
|
+
keyExpression,
|
|
215
|
+
serializedChildren: serialized,
|
|
216
|
+
ns,
|
|
217
|
+
defaultValue,
|
|
218
|
+
hasCount,
|
|
219
|
+
isOrdinal,
|
|
220
|
+
contextExpression,
|
|
221
|
+
optionsNode,
|
|
222
|
+
explicitDefault
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Creates a dummy React component. The implementation / return value is
|
|
227
|
+
* irrelevant, as long as we have something realistic-looking to pass to
|
|
228
|
+
* react-i18next.
|
|
229
|
+
*/
|
|
230
|
+
function makeDummyComponent(name) {
|
|
231
|
+
const result = () => null;
|
|
232
|
+
Object.defineProperty(result, 'name', { value: name });
|
|
233
|
+
result.displayName = name;
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
function makeDummyProps(attributes) {
|
|
237
|
+
return attributes.length
|
|
238
|
+
? Object.fromEntries(attributes.map((attr) => {
|
|
239
|
+
if (attr.type === 'SpreadElement') {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
else if (attr.name.type === 'Identifier') {
|
|
243
|
+
return [attr.name.value, ''];
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return [`${attr.name.namespace.value}:${attr.name.name.value}`, ''];
|
|
247
|
+
}
|
|
248
|
+
}).filter(i => i != null))
|
|
249
|
+
: null;
|
|
250
|
+
}
|
|
251
|
+
function getElementName(element) {
|
|
252
|
+
switch (element.type) {
|
|
253
|
+
case 'Identifier':
|
|
254
|
+
return /\p{Uppercase_Letter}/u.test(element.value) ? makeDummyComponent(element.value) : element.value;
|
|
255
|
+
case 'JSXMemberExpression':
|
|
256
|
+
// element.object should be irrelevant for naming purposes here
|
|
257
|
+
return makeDummyComponent(element.property.value);
|
|
258
|
+
case 'JSXNamespacedName':
|
|
259
|
+
return `${element.namespace.value}:${element.name.value}`;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function trimTextNode(text) {
|
|
263
|
+
text = text.replace(/\r\n/g, '\n'); // Normalize line endings
|
|
264
|
+
// If text is ONLY whitespace AND contains a newline, remove it entirely
|
|
265
|
+
if (/^\s+$/.test(text) && /\n/.test(text)) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
// Trim leading/trailing whitespace sequences containing newlines
|
|
269
|
+
text = text.replace(/^[ \t]*\n[ \t]*/, '');
|
|
270
|
+
text = text.replace(/[ \t]*\n[ \t]*$/, '');
|
|
271
|
+
// Replace whitespace sequences containing newlines with single space
|
|
272
|
+
text = text.replace(/[ \t]*\n[ \t]*/g, ' ');
|
|
273
|
+
return text;
|
|
274
|
+
}
|
|
275
|
+
function swcExpressionToReactNode(expr) {
|
|
276
|
+
switch (expr.type) {
|
|
277
|
+
case 'JSXEmptyExpression':
|
|
278
|
+
return null;
|
|
279
|
+
case 'TsAsExpression':
|
|
280
|
+
return swcExpressionToReactNode(expr.expression);
|
|
281
|
+
case 'ParenthesisExpression':
|
|
282
|
+
return swcExpressionToReactNode(expr.expression);
|
|
283
|
+
case 'ConditionalExpression': {
|
|
284
|
+
const consequent = swcExpressionToReactNode(expr.consequent);
|
|
285
|
+
const alternate = swcExpressionToReactNode(expr.alternate);
|
|
286
|
+
// Heuristic:
|
|
287
|
+
// - If one branch is a strict prefix of the other, pick the longer (keeps extra static tail),
|
|
288
|
+
// e.g. "to select" vs "to select, or right click..."
|
|
289
|
+
// - Otherwise, stay deterministic and prefer consequent (avoids choosing alternates just because they’re 1 char longer).
|
|
290
|
+
if (typeof consequent === 'string' &&
|
|
291
|
+
typeof alternate === 'string' &&
|
|
292
|
+
alternate.length !== consequent.length &&
|
|
293
|
+
alternate.startsWith(consequent)) {
|
|
294
|
+
return alternate;
|
|
295
|
+
}
|
|
296
|
+
return consequent;
|
|
297
|
+
}
|
|
298
|
+
case 'StringLiteral':
|
|
299
|
+
return expr.value;
|
|
300
|
+
case 'TemplateLiteral':
|
|
301
|
+
if (astUtils.isSimpleTemplateLiteral(expr)) {
|
|
302
|
+
return expr.quasis[0].raw;
|
|
303
|
+
}
|
|
304
|
+
// Too complex!
|
|
305
|
+
break;
|
|
306
|
+
case 'Identifier':
|
|
307
|
+
// Not a valid React element, but props for Trans interpolation
|
|
308
|
+
// TODO: This might actually be an error - not sure that react-i18next can handle at runtime
|
|
309
|
+
return { [expr.value]: expr.value };
|
|
310
|
+
case 'ObjectExpression': {
|
|
311
|
+
const keys = expr.properties.map((prop) => {
|
|
312
|
+
if (prop.type === 'KeyValueProperty' && (prop.key.type === 'Identifier' || prop.key.type === 'StringLiteral')) {
|
|
313
|
+
return prop.key.value;
|
|
314
|
+
}
|
|
315
|
+
else if (prop.type === 'Identifier') {
|
|
316
|
+
return prop.value;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
// Too complex to represent! TODO: Flag an error
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}).filter(k => k !== null);
|
|
323
|
+
// Not a valid React element, but props for Trans interpolation
|
|
324
|
+
return Object.fromEntries(keys.map(k => [k, k]));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Too complex to represent! TODO: Flag an error
|
|
328
|
+
return React__namespace.createElement('expression', { expression: expr });
|
|
329
|
+
}
|
|
330
|
+
function swcChildToReactNode(node) {
|
|
331
|
+
switch (node.type) {
|
|
332
|
+
case 'JSXText':
|
|
333
|
+
return trimTextNode(node.value);
|
|
334
|
+
case 'JSXExpressionContainer':
|
|
335
|
+
return swcExpressionToReactNode(node.expression);
|
|
336
|
+
case 'JSXSpreadChild':
|
|
337
|
+
return '';
|
|
338
|
+
case 'JSXElement':
|
|
339
|
+
return React__namespace.createElement(getElementName(node.opening.name), makeDummyProps(node.opening.attributes), ...swcChildrenToReactNodes(node.children));
|
|
340
|
+
case 'JSXFragment':
|
|
341
|
+
return React__namespace.createElement(React__namespace.Fragment, null, ...swcChildrenToReactNodes(node.children));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function swcChildrenToReactNodes(children) {
|
|
345
|
+
return children.map(swcChildToReactNode).filter(n => n !== null);
|
|
346
|
+
}
|
|
347
|
+
function serializeJSXChildren(children, config) {
|
|
348
|
+
const i18nextOptions = { ...reactI18next.getDefaults() };
|
|
349
|
+
if (config.extract.transKeepBasicHtmlNodesFor) {
|
|
350
|
+
i18nextOptions.transKeepBasicHtmlNodesFor = config.extract.transKeepBasicHtmlNodesFor;
|
|
351
|
+
}
|
|
352
|
+
return reactI18next.nodesToString(swcChildrenToReactNodes(children), i18nextOptions);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
exports.extractFromTransComponent = extractFromTransComponent;
|