@npm-questionpro/wick-ui-i18n 2.13.0 → 2.14.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 +3 -3
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/processor.js +2 -2
- package/src/transform.js +37 -24
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# wick-ui-i18n
|
|
2
2
|
|
|
3
|
-
Vite plugin — wraps JSX text in Wu\* components with `<WuTranslate>`, rewrites translatable props to `{
|
|
3
|
+
Vite plugin — wraps JSX text in Wu\* components with `<WuTranslate>`, rewrites translatable props to `{wt("...")}`,
|
|
4
4
|
records `wt()` / `useWt()()` calls, and emits `wick-ui-i18n.json`.
|
|
5
5
|
|
|
6
6
|
> Full docs and examples: Storybook → **i18n/Overview**
|
|
@@ -22,7 +22,7 @@ export default defineConfig({plugins: [react(), wickI18n()]})
|
|
|
22
22
|
| Source | Output |
|
|
23
23
|
| ----------------------------------------- | --------------------------------------------- |
|
|
24
24
|
| `<WuButton>Save</WuButton>` | `<WuTranslate __i18nKey="Save" />` |
|
|
25
|
-
| `<WuInput Label="First name" />` | `Label={
|
|
25
|
+
| `<WuInput Label="First name" />` | `Label={wt("First name")}` |
|
|
26
26
|
| ``<WuButton>{`${n} results`}</WuButton>`` | `<><WuTranslate __i18nKey="results" />{n}</>` |
|
|
27
27
|
| `wt("Hello")` | recorded in dictionary |
|
|
28
28
|
| ``wt(`Hello ${name}`)`` | `` `${wt("Hello")} ${name}` `` |
|
|
@@ -37,7 +37,7 @@ export default defineConfig({plugins: [react(), wickI18n()]})
|
|
|
37
37
|
| ------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
38
38
|
| `components` | `[]` | Extra components treated like Wu\* |
|
|
39
39
|
| `ignoreComponents` | `[]` | Extra components never translated |
|
|
40
|
-
| `translatableProps` | `['Label','placeholder','title','aria-label','aria-placeholder']` | Props rewritten to `
|
|
40
|
+
| `translatableProps` | `['Label','placeholder','title','aria-label','aria-placeholder']` | Props rewritten to `wt()` |
|
|
41
41
|
| `dataLabelKeys` | `[]` | Property names whose values are labels in mixed data files |
|
|
42
42
|
| `dictionaryFiles` | — | Globs (project files via Vite) or exact paths (anywhere, incl. `node_modules` via `fs`) |
|
|
43
43
|
| `excludeFiles` | — | Files skipped entirely |
|
package/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface WickI18nOptions {
|
|
|
9
9
|
ignoreComponents?: string[]
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Extra JSX prop names rewritten to `{
|
|
12
|
+
* Extra JSX prop names rewritten to `{wt("...")}` on Wu* components.
|
|
13
13
|
* Appended to the defaults — defaults are always kept:
|
|
14
14
|
* `['Label', 'placeholder', 'title', 'aria-label', 'aria-placeholder']`.
|
|
15
15
|
*/
|
|
@@ -67,7 +67,7 @@ export interface WickI18nOptions {
|
|
|
67
67
|
* Vite plugin that automatically translates Wick UI components.
|
|
68
68
|
*
|
|
69
69
|
* - JSX text content inside Wu* → `<WuTranslate __i18nKey="..." />`
|
|
70
|
-
* - Translatable props (`Label`, `placeholder`, `title`, …) → `{
|
|
70
|
+
* - Translatable props (`Label`, `placeholder`, `title`, …) → `{wt("...")}`
|
|
71
71
|
* - `wt("literal")` and `useWt()("literal")` calls → keys recorded in dictionary
|
|
72
72
|
* - `wt(\`Hello ${name}\`)` and `useWt()(\`Hello ${name}\`)` → each static quasi extracted and rewritten
|
|
73
73
|
* - Emits `wick-ui-i18n.json` at build time / serves it at `GET /wick-ui-i18n.json` in dev
|
package/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {extractStringsFromFile} from './src/extractStrings.js'
|
|
|
31
31
|
* @typedef {object} WickI18nOptions
|
|
32
32
|
* @property {string[]} [components] - Extra component names that trigger translation.
|
|
33
33
|
* @property {string[]} [ignoreComponents] - Component names to exclude from translation.
|
|
34
|
-
* @property {string[]} [translatableProps] - JSX prop names rewritten to `{
|
|
34
|
+
* @property {string[]} [translatableProps] - JSX prop names rewritten to `{wt("...")}`. Defaults to `['Label','placeholder','title','aria-label','aria-placeholder']`.
|
|
35
35
|
* @property {string[]} [dataLabelKeys] - Object property names whose string values are labels in mixed data
|
|
36
36
|
* files (e.g. `['label', 'title']` in nav config arrays). No code rewrite;
|
|
37
37
|
* keys are recorded into `wick-ui-i18n.json` for the translation API.
|
package/package.json
CHANGED
package/src/processor.js
CHANGED
|
@@ -27,7 +27,7 @@ export class TranslationProcessor {
|
|
|
27
27
|
* @param {object} options
|
|
28
28
|
* @param {string[]} options.components - Component names that trigger translation.
|
|
29
29
|
* @param {string[]} [options.ignoreComponents] - Extra components to exclude.
|
|
30
|
-
* @param {string[]} [options.translatableProps] - Extra JSX prop names to rewrite to
|
|
30
|
+
* @param {string[]} [options.translatableProps] - Extra JSX prop names to rewrite to wt(). Appended to defaults.
|
|
31
31
|
* @param {string[]} [options.dataLabelKeys] - Object property names whose string values are labels in mixed
|
|
32
32
|
* data files (e.g. ['label', 'title'] in nav config arrays).
|
|
33
33
|
* @param {boolean} [options.debug] - Enable verbose logging.
|
|
@@ -121,7 +121,7 @@ export class TranslationProcessor {
|
|
|
121
121
|
/**
|
|
122
122
|
* Return `true` when `propName` is in the translatable-props set and the
|
|
123
123
|
* immediate parent JSX element is a Wu* component or in `components`, and
|
|
124
|
-
* is not in `ignoreComponents`. Matched props are rewritten to `{
|
|
124
|
+
* is not in `ignoreComponents`. Matched props are rewritten to `{wt("...")}` by the transform.
|
|
125
125
|
*
|
|
126
126
|
* @param {string} propName
|
|
127
127
|
* @param {import('@babel/traverse').NodePath} path - JSXAttribute path.
|
package/src/transform.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview AST transform — parses a JSX/TSX/TS file and:
|
|
3
3
|
* - Rewrites JSX text content inside Wu* components to `<WuTranslate>`
|
|
4
|
-
* - Rewrites translatable props (placeholder, title, …) to `{
|
|
4
|
+
* - Rewrites translatable props (placeholder, title, …) to `{wt("…")}`
|
|
5
5
|
* - Prepends the necessary imports when added.
|
|
6
6
|
* - Optionally records Wu* component usages into a TelemetryCollector.
|
|
7
7
|
*/
|
|
@@ -75,12 +75,12 @@ function handleCapture(path, text, start, end, ms, processor, id, skipExplicitKe
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* Transform a JSX prop value that is a TemplateLiteral into a
|
|
79
|
-
* (or a new template literal with
|
|
78
|
+
* Transform a JSX prop value that is a TemplateLiteral into a wt() call
|
|
79
|
+
* (or a new template literal with wt() for each static quasi).
|
|
80
80
|
*
|
|
81
|
-
* Static: Label={`hello`} → Label={
|
|
82
|
-
* Dynamic: Label={`${name} hello`} → Label={`${name} ${
|
|
83
|
-
* Dynamic: Label={`${a} and ${b}`} → Label={`${a} ${
|
|
81
|
+
* Static: Label={`hello`} → Label={wt("hello")}
|
|
82
|
+
* Dynamic: Label={`${name} hello`} → Label={`${name} ${wt("hello")}`}
|
|
83
|
+
* Dynamic: Label={`${a} and ${b}`} → Label={`${a} ${wt("and")} ${b}`}
|
|
84
84
|
*
|
|
85
85
|
* @param {import('@babel/traverse').NodePath} path - JSXAttribute path.
|
|
86
86
|
* @param {import('@babel/types').TemplateLiteral} expr
|
|
@@ -102,11 +102,11 @@ function transformPropTemplateLiteral(path, expr, code, ms, processor, id) {
|
|
|
102
102
|
if (!text || HTML_ENTITY_RE.test(text)) return false
|
|
103
103
|
processor.record(text, text, id, componentTree)
|
|
104
104
|
// Overwrite the entire JSXExpressionContainer
|
|
105
|
-
ms.overwrite(path.node.value.start, path.node.value.end, `{
|
|
105
|
+
ms.overwrite(path.node.value.start, path.node.value.end, `{wt(${JSON.stringify(text)})}`)
|
|
106
106
|
return true
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// Dynamic template literal: walk quasis, replace static parts with
|
|
109
|
+
// Dynamic template literal: walk quasis, replace static parts with wt()
|
|
110
110
|
const parts = ['`']
|
|
111
111
|
let hasTranslatable = false
|
|
112
112
|
|
|
@@ -116,7 +116,7 @@ function transformPropTemplateLiteral(path, expr, code, ms, processor, id) {
|
|
|
116
116
|
|
|
117
117
|
if (text && !HTML_ENTITY_RE.test(text)) {
|
|
118
118
|
processor.record(text, text, id, componentTree)
|
|
119
|
-
parts.push(`${leading}\${
|
|
119
|
+
parts.push(`${leading}\${wt(${JSON.stringify(text)})}${trailing}`)
|
|
120
120
|
hasTranslatable = true
|
|
121
121
|
} else {
|
|
122
122
|
parts.push(cooked)
|
|
@@ -157,8 +157,11 @@ export function transformFile(code, id, processor, {telemetry} = {}) {
|
|
|
157
157
|
const ms = new MagicString(code)
|
|
158
158
|
let needsImport = false
|
|
159
159
|
let hasImport = false
|
|
160
|
-
let
|
|
161
|
-
let
|
|
160
|
+
let needsWtImport = false
|
|
161
|
+
let hasWtImport = false
|
|
162
|
+
// Offset just before the closing `}` of an existing named import from wick-ui-lib,
|
|
163
|
+
// used to splice in extra names instead of prepending a second import statement.
|
|
164
|
+
let wickUiLibImportInsertPos = null
|
|
162
165
|
// Local names of components actually imported from @npm-questionpro/* — telemetry only
|
|
163
166
|
// counts these, so a same-named Wu* component from an unrelated package (e.g. a legacy
|
|
164
167
|
// internal UI lib) never pollutes the @npm-questionpro/wick-ui-* usage stats.
|
|
@@ -178,9 +181,13 @@ export function transformFile(code, id, processor, {telemetry} = {}) {
|
|
|
178
181
|
/** Track whether WuTranslate / wt are already imported so we don't duplicate them. */
|
|
179
182
|
ImportDeclaration(path) {
|
|
180
183
|
if (path.node.source.value.includes('wick-ui-lib')) {
|
|
184
|
+
const namedSpecifiers = path.node.specifiers.filter(s => s.type === 'ImportSpecifier')
|
|
185
|
+
if (namedSpecifiers.length) {
|
|
186
|
+
wickUiLibImportInsertPos = namedSpecifiers[namedSpecifiers.length - 1].end
|
|
187
|
+
}
|
|
181
188
|
for (const s of path.node.specifiers) {
|
|
182
189
|
if (s.imported?.name === 'WuTranslate') hasImport = true
|
|
183
|
-
if (s.imported?.name === '
|
|
190
|
+
if (s.imported?.name === 'wt') hasWtImport = true
|
|
184
191
|
}
|
|
185
192
|
}
|
|
186
193
|
if (path.node.source.value.startsWith('@npm-questionpro/')) {
|
|
@@ -210,12 +217,12 @@ export function transformFile(code, id, processor, {telemetry} = {}) {
|
|
|
210
217
|
|
|
211
218
|
/**
|
|
212
219
|
* Translatable props (Label, placeholder, title, aria-label, ...)
|
|
213
|
-
* rewritten to `{
|
|
220
|
+
* rewritten to `{wt("foo")}` on Wu* components.
|
|
214
221
|
*
|
|
215
222
|
* Handles all three value shapes:
|
|
216
|
-
* Label="hello" → Label={
|
|
217
|
-
* Label={`hello`} → Label={
|
|
218
|
-
* Label={`${name} hello`} → Label={`${name} ${
|
|
223
|
+
* Label="hello" → Label={wt("hello")}
|
|
224
|
+
* Label={`hello`} → Label={wt("hello")}
|
|
225
|
+
* Label={`${name} hello`} → Label={`${name} ${wt("hello")}`}
|
|
219
226
|
*/
|
|
220
227
|
JSXAttribute(path) {
|
|
221
228
|
const propName = path.node.name.name
|
|
@@ -231,8 +238,8 @@ export function transformFile(code, id, processor, {telemetry} = {}) {
|
|
|
231
238
|
if (!text) return
|
|
232
239
|
if (!processor.shouldTranslateProp(propName, path)) return
|
|
233
240
|
processor.record(text, text, id, getComponentTree(path))
|
|
234
|
-
ms.overwrite(value.start, value.end, `{
|
|
235
|
-
|
|
241
|
+
ms.overwrite(value.start, value.end, `{wt(${JSON.stringify(text)})}`)
|
|
242
|
+
needsWtImport = true
|
|
236
243
|
return
|
|
237
244
|
}
|
|
238
245
|
|
|
@@ -240,7 +247,7 @@ export function transformFile(code, id, processor, {telemetry} = {}) {
|
|
|
240
247
|
// Label={`hello`} or Label={`${name} hello`}
|
|
241
248
|
if (value.type === 'JSXExpressionContainer' && value.expression.type === 'TemplateLiteral') {
|
|
242
249
|
if (transformPropTemplateLiteral(path, value.expression, code, ms, processor, id)) {
|
|
243
|
-
|
|
250
|
+
needsWtImport = true
|
|
244
251
|
}
|
|
245
252
|
}
|
|
246
253
|
},
|
|
@@ -315,12 +322,18 @@ export function transformFile(code, id, processor, {telemetry} = {}) {
|
|
|
315
322
|
|
|
316
323
|
if (!ms.hasChanged()) return null
|
|
317
324
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
325
|
+
// Merge into one statement — either spliced into an existing wick-ui-lib import or a
|
|
326
|
+
// single new prepended import — never two separate `import ... from 'wick-ui-lib'` lines.
|
|
327
|
+
const namesToImport = []
|
|
328
|
+
if (needsWtImport && !hasWtImport) namesToImport.push('wt')
|
|
329
|
+
if (needsImport && !hasImport) namesToImport.push('WuTranslate')
|
|
321
330
|
|
|
322
|
-
if (
|
|
323
|
-
|
|
331
|
+
if (namesToImport.length) {
|
|
332
|
+
if (wickUiLibImportInsertPos !== null) {
|
|
333
|
+
ms.appendLeft(wickUiLibImportInsertPos, `, ${namesToImport.join(', ')}`)
|
|
334
|
+
} else {
|
|
335
|
+
ms.prepend(`import { ${namesToImport.join(', ')} } from '@npm-questionpro/wick-ui-lib';\n`)
|
|
336
|
+
}
|
|
324
337
|
}
|
|
325
338
|
|
|
326
339
|
return {code: ms.toString(), map: ms.generateMap({hires: true})}
|