vue-i18n-extract-plugin 1.0.54 → 1.0.55

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
@@ -48,6 +48,7 @@ extractI18n(options)
48
48
  ```javascript
49
49
  const defaultOptions = {
50
50
  translateKey: "$t", // 提取的函数的名称
51
+ JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa" defaultMsg="xxx" />
51
52
  rewrite: false, // 是否将提取到的内容转换为id后重写入源文件
52
53
  extractFromText: true, // 是否允许从纯文本节点中提取翻译内容
53
54
  autoImportI18n: true, // 是否自动导入 i18n 模块
package/lib/index.js CHANGED
@@ -16,6 +16,7 @@ const {
16
16
  relativeCWDPath,
17
17
  getLangJsonPath,
18
18
  shouldExtract,
19
+ registerLangMatch,
19
20
  trimEmptyLine,
20
21
  padEmptyLine,
21
22
  excludeDirectives,
@@ -58,6 +59,7 @@ module.exports = {
58
59
  extractFunctionName,
59
60
  relativeCWDPath,
60
61
  shouldExtract,
62
+ registerLangMatch,
61
63
  trimEmptyLine,
62
64
  padEmptyLine,
63
65
  excludeDirectives,
package/lib/options.js CHANGED
@@ -2,6 +2,7 @@ const { GoogleTranslator } = require("./translators");
2
2
 
3
3
  const defaultOptions = {
4
4
  translateKey: "$t", // 提取的函数的名称
5
+ JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa" defaultMsg="xxx" />
5
6
  rewrite: false, // 是否将提取到的内容转换为id后重写入源文件
6
7
  extractFromText: true, // 是否允许从纯文本节点中提取翻译内容
7
8
  autoImportI18n: true, // 是否自动导入 i18n 模块
package/lib/utils.js CHANGED
@@ -157,10 +157,18 @@ function sleep(ms) {
157
157
  }
158
158
 
159
159
  function shouldExtract(str, langKey) {
160
- if (REGEX_MAP[langKey]) {
161
- return REGEX_MAP[langKey].test(str);
160
+ const regex = REGEX_MAP[langKey] || REGEX_MAP[translateLangKeyEnum.ZH];
161
+ if (regex instanceof RegExp) {
162
+ return regex.test(str);
162
163
  }
163
- return REGEX_MAP[translateLangKeyEnum.ZH].test(str);
164
+ if (typeof regex === "function") {
165
+ return regex(str);
166
+ }
167
+ return false;
168
+ }
169
+
170
+ function registerLangMatch(langKey, regex) {
171
+ REGEX_MAP[langKey] = regex;
164
172
  }
165
173
 
166
174
  function trimEmptyLine(str) {
@@ -178,13 +186,15 @@ const translateLangKeyEnum = {
178
186
  KO: "ko",
179
187
  RU: "ru"
180
188
  };
189
+
181
190
  const REGEX_MAP = {
182
- [translateLangKeyEnum.ZH]: /[\u4e00-\u9fff]/,
191
+ [translateLangKeyEnum.ZH]: /[\u4e00-\u9fff]/, // 简中/繁中
183
192
  [translateLangKeyEnum.EN]: /[a-zA-Z]/,
184
193
  [translateLangKeyEnum.JA]: /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]/, // 日语假名和汉字
185
194
  [translateLangKeyEnum.KO]: /[\uAC00-\uD7A3]/, // 韩语字母
186
195
  [translateLangKeyEnum.RU]: /[йцукенгшщзхъфывапролджэячсмитьбюё .-]{1,}/ // 俄语字母
187
196
  };
197
+
188
198
  const excludeDirectives = [
189
199
  "model",
190
200
  "slot",
@@ -195,6 +205,7 @@ const excludeDirectives = [
195
205
  "once",
196
206
  "memo"
197
207
  ];
208
+
198
209
  const EXCLUDED_CALL = [
199
210
  "$deepScan",
200
211
  "console.log",
@@ -227,6 +238,7 @@ module.exports = {
227
238
  fixFolderPath,
228
239
  sleep,
229
240
  shouldExtract,
241
+ registerLangMatch,
230
242
  trimEmptyLine,
231
243
  padEmptyLine,
232
244
  excludeDirectives,
package/lib/visitors.js CHANGED
@@ -28,6 +28,22 @@ function isVNodeCall(path, nodeName) {
28
28
  );
29
29
  }
30
30
 
31
+ function isJSXElement(path, nodeName) {
32
+ const jsxElement = path.findParent(p => p.isJSXOpeningElement());
33
+
34
+ if (
35
+ jsxElement &&
36
+ t.isJSXIdentifier(jsxElement.node.name, { name: nodeName })
37
+ ) {
38
+ const jsxAttr = path.findParent(p => p.isJSXAttribute());
39
+ const attrName = jsxAttr.node.name.name;
40
+ if (attrName === "defaultMsg" || attrName === "values") {
41
+ return true;
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+
31
47
  function getPropKey(propNode) {
32
48
  if (t.isIdentifier(propNode.key)) {
33
49
  return propNode.key.name;
@@ -158,6 +174,11 @@ function createI18nVisitor(option, i18nMap) {
158
174
  return;
159
175
  }
160
176
 
177
+ // 跳过<Trans defaultMsg="你好,{name}" values={{name: '世界'} /> defaultMsg和values属性的转换
178
+ if (isJSXElement(path, option.JSXElement)) {
179
+ return;
180
+ }
181
+
161
182
  // 获取真实调用函数
162
183
  const extractFnName = extractFunctionName(path);
163
184
 
@@ -330,6 +351,77 @@ function createI18nVisitor(option, i18nMap) {
330
351
  )
331
352
  );
332
353
  }
354
+ },
355
+ JSXElement(path) {
356
+ // <Trans id="aaa" defaultMsg="xxx" />
357
+ const openingElement = path.node.openingElement;
358
+ if (
359
+ !t.isJSXIdentifier(openingElement.name) ||
360
+ openingElement.name.name !== option.JSXElement
361
+ ) {
362
+ return;
363
+ }
364
+
365
+ let idAttr = null;
366
+ let idValue = null;
367
+ let defaultMsgValue = null;
368
+
369
+ // 遍历属性,查找 id 和 defaultMsg
370
+ openingElement.attributes.forEach(attr => {
371
+ if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) return;
372
+
373
+ if (attr.name.name === "id") {
374
+ idAttr = attr;
375
+ if (attr.value && t.isStringLiteral(attr.value)) {
376
+ idValue = attr.value.value;
377
+ }
378
+ }
379
+
380
+ if (attr.name.name === "defaultMsg") {
381
+ if (attr.value && t.isStringLiteral(attr.value)) {
382
+ defaultMsgValue = attr.value.value;
383
+ }
384
+ }
385
+ });
386
+
387
+ // 计算 id,如果未提供,则使用 defaultMsg 的哈希值
388
+ if (!idValue && defaultMsgValue) {
389
+ idValue = generateId(defaultMsgValue);
390
+
391
+ if (i18nMap) {
392
+ i18nMap[idValue] = defaultMsgValue;
393
+ }
394
+ }
395
+
396
+ // 有id并且有defaultMsg的情况
397
+ if (idValue && defaultMsgValue) {
398
+ if (i18nMap) {
399
+ i18nMap[idValue] = defaultMsgValue;
400
+ }
401
+ }
402
+
403
+ if (idValue) {
404
+ // 添加或更新 id 属性
405
+ if (idAttr) {
406
+ idAttr.value = t.stringLiteral(idValue);
407
+ } else {
408
+ openingElement.attributes.push(
409
+ t.jsxAttribute(t.jsxIdentifier("id"), t.stringLiteral(idValue))
410
+ );
411
+ }
412
+ }
413
+
414
+ if (!option.keepRaw) {
415
+ // 移除 defaultMsg
416
+ openingElement.attributes = openingElement.attributes.filter(
417
+ attr =>
418
+ !(
419
+ t.isJSXAttribute(attr) &&
420
+ t.isJSXIdentifier(attr.name) &&
421
+ attr.name.name === "defaultMsg"
422
+ )
423
+ );
424
+ }
333
425
  }
334
426
  };
335
427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-i18n-extract-plugin",
3
- "version": "1.0.54",
3
+ "version": "1.0.55",
4
4
  "main": "lib/index.js",
5
5
  "types": "types/index.d.ts",
6
6
  "bin": {
package/types/index.d.ts CHANGED
@@ -16,6 +16,7 @@ export {
16
16
  relativeCWDPath,
17
17
  getLangJsonPath,
18
18
  shouldExtract,
19
+ registerLangMatch,
19
20
  trimEmptyLine,
20
21
  padEmptyLine,
21
22
  excludeDirectives,
package/types/utils.d.ts CHANGED
@@ -15,6 +15,7 @@ export function resolveFilterPath(pathStr: string): string;
15
15
  export function fixFolderPath(pathStr: string | RegExp): string;
16
16
  export function sleep(ms: number): Promise<void>;
17
17
  export function shouldExtract(str: string, langKey: LangKey): boolean;
18
+ export function registerLangMatch(langKey: LangKey, regex: RegExp | ((str: string) => boolean)): void;
18
19
  export function trimEmptyLine(str: string): string;
19
20
  export function padEmptyLine(str: string): string;
20
21
  export const excludeDirectives: string[];