@npm-questionpro/wick-ui-i18n 0.14.0 → 2.0.0-next.10
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 +62 -61
- package/index.d.ts +1 -0
- package/index.js +24 -24
- package/package.json +22 -6
- package/src/debug.js +25 -26
- package/src/processor.js +73 -70
- package/src/transform.js +104 -95
- package/src/transformJSXTextWithEntities.js +25 -25
- package/src/transformTemplateLiteral.js +22 -22
- package/src/transformWtCalls.js +41 -41
- package/wickuii18n.test.js +0 -873
package/src/transform.js
CHANGED
|
@@ -5,21 +5,18 @@
|
|
|
5
5
|
* - Prepends the necessary imports when added.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import MagicString from
|
|
9
|
-
import {
|
|
10
|
-
import _traverse from
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
recordWtCall,
|
|
16
|
-
transformWtTemplateLiteral,
|
|
17
|
-
} from "./transformWtCalls.js";
|
|
8
|
+
import MagicString from 'magic-string'
|
|
9
|
+
import {parse} from '@babel/parser'
|
|
10
|
+
import _traverse from '@babel/traverse'
|
|
11
|
+
import {getComponentTree} from './debug.js'
|
|
12
|
+
import {transformTemplateLiteralExpression} from './transformTemplateLiteral.js'
|
|
13
|
+
import {transformJSXTextWithEntities} from './transformJSXTextWithEntities.js'
|
|
14
|
+
import {recordWtCall, transformWtTemplateLiteral} from './transformWtCalls.js'
|
|
18
15
|
|
|
19
|
-
const traverse = _traverse.default || _traverse
|
|
16
|
+
const traverse = _traverse.default || _traverse
|
|
20
17
|
|
|
21
18
|
/** Babel parser plugins applied to every file. */
|
|
22
|
-
const BABEL_PLUGINS = [
|
|
19
|
+
const BABEL_PLUGINS = ['jsx', 'typescript']
|
|
23
20
|
|
|
24
21
|
/**
|
|
25
22
|
* Matches any HTML entity: named (&), decimal (©), or hex (©).
|
|
@@ -27,14 +24,14 @@ const BABEL_PLUGINS = ["jsx", "typescript"];
|
|
|
27
24
|
* Note: for JSXText this must be tested against the RAW source, not the Babel
|
|
28
25
|
* decoded .value (Babel turns & → "&", → "\u00a0", etc.).
|
|
29
26
|
*/
|
|
30
|
-
const HTML_ENTITY_RE = /&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+)
|
|
27
|
+
const HTML_ENTITY_RE = /&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+);/
|
|
31
28
|
|
|
32
29
|
/** @param {import('@babel/types').Node} node @returns {string|null} */
|
|
33
30
|
function getStaticString(node) {
|
|
34
|
-
if (node.type ===
|
|
35
|
-
if (node.type ===
|
|
36
|
-
return node.quasis[0].value.cooked
|
|
37
|
-
return null
|
|
31
|
+
if (node.type === 'StringLiteral') return node.value
|
|
32
|
+
if (node.type === 'TemplateLiteral' && !node.expressions.length)
|
|
33
|
+
return node.quasis[0].value.cooked
|
|
34
|
+
return null
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
/**
|
|
@@ -49,20 +46,28 @@ function getStaticString(node) {
|
|
|
49
46
|
* @returns {boolean} true when at least one branch was captured
|
|
50
47
|
*/
|
|
51
48
|
function handleConditional(expr, path, ms, processor, id) {
|
|
52
|
-
let changed = false
|
|
49
|
+
let changed = false
|
|
53
50
|
|
|
54
51
|
for (const branch of [expr.consequent, expr.alternate]) {
|
|
55
|
-
const text = getStaticString(branch)
|
|
52
|
+
const text = getStaticString(branch)
|
|
56
53
|
if (text !== null) {
|
|
57
54
|
changed =
|
|
58
|
-
handleCapture(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
handleCapture(
|
|
56
|
+
path,
|
|
57
|
+
text,
|
|
58
|
+
branch.start,
|
|
59
|
+
branch.end,
|
|
60
|
+
ms,
|
|
61
|
+
processor,
|
|
62
|
+
id,
|
|
63
|
+
true,
|
|
64
|
+
) || changed
|
|
65
|
+
} else if (branch.type === 'ConditionalExpression') {
|
|
66
|
+
changed = handleConditional(branch, path, ms, processor, id) || changed
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
return changed
|
|
70
|
+
return changed
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
/**
|
|
@@ -90,18 +95,22 @@ function handleCapture(
|
|
|
90
95
|
) {
|
|
91
96
|
const cleanText = text
|
|
92
97
|
.trim()
|
|
93
|
-
.replace(/\n/g,
|
|
94
|
-
.replace(/\s{2,}/g,
|
|
95
|
-
if (!cleanText || !processor.shouldTranslate(path)) return false
|
|
98
|
+
.replace(/\n/g, ' ')
|
|
99
|
+
.replace(/\s{2,}/g, ' ')
|
|
100
|
+
if (!cleanText || !processor.shouldTranslate(path)) return false
|
|
96
101
|
// For StringLiteral / TemplateLiteral quasis / ternary branches: entities are
|
|
97
102
|
// not decoded by the JS parser, so cleanText still contains "&" etc.
|
|
98
|
-
if (HTML_ENTITY_RE.test(cleanText)) return false
|
|
103
|
+
if (HTML_ENTITY_RE.test(cleanText)) return false
|
|
99
104
|
|
|
100
|
-
const key = (!skipExplicitKey && processor.getExplicitKey(path)) || cleanText
|
|
101
|
-
processor.record(key, cleanText, id, getComponentTree(path))
|
|
105
|
+
const key = (!skipExplicitKey && processor.getExplicitKey(path)) || cleanText
|
|
106
|
+
processor.record(key, cleanText, id, getComponentTree(path))
|
|
102
107
|
|
|
103
|
-
ms.overwrite(
|
|
104
|
-
|
|
108
|
+
ms.overwrite(
|
|
109
|
+
start,
|
|
110
|
+
end,
|
|
111
|
+
`<WuTranslate __i18nKey=${JSON.stringify(key)}></WuTranslate>`,
|
|
112
|
+
)
|
|
113
|
+
return true
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
/**
|
|
@@ -116,34 +125,34 @@ function handleCapture(
|
|
|
116
125
|
*/
|
|
117
126
|
export function transformFile(code, id, processor) {
|
|
118
127
|
const ast = parse(code, {
|
|
119
|
-
sourceType:
|
|
128
|
+
sourceType: 'module',
|
|
120
129
|
plugins: BABEL_PLUGINS,
|
|
121
|
-
})
|
|
130
|
+
})
|
|
122
131
|
|
|
123
|
-
const ms = new MagicString(code)
|
|
124
|
-
let needsImport = false
|
|
125
|
-
let hasImport = false
|
|
126
|
-
let hasWtTransform = false
|
|
127
|
-
let needsWtImport = false
|
|
128
|
-
let hasWtImport = false
|
|
132
|
+
const ms = new MagicString(code)
|
|
133
|
+
let needsImport = false
|
|
134
|
+
let hasImport = false
|
|
135
|
+
let hasWtTransform = false
|
|
136
|
+
let needsWtImport = false
|
|
137
|
+
let hasWtImport = false
|
|
129
138
|
|
|
130
139
|
traverse(ast, {
|
|
131
140
|
/** wt("static string") call — record the key in the dictionary.
|
|
132
141
|
* No code transformation; wt() handles runtime lookup.
|
|
133
142
|
*/
|
|
134
143
|
CallExpression(path) {
|
|
135
|
-
if (recordWtCall(path, processor, id)) return
|
|
144
|
+
if (recordWtCall(path, processor, id)) return
|
|
136
145
|
if (transformWtTemplateLiteral(path, code, ms, processor, id)) {
|
|
137
|
-
hasWtTransform = true
|
|
146
|
+
hasWtTransform = true
|
|
138
147
|
}
|
|
139
148
|
},
|
|
140
149
|
|
|
141
150
|
/** Track whether WuTranslate / wt are already imported so we don't duplicate them. */
|
|
142
151
|
ImportDeclaration(path) {
|
|
143
|
-
if (path.node.source.value.includes(
|
|
152
|
+
if (path.node.source.value.includes('wick-ui-lib')) {
|
|
144
153
|
for (const s of path.node.specifiers) {
|
|
145
|
-
if (s.imported?.name ===
|
|
146
|
-
if (s.imported?.name ===
|
|
154
|
+
if (s.imported?.name === 'WuTranslate') hasImport = true
|
|
155
|
+
if (s.imported?.name === 'wt') hasWtImport = true
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
},
|
|
@@ -154,19 +163,19 @@ export function transformFile(code, id, processor) {
|
|
|
154
163
|
* Enabled via `extractFromKeys` option.
|
|
155
164
|
*/
|
|
156
165
|
ObjectProperty(path) {
|
|
157
|
-
if (!processor.extractFromKeys.size) return
|
|
158
|
-
const keyNode = path.node.key
|
|
159
|
-
const keyName = keyNode.name ?? keyNode.value
|
|
160
|
-
if (!keyName || !processor.extractFromKeys.has(keyName)) return
|
|
161
|
-
const value = path.node.value
|
|
162
|
-
if (value.type !==
|
|
166
|
+
if (!processor.extractFromKeys.size) return
|
|
167
|
+
const keyNode = path.node.key
|
|
168
|
+
const keyName = keyNode.name ?? keyNode.value
|
|
169
|
+
if (!keyName || !processor.extractFromKeys.has(keyName)) return
|
|
170
|
+
const value = path.node.value
|
|
171
|
+
if (value.type !== 'StringLiteral') return
|
|
163
172
|
const text = value.value
|
|
164
173
|
.trim()
|
|
165
|
-
.replace(/\n/g,
|
|
166
|
-
.replace(/\s{2,}/g,
|
|
167
|
-
if (!text) return
|
|
168
|
-
if (HTML_ENTITY_RE.test(text)) return
|
|
169
|
-
processor.record(text, text, id,
|
|
174
|
+
.replace(/\n/g, ' ')
|
|
175
|
+
.replace(/\s{2,}/g, ' ')
|
|
176
|
+
if (!text) return
|
|
177
|
+
if (HTML_ENTITY_RE.test(text)) return
|
|
178
|
+
processor.record(text, text, id, '(data)')
|
|
170
179
|
},
|
|
171
180
|
|
|
172
181
|
/**
|
|
@@ -174,42 +183,42 @@ export function transformFile(code, id, processor) {
|
|
|
174
183
|
* rewritten to `{wt("foo")}` on Wu* components.
|
|
175
184
|
*/
|
|
176
185
|
JSXAttribute(path) {
|
|
177
|
-
const propName = path.node.name.name
|
|
178
|
-
if (typeof propName !==
|
|
179
|
-
const value = path.node.value
|
|
180
|
-
if (!value || value.type !==
|
|
186
|
+
const propName = path.node.name.name
|
|
187
|
+
if (typeof propName !== 'string') return
|
|
188
|
+
const value = path.node.value
|
|
189
|
+
if (!value || value.type !== 'StringLiteral') return
|
|
181
190
|
|
|
182
|
-
const rawValue = code.slice(value.start, value.end)
|
|
183
|
-
if (HTML_ENTITY_RE.test(rawValue)) return
|
|
191
|
+
const rawValue = code.slice(value.start, value.end)
|
|
192
|
+
if (HTML_ENTITY_RE.test(rawValue)) return
|
|
184
193
|
|
|
185
194
|
const text = value.value
|
|
186
195
|
.trim()
|
|
187
|
-
.replace(/\n/g,
|
|
188
|
-
.replace(/\s{2,}/g,
|
|
189
|
-
if (!text) return
|
|
196
|
+
.replace(/\n/g, ' ')
|
|
197
|
+
.replace(/\s{2,}/g, ' ')
|
|
198
|
+
if (!text) return
|
|
190
199
|
|
|
191
|
-
if (!processor.shouldTranslateProp(propName, path)) return
|
|
200
|
+
if (!processor.shouldTranslateProp(propName, path)) return
|
|
192
201
|
|
|
193
|
-
processor.record(text, text, id, getComponentTree(path))
|
|
194
|
-
ms.overwrite(value.start, value.end, `{wt(${JSON.stringify(text)})}`)
|
|
195
|
-
needsWtImport = true
|
|
202
|
+
processor.record(text, text, id, getComponentTree(path))
|
|
203
|
+
ms.overwrite(value.start, value.end, `{wt(${JSON.stringify(text)})}`)
|
|
204
|
+
needsWtImport = true
|
|
196
205
|
},
|
|
197
206
|
|
|
198
207
|
/** Plain JSX text: `<Foo>Hello world</Foo>` */
|
|
199
208
|
JSXText(path) {
|
|
200
|
-
const text = path.node.value
|
|
201
|
-
const trimmed = text.trim()
|
|
209
|
+
const text = path.node.value
|
|
210
|
+
const trimmed = text.trim()
|
|
202
211
|
// Babel decodes entities in JSXText.value (& → "&", © → "©").
|
|
203
212
|
// Check the raw source slice — if entities found, split around them so
|
|
204
213
|
// translatable text segments are still wrapped while entities stay put.
|
|
205
|
-
const rawSource = code.slice(path.node.start, path.node.end)
|
|
214
|
+
const rawSource = code.slice(path.node.start, path.node.end)
|
|
206
215
|
if (HTML_ENTITY_RE.test(rawSource)) {
|
|
207
216
|
if (transformJSXTextWithEntities(path, rawSource, ms, processor, id)) {
|
|
208
|
-
needsImport = true
|
|
217
|
+
needsImport = true
|
|
209
218
|
}
|
|
210
|
-
return
|
|
219
|
+
return
|
|
211
220
|
}
|
|
212
|
-
const start = path.node.start + text.indexOf(trimmed)
|
|
221
|
+
const start = path.node.start + text.indexOf(trimmed)
|
|
213
222
|
if (
|
|
214
223
|
handleCapture(
|
|
215
224
|
path,
|
|
@@ -221,7 +230,7 @@ export function transformFile(code, id, processor) {
|
|
|
221
230
|
id,
|
|
222
231
|
)
|
|
223
232
|
) {
|
|
224
|
-
needsImport = true
|
|
233
|
+
needsImport = true
|
|
225
234
|
}
|
|
226
235
|
},
|
|
227
236
|
|
|
@@ -233,26 +242,26 @@ export function transformFile(code, id, processor) {
|
|
|
233
242
|
* a JSXAttribute, not a JSXElement child position.
|
|
234
243
|
*/
|
|
235
244
|
JSXExpressionContainer(path) {
|
|
236
|
-
if (path.parent.type ===
|
|
245
|
+
if (path.parent.type === 'JSXAttribute') return
|
|
237
246
|
|
|
238
|
-
const expr = path.node.expression
|
|
239
|
-
let text = null
|
|
247
|
+
const expr = path.node.expression
|
|
248
|
+
let text = null
|
|
240
249
|
|
|
241
|
-
if (expr.type ===
|
|
242
|
-
text = expr.value
|
|
250
|
+
if (expr.type === 'StringLiteral') {
|
|
251
|
+
text = expr.value
|
|
243
252
|
} else if (
|
|
244
|
-
expr.type ===
|
|
253
|
+
expr.type === 'TemplateLiteral' &&
|
|
245
254
|
expr.expressions.length > 0
|
|
246
255
|
) {
|
|
247
256
|
if (transformTemplateLiteralExpression(path, code, ms, processor, id)) {
|
|
248
|
-
needsImport = true
|
|
257
|
+
needsImport = true
|
|
249
258
|
}
|
|
250
|
-
return
|
|
251
|
-
} else if (expr.type ===
|
|
252
|
-
text = expr.quasis[0].value.cooked
|
|
253
|
-
} else if (expr.type ===
|
|
254
|
-
if (handleConditional(expr, path, ms, processor, id)) needsImport = true
|
|
255
|
-
return
|
|
259
|
+
return
|
|
260
|
+
} else if (expr.type === 'TemplateLiteral' && !expr.expressions.length) {
|
|
261
|
+
text = expr.quasis[0].value.cooked
|
|
262
|
+
} else if (expr.type === 'ConditionalExpression') {
|
|
263
|
+
if (handleConditional(expr, path, ms, processor, id)) needsImport = true
|
|
264
|
+
return
|
|
256
265
|
}
|
|
257
266
|
|
|
258
267
|
if (
|
|
@@ -267,20 +276,20 @@ export function transformFile(code, id, processor) {
|
|
|
267
276
|
id,
|
|
268
277
|
)
|
|
269
278
|
) {
|
|
270
|
-
needsImport = true
|
|
279
|
+
needsImport = true
|
|
271
280
|
}
|
|
272
281
|
},
|
|
273
|
-
})
|
|
282
|
+
})
|
|
274
283
|
|
|
275
|
-
if (!needsImport && !hasWtTransform && !needsWtImport) return null
|
|
284
|
+
if (!needsImport && !hasWtTransform && !needsWtImport) return null
|
|
276
285
|
|
|
277
286
|
if (needsWtImport && !hasWtImport) {
|
|
278
|
-
ms.prepend(`import { wt } from '@npm-questionpro/wick-ui-lib';\n`)
|
|
287
|
+
ms.prepend(`import { wt } from '@npm-questionpro/wick-ui-lib';\n`)
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
if (needsImport && !hasImport) {
|
|
282
|
-
ms.prepend(`import { WuTranslate } from '@npm-questionpro/wick-ui-lib';\n`)
|
|
291
|
+
ms.prepend(`import { WuTranslate } from '@npm-questionpro/wick-ui-lib';\n`)
|
|
283
292
|
}
|
|
284
293
|
|
|
285
|
-
return {
|
|
294
|
+
return {code: ms.toString(), map: ms.generateMap({hires: true})}
|
|
286
295
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* <WuButton><<WuTranslate __i18nKey="Tag" />></WuButton>
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import {getComponentTree} from './debug.js'
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Splits the raw source around HTML entities (capturing group keeps entities
|
|
@@ -27,13 +27,13 @@ import { getComponentTree } from "./debug.js";
|
|
|
27
27
|
* "<Tag>" → ["", "<", "Tag", ">", ""]
|
|
28
28
|
*/
|
|
29
29
|
const HTML_ENTITY_SPLIT_RE =
|
|
30
|
-
/(&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+);)/g
|
|
30
|
+
/(&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+);)/g
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Tests whether a single segment (from the split above) is itself an entity.
|
|
34
34
|
*/
|
|
35
35
|
const HTML_ENTITY_SEGMENT_RE =
|
|
36
|
-
/^&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+)
|
|
36
|
+
/^&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+);$/
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Given a text segment, extract leading whitespace, the translatable core,
|
|
@@ -43,15 +43,15 @@ const HTML_ENTITY_SEGMENT_RE =
|
|
|
43
43
|
* @returns {{ leading: string, text: string, trailing: string }}
|
|
44
44
|
*/
|
|
45
45
|
function splitSegment(raw) {
|
|
46
|
-
const leading = raw.match(/^\s*/)[0]
|
|
47
|
-
const trailing = raw.match(/\s*$/)[0]
|
|
46
|
+
const leading = raw.match(/^\s*/)[0]
|
|
47
|
+
const trailing = raw.match(/\s*$/)[0]
|
|
48
48
|
// Normalise internal whitespace to match handleCapture behaviour:
|
|
49
49
|
// newlines → single space, consecutive spaces → single space.
|
|
50
50
|
const text = raw
|
|
51
51
|
.slice(leading.length, raw.length - trailing.length)
|
|
52
|
-
.replace(/\n/g,
|
|
53
|
-
.replace(/\s{2,}/g,
|
|
54
|
-
return {
|
|
52
|
+
.replace(/\n/g, ' ')
|
|
53
|
+
.replace(/\s{2,}/g, ' ')
|
|
54
|
+
return {leading, text, trailing}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -80,48 +80,48 @@ export function transformJSXTextWithEntities(
|
|
|
80
80
|
processor,
|
|
81
81
|
id,
|
|
82
82
|
) {
|
|
83
|
-
if (!processor.shouldTranslate(path)) return false
|
|
83
|
+
if (!processor.shouldTranslate(path)) return false
|
|
84
84
|
|
|
85
85
|
// data-i18n-key cannot span multiple split segments — warn and ignore it.
|
|
86
|
-
const explicitKey = processor.getExplicitKey(path)
|
|
86
|
+
const explicitKey = processor.getExplicitKey(path)
|
|
87
87
|
if (explicitKey) {
|
|
88
88
|
console.warn(
|
|
89
89
|
`[wick-i18n] data-i18n-key="${explicitKey}" is set on a node whose text ` +
|
|
90
90
|
`contains HTML entities. The key cannot apply across multiple segments ` +
|
|
91
91
|
`— splitting without it.`,
|
|
92
|
-
)
|
|
92
|
+
)
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
const segments = rawSource.split(HTML_ENTITY_SPLIT_RE)
|
|
96
|
-
const componentTree = getComponentTree(path)
|
|
95
|
+
const segments = rawSource.split(HTML_ENTITY_SPLIT_RE)
|
|
96
|
+
const componentTree = getComponentTree(path)
|
|
97
97
|
|
|
98
|
-
const parts = []
|
|
99
|
-
let hasTranslatable = false
|
|
98
|
+
const parts = []
|
|
99
|
+
let hasTranslatable = false
|
|
100
100
|
|
|
101
101
|
for (const seg of segments) {
|
|
102
|
-
if (!seg) continue
|
|
102
|
+
if (!seg) continue // empty strings produced by split at boundaries
|
|
103
103
|
|
|
104
104
|
if (HTML_ENTITY_SEGMENT_RE.test(seg)) {
|
|
105
105
|
// Raw entity — emit as-is, no translation key
|
|
106
|
-
parts.push(seg)
|
|
106
|
+
parts.push(seg)
|
|
107
107
|
} else {
|
|
108
|
-
const {
|
|
108
|
+
const {leading, text, trailing} = splitSegment(seg)
|
|
109
109
|
|
|
110
110
|
if (text) {
|
|
111
|
-
processor.record(text, text, id, componentTree)
|
|
111
|
+
processor.record(text, text, id, componentTree)
|
|
112
112
|
parts.push(
|
|
113
113
|
`${leading}<WuTranslate __i18nKey=${JSON.stringify(text)}></WuTranslate>${trailing}`,
|
|
114
|
-
)
|
|
115
|
-
hasTranslatable = true
|
|
114
|
+
)
|
|
115
|
+
hasTranslatable = true
|
|
116
116
|
} else {
|
|
117
117
|
// Pure whitespace between / around entities — preserve as JSX text
|
|
118
|
-
parts.push(seg)
|
|
118
|
+
parts.push(seg)
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
if (!hasTranslatable) return false
|
|
123
|
+
if (!hasTranslatable) return false
|
|
124
124
|
|
|
125
|
-
ms.overwrite(path.node.start, path.node.end, parts.join(
|
|
126
|
-
return true
|
|
125
|
+
ms.overwrite(path.node.start, path.node.end, parts.join(''))
|
|
126
|
+
return true
|
|
127
127
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* <WuButton><><WuTranslate __i18nKey="hello" /> {name} <WuTranslate __i18nKey="how are you" /></></WuButton>
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {getComponentTree} from './debug.js'
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Given a quasi's cooked string, extract leading whitespace, trimmed text,
|
|
@@ -21,10 +21,10 @@ import { getComponentTree } from "./debug.js";
|
|
|
21
21
|
* @returns {{ leading: string, text: string, trailing: string }}
|
|
22
22
|
*/
|
|
23
23
|
function splitQuasi(raw) {
|
|
24
|
-
const leading = raw.match(/^\s*/)[0]
|
|
25
|
-
const trailing = raw.match(/\s*$/)[0]
|
|
26
|
-
const text = raw.slice(leading.length, raw.length - trailing.length)
|
|
27
|
-
return {
|
|
24
|
+
const leading = raw.match(/^\s*/)[0]
|
|
25
|
+
const trailing = raw.match(/\s*$/)[0]
|
|
26
|
+
const text = raw.slice(leading.length, raw.length - trailing.length)
|
|
27
|
+
return {leading, text, trailing}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -52,39 +52,39 @@ export function transformTemplateLiteralExpression(
|
|
|
52
52
|
processor,
|
|
53
53
|
id,
|
|
54
54
|
) {
|
|
55
|
-
if (!processor.shouldTranslate(path)) return false
|
|
55
|
+
if (!processor.shouldTranslate(path)) return false
|
|
56
56
|
|
|
57
|
-
const container = path.node
|
|
58
|
-
const expr = container.expression
|
|
59
|
-
const {
|
|
60
|
-
const componentTree = getComponentTree(path)
|
|
57
|
+
const container = path.node
|
|
58
|
+
const expr = container.expression // TemplateLiteral
|
|
59
|
+
const {quasis, expressions} = expr
|
|
60
|
+
const componentTree = getComponentTree(path)
|
|
61
61
|
|
|
62
|
-
const parts = []
|
|
63
|
-
let hasTranslatable = false
|
|
62
|
+
const parts = []
|
|
63
|
+
let hasTranslatable = false
|
|
64
64
|
|
|
65
65
|
for (let i = 0; i < quasis.length; i++) {
|
|
66
|
-
const cooked = quasis[i].value.cooked ?? quasis[i].value.raw
|
|
67
|
-
const {
|
|
66
|
+
const cooked = quasis[i].value.cooked ?? quasis[i].value.raw
|
|
67
|
+
const {leading, text, trailing} = splitQuasi(cooked)
|
|
68
68
|
|
|
69
69
|
if (text) {
|
|
70
|
-
processor.record(text, text, id, componentTree)
|
|
70
|
+
processor.record(text, text, id, componentTree)
|
|
71
71
|
parts.push(
|
|
72
72
|
`${leading}<WuTranslate __i18nKey=${JSON.stringify(text)}></WuTranslate>${trailing}`,
|
|
73
|
-
)
|
|
74
|
-
hasTranslatable = true
|
|
73
|
+
)
|
|
74
|
+
hasTranslatable = true
|
|
75
75
|
}
|
|
76
76
|
// whitespace-only or empty quasi — emit nothing (no key, no node)
|
|
77
77
|
|
|
78
78
|
// Interleave: expression follows its preceding quasi (skip after last quasi)
|
|
79
79
|
if (i < expressions.length) {
|
|
80
|
-
const exprNode = expressions[i]
|
|
81
|
-
parts.push(`{${code.slice(exprNode.start, exprNode.end)}}`)
|
|
80
|
+
const exprNode = expressions[i]
|
|
81
|
+
parts.push(`{${code.slice(exprNode.start, exprNode.end)}}`)
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
if (!hasTranslatable) return false
|
|
85
|
+
if (!hasTranslatable) return false
|
|
86
86
|
|
|
87
87
|
// Replace the entire {`...`} container with a React fragment
|
|
88
|
-
ms.overwrite(container.start, container.end, `<>${parts.join(
|
|
89
|
-
return true
|
|
88
|
+
ms.overwrite(container.start, container.end, `<>${parts.join('')}</>`)
|
|
89
|
+
return true
|
|
90
90
|
}
|
package/src/transformWtCalls.js
CHANGED
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
* @returns {{ leading: string, text: string, trailing: string }}
|
|
27
27
|
*/
|
|
28
28
|
function splitQuasi(raw) {
|
|
29
|
-
const leading = raw.match(/^\s*/)[0]
|
|
30
|
-
const trailing = raw.match(/\s*$/)[0]
|
|
29
|
+
const leading = raw.match(/^\s*/)[0]
|
|
30
|
+
const trailing = raw.match(/\s*$/)[0]
|
|
31
31
|
const text = raw
|
|
32
32
|
.slice(leading.length, raw.length - trailing.length)
|
|
33
|
-
.replace(/\n/g,
|
|
34
|
-
.replace(/\s{2,}/g,
|
|
35
|
-
return {leading, text, trailing}
|
|
33
|
+
.replace(/\n/g, ' ')
|
|
34
|
+
.replace(/\s{2,}/g, ' ')
|
|
35
|
+
return {leading, text, trailing}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -45,31 +45,31 @@ function splitQuasi(raw) {
|
|
|
45
45
|
* @returns {boolean}
|
|
46
46
|
*/
|
|
47
47
|
export function recordWtCall(path, processor, id) {
|
|
48
|
-
const {
|
|
48
|
+
const {callee, arguments: args} = path.node
|
|
49
49
|
|
|
50
50
|
// Only handle bare `wt(...)` identifiers — not obj.wt(...) etc.
|
|
51
|
-
if (callee.type !==
|
|
52
|
-
if (args.length !== 1) return false
|
|
51
|
+
if (callee.type !== 'Identifier' || callee.name !== 'wt') return false
|
|
52
|
+
if (args.length !== 1) return false
|
|
53
53
|
|
|
54
|
-
const arg = args[0]
|
|
55
|
-
let text = null
|
|
54
|
+
const arg = args[0]
|
|
55
|
+
let text = null
|
|
56
56
|
|
|
57
|
-
if (arg.type ===
|
|
58
|
-
text = arg.value
|
|
59
|
-
} else if (arg.type ===
|
|
60
|
-
text = arg.quasis[0].value.cooked ?? arg.quasis[0].value.raw
|
|
57
|
+
if (arg.type === 'StringLiteral') {
|
|
58
|
+
text = arg.value
|
|
59
|
+
} else if (arg.type === 'TemplateLiteral' && arg.expressions.length === 0) {
|
|
60
|
+
text = arg.quasis[0].value.cooked ?? arg.quasis[0].value.raw
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
if (text === null) return false
|
|
63
|
+
if (text === null) return false
|
|
64
64
|
|
|
65
65
|
const cleanText = text
|
|
66
66
|
.trim()
|
|
67
|
-
.replace(/\n/g,
|
|
68
|
-
.replace(/\s{2,}/g,
|
|
69
|
-
if (!cleanText) return false
|
|
67
|
+
.replace(/\n/g, ' ')
|
|
68
|
+
.replace(/\s{2,}/g, ' ')
|
|
69
|
+
if (!cleanText) return false
|
|
70
70
|
|
|
71
|
-
processor.record(cleanText, cleanText, id,
|
|
72
|
-
return true
|
|
71
|
+
processor.record(cleanText, cleanText, id, '(wt)')
|
|
72
|
+
return true
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -95,42 +95,42 @@ export function recordWtCall(path, processor, id) {
|
|
|
95
95
|
* @returns {boolean} `true` when the call was rewritten.
|
|
96
96
|
*/
|
|
97
97
|
export function transformWtTemplateLiteral(path, code, ms, processor, id) {
|
|
98
|
-
const {callee, arguments: args} = path.node
|
|
98
|
+
const {callee, arguments: args} = path.node
|
|
99
99
|
|
|
100
|
-
if (callee.type !==
|
|
101
|
-
if (args.length !== 1) return false
|
|
100
|
+
if (callee.type !== 'Identifier' || callee.name !== 'wt') return false
|
|
101
|
+
if (args.length !== 1) return false
|
|
102
102
|
|
|
103
|
-
const arg = args[0]
|
|
104
|
-
if (arg.type !==
|
|
105
|
-
return false
|
|
103
|
+
const arg = args[0]
|
|
104
|
+
if (arg.type !== 'TemplateLiteral' || arg.expressions.length === 0)
|
|
105
|
+
return false
|
|
106
106
|
|
|
107
|
-
const {quasis, expressions} = arg
|
|
108
|
-
const parts = [
|
|
109
|
-
let hasTranslatable = false
|
|
107
|
+
const {quasis, expressions} = arg
|
|
108
|
+
const parts = ['`']
|
|
109
|
+
let hasTranslatable = false
|
|
110
110
|
|
|
111
111
|
for (let i = 0; i < quasis.length; i++) {
|
|
112
|
-
const cooked = quasis[i].value.cooked ?? quasis[i].value.raw
|
|
113
|
-
const {leading, text, trailing} = splitQuasi(cooked)
|
|
112
|
+
const cooked = quasis[i].value.cooked ?? quasis[i].value.raw
|
|
113
|
+
const {leading, text, trailing} = splitQuasi(cooked)
|
|
114
114
|
|
|
115
115
|
if (text) {
|
|
116
|
-
processor.record(text, text, id,
|
|
117
|
-
parts.push(`${leading}\${wt(${JSON.stringify(text)})}${trailing}`)
|
|
118
|
-
hasTranslatable = true
|
|
116
|
+
processor.record(text, text, id, '(wt)')
|
|
117
|
+
parts.push(`${leading}\${wt(${JSON.stringify(text)})}${trailing}`)
|
|
118
|
+
hasTranslatable = true
|
|
119
119
|
} else {
|
|
120
120
|
// empty or whitespace-only quasi — preserve as literal template text
|
|
121
|
-
parts.push(cooked)
|
|
121
|
+
parts.push(cooked)
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
if (i < expressions.length) {
|
|
125
|
-
const exprSrc = code.slice(expressions[i].start, expressions[i].end)
|
|
126
|
-
parts.push(`\${${exprSrc}}`)
|
|
125
|
+
const exprSrc = code.slice(expressions[i].start, expressions[i].end)
|
|
126
|
+
parts.push(`\${${exprSrc}}`)
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
parts.push(
|
|
130
|
+
parts.push('`')
|
|
131
131
|
|
|
132
|
-
if (!hasTranslatable) return false
|
|
132
|
+
if (!hasTranslatable) return false
|
|
133
133
|
|
|
134
|
-
ms.overwrite(path.node.start, path.node.end, parts.join(
|
|
135
|
-
return true
|
|
134
|
+
ms.overwrite(path.node.start, path.node.end, parts.join(''))
|
|
135
|
+
return true
|
|
136
136
|
}
|