@npm-questionpro/wick-ui-i18n 0.14.0 → 2.0.0-next.4
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 +23 -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/README.md
CHANGED
|
@@ -7,98 +7,99 @@ translatable props to `{wt("...")}`, and emits `wick-ui-i18n.json`.
|
|
|
7
7
|
|
|
8
8
|
## JSX text
|
|
9
9
|
|
|
10
|
-
| Input
|
|
11
|
-
|
|
12
|
-
| `<WuButton>Hello</WuButton>`
|
|
13
|
-
| `<WuIcon>star</WuIcon>`
|
|
14
|
-
| `<div>Hello</div>`
|
|
15
|
-
| `<WuButton data-skip>Hello</WuButton>`
|
|
16
|
-
| `<span data-i18n-wrapper>Hello</span>`
|
|
17
|
-
| `<WuButton data-i18n-key="k">Hello</WuButton>` | ✅ `<WuTranslate __i18nKey="k" />`
|
|
18
|
-
| `<WuButton>&</WuButton>`
|
|
19
|
-
| `<WuButton>Hello & World</WuButton>`
|
|
10
|
+
| Input | Output |
|
|
11
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
12
|
+
| `<WuButton>Hello</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" />` |
|
|
13
|
+
| `<WuIcon>star</WuIcon>` | ❌ |
|
|
14
|
+
| `<div>Hello</div>` | ❌ |
|
|
15
|
+
| `<WuButton data-skip>Hello</WuButton>` | ❌ |
|
|
16
|
+
| `<span data-i18n-wrapper>Hello</span>` | ✅ `<WuTranslate __i18nKey="Hello" />` |
|
|
17
|
+
| `<WuButton data-i18n-key="k">Hello</WuButton>` | ✅ `<WuTranslate __i18nKey="k" />` |
|
|
18
|
+
| `<WuButton>&</WuButton>` | ❌ |
|
|
19
|
+
| `<WuButton>Hello & World</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" /> & <WuTranslate __i18nKey="World" />` |
|
|
20
20
|
|
|
21
21
|
## JSX string expressions
|
|
22
22
|
|
|
23
|
-
| Input
|
|
24
|
-
|
|
25
|
-
| `<WuButton>{"Hello"}</WuButton>`
|
|
26
|
-
|
|
|
27
|
-
| `<WuButton>{variable}</WuButton>`
|
|
23
|
+
| Input | Output |
|
|
24
|
+
| ---------------------------------- | -------------------------------------- |
|
|
25
|
+
| `<WuButton>{"Hello"}</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" />` |
|
|
26
|
+
| ``<WuButton>{`Hello`}</WuButton>`` | ✅ `<WuTranslate __i18nKey="Hello" />` |
|
|
27
|
+
| `<WuButton>{variable}</WuButton>` | ❌ |
|
|
28
28
|
|
|
29
29
|
## JSX ternaries
|
|
30
30
|
|
|
31
|
-
| Input
|
|
32
|
-
|
|
33
|
-
| `<WuButton>{flag ? "Yes" : "No"}</WuButton>`
|
|
34
|
-
| `<WuButton>{flag ? "Yes" : variable}</WuButton>`
|
|
35
|
-
| `<WuButton>{flag ? variable : variable}</WuButton>` | ❌
|
|
36
|
-
| `<WuButton>{a ? "A" : b ? "B" : "C"}</WuButton>`
|
|
31
|
+
| Input | Output |
|
|
32
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
33
|
+
| `<WuButton>{flag ? "Yes" : "No"}</WuButton>` | ✅ `{flag ? <WuTranslate __i18nKey="Yes" /> : <WuTranslate __i18nKey="No" />}` |
|
|
34
|
+
| `<WuButton>{flag ? "Yes" : variable}</WuButton>` | ✅ `{flag ? <WuTranslate __i18nKey="Yes" /> : variable}` |
|
|
35
|
+
| `<WuButton>{flag ? variable : variable}</WuButton>` | ❌ |
|
|
36
|
+
| `<WuButton>{a ? "A" : b ? "B" : "C"}</WuButton>` | ✅ `{a ? <WuTranslate __i18nKey="A" /> : b ? <WuTranslate __i18nKey="B" /> : <WuTranslate __i18nKey="C" />}` |
|
|
37
37
|
|
|
38
38
|
## JSX template literals with expressions
|
|
39
39
|
|
|
40
|
-
| Input
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
40
|
+
| Input | Output |
|
|
41
|
+
| ------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
|
42
|
+
| ``<WuButton>{`Hello ${name}`}</WuButton>`` | ✅ `<><WuTranslate __i18nKey="Hello" /> {name}</>` |
|
|
43
|
+
| ``<WuButton>{`${name} world`}</WuButton>`` | ✅ `<>{name} <WuTranslate __i18nKey="world" /></>` |
|
|
44
|
+
| ``<WuButton>{`Hello ${a} and ${b}`}</WuButton>`` | ✅ `<><WuTranslate __i18nKey="Hello" /> {a} <WuTranslate __i18nKey="and" /> {b}</>` |
|
|
45
|
+
| ``<WuButton>{`${a}${b}`}</WuButton>`` | ❌ |
|
|
46
46
|
|
|
47
47
|
## JSX mixed children
|
|
48
48
|
|
|
49
|
-
| Input
|
|
50
|
-
|
|
49
|
+
| Input | Output |
|
|
50
|
+
| ----------------------------------- | --------------------------------------------- |
|
|
51
51
|
| `<WuButton>Hello {name}</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" /> {name}` |
|
|
52
|
-
| `<WuButton>{a} and {b}</WuButton>`
|
|
53
|
-
| `<WuButton>{a} {b}</WuButton>`
|
|
52
|
+
| `<WuButton>{a} and {b}</WuButton>` | ✅ `{a} <WuTranslate __i18nKey="and" /> {b}` |
|
|
53
|
+
| `<WuButton>{a} {b}</WuButton>` | ❌ |
|
|
54
54
|
|
|
55
55
|
## JSX props
|
|
56
56
|
|
|
57
57
|
Defaults: `Label`, `placeholder`, `title`, `aria-label`, `aria-placeholder`.
|
|
58
58
|
|
|
59
|
-
| Input
|
|
60
|
-
|
|
61
|
-
| `<WuField Label="First name" />`
|
|
59
|
+
| Input | Output |
|
|
60
|
+
| -------------------------------------- | ----------------------------------- |
|
|
61
|
+
| `<WuField Label="First name" />` | ✅ `Label={wt("First name")}` |
|
|
62
62
|
| `<WuInput placeholder="Enter name" />` | ✅ `placeholder={wt("Enter name")}` |
|
|
63
|
-
| `<WuDialog title="Confirm?" />`
|
|
64
|
-
| `<WuField Label={variable} />`
|
|
65
|
-
| `<WuField Label="" />`
|
|
66
|
-
| `<WuIcon Label="x" />`
|
|
67
|
-
| `<input placeholder="x" />`
|
|
68
|
-
| `<WuField data-skip Label="x" />`
|
|
63
|
+
| `<WuDialog title="Confirm?" />` | ✅ `title={wt("Confirm?")}` |
|
|
64
|
+
| `<WuField Label={variable} />` | ❌ |
|
|
65
|
+
| `<WuField Label="" />` | ❌ |
|
|
66
|
+
| `<WuIcon Label="x" />` | ❌ |
|
|
67
|
+
| `<input placeholder="x" />` | ❌ |
|
|
68
|
+
| `<WuField data-skip Label="x" />` | ❌ |
|
|
69
69
|
|
|
70
70
|
## `wt()` calls
|
|
71
71
|
|
|
72
|
-
Plugin records static args into `wick-ui-i18n.json`. No code rewrite unless
|
|
72
|
+
Plugin records static args into `wick-ui-i18n.json`. No code rewrite unless
|
|
73
|
+
template literal with expressions.
|
|
73
74
|
|
|
74
|
-
| Input
|
|
75
|
-
|
|
76
|
-
| `wt("Hello")`
|
|
77
|
-
| ``
|
|
78
|
-
| ``
|
|
79
|
-
| ``
|
|
80
|
-
| `wt(variable)`
|
|
81
|
-
| ``
|
|
75
|
+
| Input | Dictionary | Code output |
|
|
76
|
+
| ----------------------------- | --------------------- | --------------------------------------------- |
|
|
77
|
+
| `wt("Hello")` | ✅ `"Hello"` | `wt("Hello")` |
|
|
78
|
+
| ``wt(`Hello`)`` | ✅ `"Hello"` | ``wt(`Hello`)`` |
|
|
79
|
+
| ``wt(`Hello ${name}`)`` | ✅ `"Hello"` | `` `${wt("Hello")} ${name}` `` |
|
|
80
|
+
| ``wt(`Hello ${a} and ${b}`)`` | ✅ `"Hello"`, `"and"` | `` `${wt("Hello")} ${a} ${wt("and")} ${b}` `` |
|
|
81
|
+
| `wt(variable)` | ❌ | — |
|
|
82
|
+
| ``wt(`${a}${b}`)`` | ❌ | — |
|
|
82
83
|
|
|
83
84
|
## Data files (`extractFromKeys` option)
|
|
84
85
|
|
|
85
86
|
No code rewrite — keys only recorded in `wick-ui-i18n.json`.
|
|
86
87
|
|
|
87
|
-
| Input
|
|
88
|
-
|
|
88
|
+
| Input | Dictionary |
|
|
89
|
+
| ------------------------------------------------------- | ---------------- |
|
|
89
90
|
| `{ label: 'Analytics' }` + `extractFromKeys: ['label']` | ✅ `"Analytics"` |
|
|
90
|
-
| `{ label: variable }`
|
|
91
|
-
| `{ label: '' }`
|
|
91
|
+
| `{ label: variable }` | ❌ |
|
|
92
|
+
| `{ label: '' }` | ❌ |
|
|
92
93
|
|
|
93
94
|
---
|
|
94
95
|
|
|
95
96
|
## Options
|
|
96
97
|
|
|
97
|
-
| Option
|
|
98
|
-
|
|
99
|
-
| `components`
|
|
100
|
-
| `ignoreComponents`
|
|
101
|
-
| `translatableProps` | `['Label','placeholder','title','aria-label','aria-placeholder']` | Props rewritten to `wt()`
|
|
102
|
-
| `extractFromKeys`
|
|
103
|
-
| `excludeFiles`
|
|
104
|
-
| `debug`
|
|
98
|
+
| Option | Default | Description |
|
|
99
|
+
| ------------------- | ----------------------------------------------------------------- | ------------------------------------- |
|
|
100
|
+
| `components` | `[]` | Extra components treated like Wu\* |
|
|
101
|
+
| `ignoreComponents` | `[]` | Extra components never translated |
|
|
102
|
+
| `translatableProps` | `['Label','placeholder','title','aria-label','aria-placeholder']` | Props rewritten to `wt()` |
|
|
103
|
+
| `extractFromKeys` | `[]` | Object keys extracted into dictionary |
|
|
104
|
+
| `excludeFiles` | — | Files skipped entirely |
|
|
105
|
+
| `debug` | `false` | Log transforms to console |
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* };
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
21
|
+
import {createFilter} from 'vite'
|
|
22
|
+
import {TranslationProcessor} from './src/processor.js'
|
|
23
|
+
import {transformFile} from './src/transform.js'
|
|
24
|
+
import {printReport} from './src/debug.js'
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* @typedef {object} WickI18nOptions
|
|
@@ -46,15 +46,15 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
46
46
|
translatableProps: options.translatableProps,
|
|
47
47
|
extractFromKeys: options.extractFromKeys,
|
|
48
48
|
debug: options.debug,
|
|
49
|
-
})
|
|
49
|
+
})
|
|
50
50
|
|
|
51
|
-
const filter = createFilter([/\.(jsx|tsx|ts)$/], options.excludeFiles)
|
|
51
|
+
const filter = createFilter([/\.(jsx|tsx|ts)$/], options.excludeFiles)
|
|
52
52
|
|
|
53
|
-
let base =
|
|
53
|
+
let base = '/'
|
|
54
54
|
|
|
55
55
|
return {
|
|
56
|
-
name:
|
|
57
|
-
enforce:
|
|
56
|
+
name: 'wick-ui-i18n',
|
|
57
|
+
enforce: 'pre',
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Capture the resolved base so the dev-server middleware path matches
|
|
@@ -63,7 +63,7 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
63
63
|
* @param {import('vite').ResolvedConfig} resolvedConfig
|
|
64
64
|
*/
|
|
65
65
|
configResolved(resolvedConfig) {
|
|
66
|
-
base = resolvedConfig.base
|
|
66
|
+
base = resolvedConfig.base
|
|
67
67
|
},
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -71,8 +71,8 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
71
71
|
* watch-mode rebuilds.
|
|
72
72
|
*/
|
|
73
73
|
buildStart() {
|
|
74
|
-
processor.dictionary.clear()
|
|
75
|
-
processor.entries = []
|
|
74
|
+
processor.dictionary.clear()
|
|
75
|
+
processor.entries = []
|
|
76
76
|
},
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -84,14 +84,14 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
84
84
|
*/
|
|
85
85
|
transform(code, id) {
|
|
86
86
|
const hasAnyTarget =
|
|
87
|
-
code.includes(
|
|
87
|
+
code.includes('Wu') ||
|
|
88
88
|
/\bwt\(/.test(code) ||
|
|
89
89
|
(processor.components.size > 0 &&
|
|
90
90
|
[...processor.components].some(c => code.includes(c))) ||
|
|
91
91
|
(processor.extractFromKeys.size > 0 &&
|
|
92
|
-
[...processor.extractFromKeys].some(k => code.includes(k)))
|
|
93
|
-
if (!filter(id) || !hasAnyTarget) return null
|
|
94
|
-
return transformFile(code, id, processor)
|
|
92
|
+
[...processor.extractFromKeys].some(k => code.includes(k)))
|
|
93
|
+
if (!filter(id) || !hasAnyTarget) return null
|
|
94
|
+
return transformFile(code, id, processor)
|
|
95
95
|
},
|
|
96
96
|
|
|
97
97
|
/**
|
|
@@ -101,26 +101,26 @@ export default function wickuiI18nPlugin(options = {}) {
|
|
|
101
101
|
*/
|
|
102
102
|
configureServer(server) {
|
|
103
103
|
server.middlewares.use(`${base}wick-ui-i18n.json`, (_req, res) => {
|
|
104
|
-
res.setHeader(
|
|
104
|
+
res.setHeader('Content-Type', 'application/json')
|
|
105
105
|
res.end(
|
|
106
106
|
JSON.stringify(Object.fromEntries(processor.dictionary), null, 2),
|
|
107
|
-
)
|
|
108
|
-
})
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
109
|
},
|
|
110
110
|
|
|
111
111
|
/** Emit the translation dictionary as a build asset and print debug table. */
|
|
112
112
|
generateBundle() {
|
|
113
113
|
this.emitFile({
|
|
114
|
-
type:
|
|
115
|
-
fileName:
|
|
114
|
+
type: 'asset',
|
|
115
|
+
fileName: 'wick-ui-i18n.json',
|
|
116
116
|
source: JSON.stringify(
|
|
117
117
|
Object.fromEntries(processor.dictionary),
|
|
118
118
|
null,
|
|
119
119
|
2,
|
|
120
120
|
),
|
|
121
|
-
})
|
|
121
|
+
})
|
|
122
122
|
|
|
123
|
-
printReport(processor.entries)
|
|
123
|
+
printReport(processor.entries)
|
|
124
124
|
},
|
|
125
|
-
}
|
|
125
|
+
}
|
|
126
126
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npm-questionpro/wick-ui-i18n",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0-next.4",
|
|
4
|
+
"private": false,
|
|
4
5
|
"license": "ISC",
|
|
5
6
|
"description": "Auto-translation AST wrapper for Wick UI",
|
|
6
7
|
"type": "module",
|
|
7
|
-
"main": "index.js",
|
|
8
8
|
"types": "index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
@@ -13,19 +13,36 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"vite": "^
|
|
16
|
+
"vite": "^8.0.15"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/babel__traverse": "^7.28.0",
|
|
20
|
-
"vite": "^
|
|
21
|
-
"vitest": "^
|
|
20
|
+
"vite": "^8.0.15",
|
|
21
|
+
"vitest": "^4.1.8",
|
|
22
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
23
|
+
"eslint": "^10.4.1",
|
|
24
|
+
"@npm-questionpro/wick-ui-eslint-config": "1.0.0",
|
|
25
|
+
"@wick-ui/tsconfig": "1.0.0"
|
|
22
26
|
},
|
|
23
27
|
"dependencies": {
|
|
24
28
|
"@babel/parser": "^7.29.0",
|
|
25
29
|
"@babel/traverse": "^7.29.0",
|
|
26
30
|
"magic-string": "^0.30.21"
|
|
27
31
|
},
|
|
32
|
+
"files": [
|
|
33
|
+
"index.js",
|
|
34
|
+
"index.d.ts",
|
|
35
|
+
"src",
|
|
36
|
+
"llms.txt"
|
|
37
|
+
],
|
|
38
|
+
"prettier": "@npm-questionpro/wick-ui-prettier-config",
|
|
28
39
|
"scripts": {
|
|
29
|
-
"test": "vitest run"
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"test:ci": "vitest run --coverage",
|
|
43
|
+
"format": "prettier --write .",
|
|
44
|
+
"format:ci": "prettier --check .",
|
|
45
|
+
"lint": "eslint . --fix",
|
|
46
|
+
"lint:ci": "eslint --max-warnings 0 ."
|
|
30
47
|
}
|
|
31
48
|
}
|
package/src/debug.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* after the bundle is generated.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {basename} from 'node:path'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Walk up the Babel path and collect JSX element names from outermost inward.
|
|
@@ -13,17 +13,17 @@ import { basename } from "node:path";
|
|
|
13
13
|
* @returns {string} e.g. `"WuProvider > div > WuButton"`
|
|
14
14
|
*/
|
|
15
15
|
export function getComponentTree(path) {
|
|
16
|
-
const parts = []
|
|
17
|
-
path.findParent(
|
|
16
|
+
const parts = []
|
|
17
|
+
path.findParent(p => {
|
|
18
18
|
if (p.isJSXElement()) {
|
|
19
19
|
const name =
|
|
20
20
|
p.node.openingElement.name.name ||
|
|
21
|
-
p.node.openingElement.name.property?.name
|
|
22
|
-
if (name) parts.unshift(name)
|
|
21
|
+
p.node.openingElement.name.property?.name
|
|
22
|
+
if (name) parts.unshift(name)
|
|
23
23
|
}
|
|
24
|
-
return false
|
|
25
|
-
})
|
|
26
|
-
return parts.join(
|
|
24
|
+
return false
|
|
25
|
+
})
|
|
26
|
+
return parts.join(' > ') || '(root)'
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -42,39 +42,38 @@ export function getComponentTree(path) {
|
|
|
42
42
|
*/
|
|
43
43
|
export function printReport(entries) {
|
|
44
44
|
if (!entries.length) {
|
|
45
|
-
console.log(
|
|
46
|
-
return
|
|
45
|
+
console.log('\n[wick-i18n] No translations captured.\n')
|
|
46
|
+
return
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const headers = [
|
|
49
|
+
const headers = ['Text', 'File', 'Component Tree']
|
|
50
50
|
|
|
51
|
-
const rows = entries.map(
|
|
51
|
+
const rows = entries.map(e => [e.text, basename(e.file), e.componentTree])
|
|
52
52
|
|
|
53
|
-
const cols = headers.length;
|
|
54
53
|
const widths = headers.map((h, i) =>
|
|
55
|
-
Math.max(h.length, ...rows.map(
|
|
56
|
-
)
|
|
54
|
+
Math.max(h.length, ...rows.map(r => r[i].length)),
|
|
55
|
+
)
|
|
57
56
|
|
|
58
|
-
const pad = (str, w) => str.padEnd(w)
|
|
57
|
+
const pad = (str, w) => str.padEnd(w)
|
|
59
58
|
const sep = (l, m, r, fill) =>
|
|
60
|
-
l + widths.map(
|
|
59
|
+
l + widths.map(w => fill.repeat(w + 2)).join(m) + r
|
|
61
60
|
|
|
62
|
-
const top = sep(
|
|
63
|
-
const mid = sep(
|
|
64
|
-
const bottom = sep(
|
|
65
|
-
const row =
|
|
66
|
-
|
|
61
|
+
const top = sep('┌', '┬', '┐', '─')
|
|
62
|
+
const mid = sep('├', '┼', '┤', '─')
|
|
63
|
+
const bottom = sep('└', '┴', '┘', '─')
|
|
64
|
+
const row = cells =>
|
|
65
|
+
'│' + cells.map((c, i) => ` ${pad(c, widths[i])} `).join('│') + '│'
|
|
67
66
|
|
|
68
67
|
const lines = [
|
|
69
|
-
|
|
68
|
+
'',
|
|
70
69
|
`[wick-i18n] Build report — ${entries.length} translation(s) captured`,
|
|
71
70
|
top,
|
|
72
71
|
row(headers),
|
|
73
72
|
mid,
|
|
74
73
|
...rows.map(row),
|
|
75
74
|
bottom,
|
|
76
|
-
|
|
77
|
-
]
|
|
75
|
+
'',
|
|
76
|
+
]
|
|
78
77
|
|
|
79
|
-
console.log(lines.join(
|
|
78
|
+
console.log(lines.join('\n'))
|
|
80
79
|
}
|