vue-i18n-extract-plugin 1.0.51 → 1.0.53

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
@@ -53,6 +53,7 @@ const defaultOptions = {
53
53
  autoImportI18n: true, // 是否自动导入 i18n 模块
54
54
  autoTranslate: true, // 提取完成后是否自动翻译
55
55
  cleanTranslate: true, // 是否清理无用的翻译内容
56
+ keepRaw: false, // 开启后只做转换不生成hash值,即:"测试" -> $t("测试"), 开启rewrite时生效
56
57
  enabled: true, // 是否启用插件
57
58
  outputJsonFileInPlugin: true, // 是否在插件中输出 JSON 文件
58
59
  outputJsonFileDebounceTimeInPlugin: 2000, // 输出 JSON 文件的防抖时间
@@ -98,6 +99,18 @@ export default {
98
99
  }),
99
100
  ...
100
101
  };
102
+
103
+ // ts支持
104
+ import { defineConfig } from "vite-i18n-extract-plugin";
105
+
106
+ export default defineConfig({
107
+ rewrite: false,
108
+ translator: new YoudaoTranslator({
109
+ appId: "youdao appId",
110
+ appKey: "youdao appKey"
111
+ }),
112
+ ...
113
+ });
101
114
  ```
102
115
 
103
116
  ## Vite plugin
@@ -145,6 +158,8 @@ import enMessages from "@/locales/en.json";
145
158
 
146
159
  const i18n = createI18n({
147
160
  legacy: false,
161
+ globalInjection: true,
162
+ allowComposition: true,
148
163
  fallbackLocale: "en",
149
164
  locale: "zh",
150
165
  messages: {
@@ -156,6 +171,9 @@ const i18n = createI18n({
156
171
  // 导出一个$t方法
157
172
  export const $t = i18n.global.t.bind(i18n.global);
158
173
 
174
+ // 建议在全局也挂载一个$t方法做兜底
175
+ globalThis.$t = $t;
176
+
159
177
  export default i18n;
160
178
  ```
161
179
 
package/lib/extract.js CHANGED
@@ -30,6 +30,11 @@ const { autoTranslate, cleanTranslate, cleanI18nMap } = require("./translate");
30
30
  const { i18nImportAstTransform } = require("./import-i18n-transform");
31
31
 
32
32
  let globalI18nMap = {};
33
+ const keepRawTextOptions = {
34
+ jsescOption: {
35
+ minimal: true // 保留原始字符串,不被转义成Unicode
36
+ }
37
+ };
33
38
 
34
39
  function encodeToString(str) {
35
40
  return str.indexOf("'") === -1 ? `'${str}'` : `"${str}"`;
@@ -68,7 +73,7 @@ function unwrapTransformedCode(ast) {
68
73
  });
69
74
 
