vue-i18n-extract-plugin 1.0.50 → 1.0.52
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 +17 -0
- package/lib/visitors.js +114 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,6 +98,18 @@ export default {
|
|
|
98
98
|
}),
|
|
99
99
|
...
|
|
100
100
|
};
|
|
101
|
+
|
|
102
|
+
// ts支持
|
|
103
|
+
import { defineConfig } from "vite-i18n-extract-plugin";
|
|
104
|
+
|
|
105
|
+
export default defineConfig({
|
|
106
|
+
rewrite: false,
|
|
107
|
+
translator: new YoudaoTranslator({
|
|
108
|
+
appId: "youdao appId",
|
|
109
|
+
appKey: "youdao appKey"
|
|
110
|
+
}),
|
|
111
|
+
...
|
|
112
|
+
});
|
|
101
113
|
```
|
|
102
114
|
|
|
103
115
|
## Vite plugin
|
|
@@ -145,6 +157,8 @@ import enMessages from "@/locales/en.json";
|
|
|
145
157
|
|
|
146
158
|
const i18n = createI18n({
|
|
147
159
|
legacy: false,
|
|
160
|
+
globalInjection: true,
|
|
161
|
+
allowComposition: true,
|
|
148
162
|
fallbackLocale: "en",
|
|
149
163
|
locale: "zh",
|
|
150
164
|
messages: {
|
|
@@ -156,6 +170,9 @@ const i18n = createI18n({
|
|
|
156
170
|
// 导出一个$t方法
|
|
157
171
|
export const $t = i18n.global.t.bind(i18n.global);
|
|
158
172
|
|
|
173
|
+
// 建议在全局也挂载一个$t方法做兜底
|
|
174
|
+
globalThis.$t = $t;
|
|
175
|
+
|
|
159
176
|
export default i18n;
|
|
160
177
|
```
|
|
161
178
|
|
package/lib/visitors.js
CHANGED
|
@@ -15,6 +15,84 @@ function isTFunction(node, option) {
|
|
|
15
15
|
);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function isVNodeCall(path, nodeName) {
|
|
19
|
+
const node = path.node;
|
|
20
|
+
if (!path.isCallExpression()) return false;
|
|
21
|
+
|
|
22
|
+
const callee = node.callee;
|
|
23
|
+
return (
|
|
24
|
+
(t.isIdentifier(callee) && callee.name === nodeName) ||
|
|
25
|
+
(t.isMemberExpression(callee) &&
|
|
26
|
+
t.isIdentifier(callee.property) &&
|
|
27
|
+
callee.property.name === nodeName)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getPropKey(propNode) {
|
|
32
|
+
if (t.isIdentifier(propNode.key)) {
|
|
33
|
+
return propNode.key.name;
|
|
34
|
+
}
|
|
35
|
+
if (t.isStringLiteral(propNode.key)) {
|
|
36
|
+
return propNode.key.value; // .replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function transformDirectiveIfNeeded(path, parentPath) {
|
|
42
|
+
let hasCreateVNode = false;
|
|
43
|
+
// 属性值情况,如 title: "xxx"
|
|
44
|
+
if (parentPath.isObjectProperty()) {
|
|
45
|
+
const propKey = getPropKey(parentPath.node);
|
|
46
|
+
|
|
47
|
+
if (!propKey) return;
|
|
48
|
+
|
|
49
|
+
const vNodeCall = path.findParent(
|
|
50
|
+
p =>
|
|
51
|
+
p.isCallExpression() &&
|
|
52
|
+
(t.isIdentifier(p.node.callee, { name: "_createVNode" }) ||
|
|
53
|
+
(t.isMemberExpression(p.node.callee) &&
|
|
54
|
+
t.isIdentifier(p.node.callee.property, { name: "_createVNode" })))
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (vNodeCall) {
|
|
58
|
+
hasCreateVNode = true;
|
|
59
|
+
const args = vNodeCall.node.arguments;
|
|
60
|
+
// PatchFlag = 8 (PROPS), dynamicProps = ["title"]
|
|
61
|
+
const patchFlagIndex = 3;
|
|
62
|
+
const dynamicPropsIndex = 4;
|
|
63
|
+
|
|
64
|
+
// 确保第 3 个参数(children)存在,否则补 null
|
|
65
|
+
if (args.length === 2) {
|
|
66
|
+
args.push(t.nullLiteral()); // => 第三个参数
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 补齐参数数组长度到第 5 个参数(index: 4)
|
|
70
|
+
while (args.length <= dynamicPropsIndex) {
|
|
71
|
+
args.push(null);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 设置 patchFlag = 8
|
|
75
|
+
args[patchFlagIndex] = t.numericLiteral(8);
|
|
76
|
+
|
|
77
|
+
// 设置 dynamicProps = ["propKey"]
|
|
78
|
+
const existingDynamicProps = args[dynamicPropsIndex];
|
|
79
|
+
if (!existingDynamicProps || !t.isArrayExpression(existingDynamicProps)) {
|
|
80
|
+
args[dynamicPropsIndex] = t.arrayExpression([t.stringLiteral(propKey)]);
|
|
81
|
+
} else {
|
|
82
|
+
const existingKeys = new Set(
|
|
83
|
+
existingDynamicProps.elements
|
|
84
|
+
.filter(el => t.isStringLiteral(el))
|
|
85
|
+
.map(el => el.value)
|
|
86
|
+
);
|
|
87
|
+
if (!existingKeys.has(propKey)) {
|
|
88
|
+
existingDynamicProps.elements.push(t.stringLiteral(propKey));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return hasCreateVNode;
|
|
94
|
+
}
|
|
95
|
+
|
|
18
96
|
function shouldTransform(path) {
|
|
19
97
|
const parent = path.parentPath;
|
|
20
98
|
return !(
|
|
@@ -52,8 +130,6 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
52
130
|
return;
|
|
53
131
|
}
|
|
54
132
|
|
|
55
|
-
// console.log("CallExpression", path.node.arguments);
|
|
56
|
-
|
|
57
133
|
const hashed = generateId(keyText);
|
|
58
134
|
|
|
59
135
|
if (i18nMap) {
|
|
@@ -104,18 +180,50 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
104
180
|
return;
|
|
105
181
|
}
|
|
106
182
|
|
|
107
|
-
// console.log("StringLiteral", value);
|
|
108
|
-
|
|
109
183
|
const hashed = generateId(value);
|
|
110
184
|
|
|
111
185
|
if (i18nMap) {
|
|
112
186
|
i18nMap[hashed] = value;
|
|
113
187
|
}
|
|
114
188
|
|
|
115
|
-
|
|
116
|
-
|
|
189
|
+
// 生成 _ctx.$t("hashed")
|
|
190
|
+
let callExpression = t.callExpression(
|
|
191
|
+
t.memberExpression(
|
|
192
|
+
t.identifier("_ctx"),
|
|
193
|
+
t.identifier(option.translateKey)
|
|
194
|
+
),
|
|
117
195
|
[t.stringLiteral(hashed)]
|
|
118
196
|
);
|
|
197
|
+
|
|
198
|
+
// 判断是否createTextVNode或MemberExpression(如 Vue.createTextVNode)
|
|
199
|
+
if (isVNodeCall(parentPath, "_createTextVNode")) {
|
|
200
|
+
// 如果 createTextVNode 参数只有一个,则补充第二个参数 1
|
|
201
|
+
const args = parentPath.node.arguments;
|
|
202
|
+
if (args.length === 1) {
|
|
203
|
+
parentPath.node.arguments = [callExpression, t.numericLiteral(1)];
|
|
204
|
+
}
|
|
205
|
+
// 是否 vue.createElementVNode(...),且该 StringLiteral 是第三个参数
|
|
206
|
+
} else if (isVNodeCall(parentPath, "_createElementVNode")) {
|
|
207
|
+
const callExpr = parentPath.node;
|
|
208
|
+
const args = callExpr.arguments;
|
|
209
|
+
|
|
210
|
+
// 当前是第三个参数,且参数数量为3
|
|
211
|
+
const argIndex = args.findIndex(arg => arg === path.node);
|
|
212
|
+
if (argIndex === 2 && args.length === 3) {
|
|
213
|
+
args[2] = callExpression; // 替换第3个参数
|
|
214
|
+
args.push(t.numericLiteral(1)); // 添加第4个参数
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
const hasCreateVNode = transformDirectiveIfNeeded(path, parentPath);
|
|
219
|
+
if (!hasCreateVNode) {
|
|
220
|
+
// 生成 $t("hashed")
|
|
221
|
+
callExpression = t.callExpression(t.identifier(option.translateKey), [
|
|
222
|
+
t.stringLiteral(hashed)
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
119
227
|
// 如果是 JSX 属性值,需要包裹在 JSXExpressionContainer 中
|
|
120
228
|
if (parentPath.isJSXAttribute()) {
|
|
121
229
|
const jsxExpression = t.jsxExpressionContainer(callExpression);
|
|
@@ -152,8 +260,6 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
152
260
|
|
|
153
261
|
if (option.extractFromText === false) return;
|
|
154
262
|
|
|
155
|
-
// console.log("TemplateElement", value);
|
|
156
|
-
|
|
157
263
|
const hashed = generateId(value);
|
|
158
264
|
|
|
159
265
|
if (i18nMap) {
|
|
@@ -175,8 +281,6 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
175
281
|
return;
|
|
176
282
|
}
|
|
177
283
|
|
|
178
|
-
// console.log("JSXText", path.node.value);
|
|
179
|
-
|
|
180
284
|
const hashed = generateId(text);
|
|
181
285
|
|
|
182
286
|
if (i18nMap) {
|
|
@@ -205,8 +309,6 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
205
309
|
return;
|
|
206
310
|
}
|
|
207
311
|
|
|
208
|
-
// console.log("JSXExpressionContainer", path.node.expression.expr);
|
|
209
|
-
|
|
210
312
|
const hashed = generateId(value);
|
|
211
313
|
|
|
212
314
|
if (i18nMap) {
|