@npm-questionpro/wick-ui-i18n 0.14.1 → 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 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 | 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>&amp;</WuButton>` | ❌ |
19
- | `<WuButton>Hello &amp; World</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" /> &amp; <WuTranslate __i18nKey="World" />` |
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>&amp;</WuButton>` | ❌ |
19
+ | `<WuButton>Hello &amp; World</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" /> &amp; <WuTranslate __i18nKey="World" />` |
20
20
 
21
21
  ## JSX string expressions
22
22
 
23
- | Input | Output |
24
- |---|---|
25
- | `<WuButton>{"Hello"}</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" />` |
26
- | `` <WuButton>{`Hello`}</WuButton> `` | ✅ `<WuTranslate __i18nKey="Hello" />` |
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 | 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" />}` |
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 | 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> `` | ❌ |
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 | Output |
50
- |---|---|
49
+ | Input | Output |
50
+ | ----------------------------------- | --------------------------------------------- |
51
51
  | `<WuButton>Hello {name}</WuButton>` | ✅ `<WuTranslate __i18nKey="Hello" /> {name}` |
52
- | `<WuButton>{a} and {b}</WuButton>` | ✅ `{a} <WuTranslate __i18nKey="and" /> {b}` |
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 | Output |
60
- |---|---|
61
- | `<WuField Label="First name" />` | ✅ `Label={wt("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?" />` | ✅ `title={wt("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 template literal with expressions.
72
+ Plugin records static args into `wick-ui-i18n.json`. No code rewrite unless
73
+ template literal with expressions.
73
74
 
74
- | Input | Dictionary | Code output |
75
- |---|---|---|
76
- | `wt("Hello")` | ✅ `"Hello"` | `wt("Hello")` |
77
- | `` wt(`Hello`) `` | ✅ `"Hello"` | `` wt(`Hello`) `` |
78
- | `` wt(`Hello ${name}`) `` | ✅ `"Hello"` | `` `${wt("Hello")} ${name}` `` |
79
- | `` wt(`Hello ${a} and ${b}`) `` | ✅ `"Hello"`, `"and"` | `` `${wt("Hello")} ${a} ${wt("and")} ${b}` `` |
80
- | `wt(variable)` | ❌ | — |
81
- | `` wt(`${a}${b}`) `` | ❌ | — |
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 | Dictionary |
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 | Default | Description |
98
- |---|---|---|
99
- | `components` | `[]` | Extra components treated like Wu* |
100
- | `ignoreComponents` | `[]` | Extra components never translated |
101
- | `translatableProps` | `['Label','placeholder','title','aria-label','aria-placeholder']` | Props rewritten to `wt()` |
102
- | `extractFromKeys` | `[]` | Object keys extracted into dictionary |
103
- | `excludeFiles` | — | Files skipped entirely |
104
- | `debug` | `false` | Log transforms to console |
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
@@ -1,5 +1,6 @@
1
1
  import type {Plugin} from 'vite'
2
2
 
3
+ // eslint-disable-next-line @typescript-eslint/naming-convention
3
4
  export interface WickI18nOptions {
4
5
  /** Extra component names to translate (in addition to Wu* components). */
5
6
  components?: string[]
package/index.js CHANGED
@@ -18,10 +18,10 @@
18
18
  * };
19
19
  */
20
20
 
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";
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: "wick-ui-i18n",
57
- enforce: "pre",
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("Wu") ||
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("Content-Type", "application/json");
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: "asset",
115
- fileName: "wick-ui-i18n.json",
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.14.1",
3
+ "version": "2.0.0-next.10",
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,35 @@
13
13
  }
14
14
  },
15
15
  "peerDependencies": {
16
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
16
+ "vite": "^8.0.15"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/babel__traverse": "^7.28.0",
20
- "vite": "^7.3.1",
21
- "vitest": "^3.2.4"
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
+ ],
37
+ "prettier": "@npm-questionpro/wick-ui-prettier-config",
28
38
  "scripts": {
29
- "test": "vitest run"
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "test:ci": "vitest run --coverage",
42
+ "format": "prettier --write .",
43
+ "format:ci": "prettier --check .",
44
+ "lint": "eslint . --fix",
45
+ "lint:ci": "eslint --max-warnings 0 ."
30
46
  }
31
47
  }
package/src/debug.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * after the bundle is generated.
5
5
  */
6
6
 
7
- import { basename } from "node:path";
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((p) => {
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(" > ") || "(root)";
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("\n[wick-i18n] No translations captured.\n");
46
- return;
45
+ console.log('\n[wick-i18n] No translations captured.\n')
46
+ return
47
47
  }
48
48
 
49
- const headers = ["Text", "File", "Component Tree"];
49
+ const headers = ['Text', 'File', 'Component Tree']
50
50
 
51
- const rows = entries.map((e) => [e.text, basename(e.file), e.componentTree]);
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((r) => r[i].length)),
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((w) => fill.repeat(w + 2)).join(m) + r;
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 = (cells) =>
66
- "" + cells.map((c, i) => ` ${pad(c, widths[i])} `).join("") + "";
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("\n"));
78
+ console.log(lines.join('\n'))
80
79
  }
package/src/processor.js CHANGED
@@ -4,23 +4,29 @@
4
4
  */
5
5
 
6
6
  /** Prop names translated by default on Wu* components. Pass `translatableProps` to override. */
7
- const DEFAULT_TRANSLATABLE_PROPS = ['Label', 'placeholder', 'title', 'aria-label', 'aria-placeholder']
7
+ const DEFAULT_TRANSLATABLE_PROPS = [
8
+ 'Label',
9
+ 'placeholder',
10
+ 'title',
11
+ 'aria-label',
12
+ 'aria-placeholder',
13
+ ]
8
14
 
9
15
  /** Components always excluded from translation regardless of user config. */
10
16
  const DEFAULT_IGNORE = [
11
- "WuIcon",
12
- "WuTranslateProvider",
13
- "WuHelpButton",
14
- "WuActivityLog",
15
- "WuAppHeader",
16
- "WuAPpHeadeMenu",
17
- "WuCopyToClipboard",
18
- "WuMenuIcon",
19
- "WuScrollArea",
20
- "WuDrawer",
21
- "WuLoader",
22
- "WuContentEditor",
23
- ];
17
+ 'WuIcon',
18
+ 'WuTranslateProvider',
19
+ 'WuHelpButton',
20
+ 'WuActivityLog',
21
+ 'WuAppHeader',
22
+ 'WuAPpHeadeMenu',
23
+ 'WuCopyToClipboard',
24
+ 'WuMenuIcon',
25
+ 'WuScrollArea',
26
+ 'WuDrawer',
27
+ 'WuLoader',
28
+ 'WuContentEditor',
29
+ ]
24
30
 
25
31
  export class TranslationProcessor {
26
32
  /**
@@ -32,21 +38,21 @@ export class TranslationProcessor {
32
38
  * @param {boolean} [options.debug] - Enable verbose logging.
33
39
  */
34
40
  constructor(options) {
35
- this.components = new Set(options.components);
41
+ this.components = new Set(options.components)
36
42
  this.ignoreComponents = new Set(
37
43
  DEFAULT_IGNORE.concat(options.ignoreComponents || []),
38
- );
44
+ )
39
45
  /** @type {Set<string>} JSX prop names that should be translated. */
40
46
  this.translatableProps = new Set(
41
47
  options.translatableProps ?? DEFAULT_TRANSLATABLE_PROPS,
42
- );
48
+ )
43
49
  /** @type {Set<string>} Object property key names whose string values are extracted (e.g. 'label'). */
44
- this.extractFromKeys = new Set(options.extractFromKeys || []);
50
+ this.extractFromKeys = new Set(options.extractFromKeys || [])
45
51
  /** @type {Map<string, string>} key → original text */
46
- this.dictionary = new Map();
52
+ this.dictionary = new Map()
47
53
  /** @type {import('./debug.js').DebugEntry[]} */
48
- this.entries = [];
49
- this.debugEnabled = options.debug || false;
54
+ this.entries = []
55
+ this.debugEnabled = options.debug || false
50
56
  }
51
57
 
52
58
  /**
@@ -54,7 +60,7 @@ export class TranslationProcessor {
54
60
  * @param {...unknown} args
55
61
  */
56
62
  log(...args) {
57
- if (this.debugEnabled) console.log("[wick-i18n]", ...args);
63
+ if (this.debugEnabled) console.log('[wick-i18n]', ...args)
58
64
  }
59
65
 
60
66
  /**
@@ -70,11 +76,11 @@ export class TranslationProcessor {
70
76
  if (this.dictionary.has(key) && this.dictionary.get(key) !== text) {
71
77
  console.warn(
72
78
  `[wick-i18n] Collision in ${file}\nKey: "${key}"\nNew: "${text}"`,
73
- );
74
- return;
79
+ )
80
+ return
75
81
  }
76
- this.dictionary.set(key, text);
77
- this.entries.push({ key, text, file, componentTree });
82
+ this.dictionary.set(key, text)
83
+ this.entries.push({key, text, file, componentTree})
78
84
  }
79
85
 
80
86
  /**
@@ -90,45 +96,41 @@ export class TranslationProcessor {
90
96
  * @returns {boolean}
91
97
  */
92
98
  shouldTranslate(path) {
93
- let isIgnored = false;
94
- let targetFound = false;
99
+ let isIgnored = false
100
+ let targetFound = false
95
101
 
96
- path.findParent((p) => {
97
- if (!p.isJSXElement()) return false;
102
+ path.findParent(p => {
103
+ if (!p.isJSXElement()) return false
98
104
 
99
105
  const name =
100
106
  p.node.openingElement.name.name ||
101
- p.node.openingElement.name.property?.name;
102
- const attrs = p.node.openingElement.attributes || [];
107
+ p.node.openingElement.name.property?.name
108
+ const attrs = p.node.openingElement.attributes || []
103
109
 
104
110
  if (
105
- attrs.some((a) =>
106
- ["data-skip", "data-i18n-skip"].includes(a.name?.name),
107
- )
111
+ attrs.some(a => ['data-skip', 'data-i18n-skip'].includes(a.name?.name))
108
112
  ) {
109
- isIgnored = true;
110
- return true;
113
+ isIgnored = true
114
+ return true
111
115
  }
112
116
 
113
117
  if (this.ignoreComponents.has(name)) {
114
- isIgnored = true;
115
- return true;
118
+ isIgnored = true
119
+ return true
116
120
  }
117
121
 
118
- const hasWrapper = attrs.some(
119
- (a) => a.name?.name === "data-i18n-wrapper",
120
- );
121
- const isTarget = this.components.has(name) || name?.startsWith("Wu");
122
+ const hasWrapper = attrs.some(a => a.name?.name === 'data-i18n-wrapper')
123
+ const isTarget = this.components.has(name) || name?.startsWith('Wu')
122
124
 
123
125
  if (hasWrapper || isTarget) {
124
- targetFound = true;
125
- return true;
126
+ targetFound = true
127
+ return true
126
128
  }
127
129
 
128
- return false;
129
- });
130
+ return false
131
+ })
130
132
 
131
- return targetFound && !isIgnored;
133
+ return targetFound && !isIgnored
132
134
  }
133
135
 
134
136
  /**
@@ -140,16 +142,17 @@ export class TranslationProcessor {
140
142
  * @returns {boolean}
141
143
  */
142
144
  shouldTranslateProp(propName, path) {
143
- if (!this.translatableProps.has(propName)) return false;
145
+ if (!this.translatableProps.has(propName)) return false
144
146
  // path.parent is the JSXOpeningElement node
145
- const openingEl = path.parent;
146
- const name = openingEl.name?.name || openingEl.name?.property?.name;
147
- if (!name) return false;
148
- if (this.ignoreComponents.has(name)) return false;
147
+ const openingEl = path.parent
148
+ const name = openingEl.name?.name || openingEl.name?.property?.name
149
+ if (!name) return false
150
+ if (this.ignoreComponents.has(name)) return false
149
151
  // Respect data-skip / data-i18n-skip on the same element
150
- const attrs = openingEl.attributes || [];
151
- if (attrs.some(a => ['data-skip', 'data-i18n-skip'].includes(a.name?.name))) return false;
152
- return name.startsWith('Wu') || this.components.has(name);
152
+ const attrs = openingEl.attributes || []
153
+ if (attrs.some(a => ['data-skip', 'data-i18n-skip'].includes(a.name?.name)))
154
+ return false
155
+ return name.startsWith('Wu') || this.components.has(name)
153
156
  }
154
157
 
155
158
  /**
@@ -160,26 +163,26 @@ export class TranslationProcessor {
160
163
  * @returns {string|null}
161
164
  */
162
165
  getExplicitKey(path) {
163
- let result = null;
164
- path.findParent((p) => {
165
- if (!p.isJSXElement()) return false;
166
+ let result = null
167
+ path.findParent(p => {
168
+ if (!p.isJSXElement()) return false
166
169
  const attr = p.node.openingElement.attributes.find(
167
- (a) => a.name?.name === "data-i18n-key",
168
- );
169
- if (!attr) return false;
170
+ a => a.name?.name === 'data-i18n-key',
171
+ )
172
+ if (!attr) return false
170
173
  const val =
171
- attr.value.type === "StringLiteral"
174
+ attr.value.type === 'StringLiteral'
172
175
  ? attr.value.value
173
- : attr.value.expression?.value;
176
+ : attr.value.expression?.value
174
177
  if (!val) {
175
178
  console.warn(
176
179
  `[wick-i18n] data-i18n-key on <${p.node.openingElement.name.name}> is dynamic or empty — falling back to text content.`,
177
- );
178
- return true;
180
+ )
181
+ return true
179
182
  }
180
- result = val;
181
- return true;
182
- });
183
- return result;
183
+ result = val
184
+ return true
185
+ })
186
+ return result
184
187
  }
185
188
  }