70
75
  if (extractedNode) {
71
- const { code: exprCode } = generate(extractedNode);
76
+ const { code: exprCode } = generate(extractedNode, keepRawTextOptions);
72
77
  return exprCode;
73
78
  } else {
74
79
  console.warn("未能提取到 _expr 表达式");
@@ -118,7 +123,7 @@ function extractTCallsFromInterpolation(code, options, i18nMap) {
118
123
  traverse(program, createI18nVisitor(options, i18nMap));
119
124
 
120
125
  if (options.rewrite) {
121
- return generate(ast, { compact: true }).code;
126
+ return generate(ast, { compact: true, ...keepRawTextOptions }).code;
122
127
  }
123
128
 
124
129
  // return ast;
@@ -252,7 +257,7 @@ function transformScript(code, options, useAst, i18nMap) {
252
257
  }
253
258
  return {
254
259
  changed: true,
255
- code: generate(ast, { retainLines: true }).code
260
+ code: generate(ast, { retainLines: true, ...keepRawTextOptions }).code
256
261
  };
257
262
  }
258
263
 
package/lib/options.js CHANGED
@@ -7,6 +7,7 @@ const defaultOptions = {
7
7
  autoImportI18n: true, // 是否自动导入 i18n 模块
8
8
  autoTranslate: true, // 提取完成后是否自动翻译
9
9
  cleanTranslate: true, // 是否清理无用的翻译内容
10
+ keepRaw: false, // 开启后只做转换不生成hash值,即:"测试" -> $t("测试"), 开启rewrite时生效
10
11
  enabled: true, // 是否启用插件
11
12
  outputJsonFileInPlugin: true, // 是否在插件中输出 JSON 文件
12
13
  outputJsonFileDebounceTimeInPlugin: 2000, // 输出 JSON 文件的防抖时间
package/lib/visitors.js CHANGED
@@ -15,16 +15,16 @@ function isTFunction(node, option) {
15
15
  );
16
16
  }
17
17
 
18
- function isCreateTextVNodeCall(path) {
18
+ function isVNodeCall(path, nodeName) {
19
19
  const node = path.node;
20
20
  if (!path.isCallExpression()) return false;
21
21
 
22
22
  const callee = node.callee;
23
23
  return (
24
- (t.isIdentifier(callee) && callee.name === "_createTextVNode") ||
24
+ (t.isIdentifier(callee) && callee.name === nodeName) ||
25
25
  (t.isMemberExpression(callee) &&
26
26
  t.isIdentifier(callee.property) &&
27
- callee.property.name === "_createTextVNode")
27
+ callee.property.name === nodeName)
28
28
  );
29
29
  }
30
30
 
@@ -106,6 +106,13 @@ function shouldTransform(path) {
106
106
  );
107
107
  }
108
108
 
109
+ function generateText(rawText, hashedText, options) {
110
+ if (options.keepRaw && options.rewrite) {
111
+ return rawText;
112
+ }
113
+ return hashedText;
114
+ }
115
+
109
116
  function createI18nVisitor(option, i18nMap) {
110
117
  const excludedCall = [...option.excludedCall, ...EXCLUDED_CALL];
111
118
 
@@ -130,15 +137,13 @@ function createI18nVisitor(option, i18nMap) {
130
137
  return;
131
138
  }
132
139
 
133
- // console.log("CallExpression", path.node.arguments);
134
-
135
140
  const hashed = generateId(keyText);
136
141
 
137
142
  if (i18nMap) {
138
143
  i18nMap[hashed] = keyText;
139
144
  }
140
145
 
141
- const newArg = t.stringLiteral(hashed);
146
+ const newArg = t.stringLiteral(generateText(keyText, hashed, option));
142
147
  path.node.arguments[0] = newArg;
143
148
  },
144
149
 
@@ -182,46 +187,46 @@ function createI18nVisitor(option, i18nMap) {
182
187
  return;
183
188
  }
184
189
 
185
- // console.log("StringLiteral", value);
186
-
187
190
  const hashed = generateId(value);
188
191
 
189
192
  if (i18nMap) {
190
193
  i18nMap[hashed] = value;
191
194
  }
192
195
 
193
- let callExpression;
196
+ // 生成 _ctx.$t("hashed")
197
+ let callExpression = t.callExpression(
198
+ t.memberExpression(
199
+ t.identifier("_ctx"),
200
+ t.identifier(option.translateKey)
201
+ ),
202
+ [t.stringLiteral(generateText(value, hashed, option))]
203
+ );
194
204
 
195
205
  // 判断是否createTextVNode或MemberExpression(如 Vue.createTextVNode)
196
- if (isCreateTextVNodeCall(parentPath)) {
197
- // _ctx.$t("hashed")
198
- callExpression = t.callExpression(
199
- t.memberExpression(
200
- t.identifier("_ctx"),
201
- t.identifier(option.translateKey)
202
- ),
203
- [t.stringLiteral(hashed)]
204
- );
205
-
206
+ if (isVNodeCall(parentPath, "_createTextVNode")) {
206
207
  // 如果 createTextVNode 参数只有一个,则补充第二个参数 1
207
208
  const args = parentPath.node.arguments;
208
209
  if (args.length === 1) {
209
210
  parentPath.node.arguments = [callExpression, t.numericLiteral(1)];
210
211
  }
212
+ // 是否 vue.createElementVNode(...),且该 StringLiteral 是第三个参数
213
+ } else if (isVNodeCall(parentPath, "_createElementVNode")) {
214
+ const callExpr = parentPath.node;
215
+ const args = callExpr.arguments;
216
+
217
+ // 当前是第三个参数,且参数数量为3
218
+ const argIndex = args.findIndex(arg => arg === path.node);
219
+ if (argIndex === 2 && args.length === 3) {
220
+ args[2] = callExpression; // 替换第3个参数
221
+ args.push(t.numericLiteral(1)); // 添加第4个参数
222
+ return;
223
+ }
211
224
  } else {
212
225
  const hasCreateVNode = transformDirectiveIfNeeded(path, parentPath);
213
- if (hasCreateVNode) {
214
- callExpression = t.callExpression(
215
- t.memberExpression(
216
- t.identifier("_ctx"),
217
- t.identifier(option.translateKey)
218
- ),
219
- [t.stringLiteral(hashed)]
220
- );
221
- } else {
222
- // $t("hashed")
226
+ if (!hasCreateVNode) {
227
+ // 生成 $t("hashed")
223
228
  callExpression = t.callExpression(t.identifier(option.translateKey), [
224
- t.stringLiteral(hashed)
229
+ t.stringLiteral(generateText(value, hashed, option))
225
230
  ]);
226
231
  }
227
232
  }
@@ -262,8 +267,6 @@ function createI18nVisitor(option, i18nMap) {
262
267
 
263
268
  if (option.extractFromText === false) return;
264
269
 
265
- // console.log("TemplateElement", value);
266
-
267
270
  const hashed = generateId(value);
268
271
 
269
272
  if (i18nMap) {
@@ -271,7 +274,7 @@ function createI18nVisitor(option, i18nMap) {
271
274
  }
272
275
 
273
276
  // 替换为字符类型翻译节点
274
- const tCallExpression = `${option.translateKey}('${hashed}')`;
277
+ const tCallExpression = `${option.translateKey}('${generateText(value, hashed, option)}')`;
275
278
  node.value.raw = node.value.cooked = `\${${tCallExpression}}`;
276
279
  },
277
280
  JSXText(path) {
@@ -285,8 +288,6 @@ function createI18nVisitor(option, i18nMap) {
285
288
  return;
286
289
  }
287
290
 
288
- // console.log("JSXText", path.node.value);
289
-
290
291
  const hashed = generateId(text);
291
292
 
292
293
  if (i18nMap) {
@@ -297,7 +298,7 @@ function createI18nVisitor(option, i18nMap) {
297
298
  path.replaceWith(
298
299
  t.jsxExpressionContainer(
299
300
  t.callExpression(t.identifier(option.translateKey), [
300
- t.stringLiteral(hashed)
301
+ t.stringLiteral(generateText(text, hashed, option))
301
302
  ])
302
303
  )
303
304
  );
@@ -315,8 +316,6 @@ function createI18nVisitor(option, i18nMap) {
315
316
  return;
316
317
  }
317
318
 
318
- // console.log("JSXExpressionContainer", path.node.expression.expr);
319
-
320
319
  const hashed = generateId(value);
321
320
 
322
321
  if (i18nMap) {
@@ -326,7 +325,7 @@ function createI18nVisitor(option, i18nMap) {
326
325
  path.replaceWith(
327
326
  t.jsxExpressionContainer(
328
327
  t.callExpression(t.identifier(option.translateKey), [
329
- t.stringLiteral(hashed)
328
+ t.stringLiteral(generateText(value, hashed, option))
330
329
  ])
331
330
  )
332
331
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-i18n-extract-plugin",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
4
  "main": "lib/index.js",
5
5
  "types": "types/index.d.ts",
6
6
  "bin": {