vue-i18n-extract-plugin 1.0.62 → 1.0.64
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 +2 -1
- package/lib/babel-plugin-i18n.js +1 -2
- package/lib/babel-plugin-import-i18n.js +7 -120
- package/lib/extract.js +7 -1
- package/lib/import-i18n-transform.js +26 -31
- package/lib/options.js +2 -1
- package/lib/visitors.js +40 -1
- package/lib/vite-plugin-import-i18n.js +3 -1
- package/lib/webpack-import-i18n-loader.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,7 +48,8 @@ extractI18n(options)
|
|
|
48
48
|
```javascript
|
|
49
49
|
const defaultOptions = {
|
|
50
50
|
translateKey: "$t", // 提取的函数的名称
|
|
51
|
-
JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa"
|
|
51
|
+
JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa" msg="xxx" />
|
|
52
|
+
jsx: false, // 是否启用 JSX 语法转换,开启后JSX里纯文件将转换为 <Trans id="aaa" msg="xxx" />而不是 $t("aaa")
|
|
52
53
|
rewrite: false, // 是否将提取到的内容转换为id后重写入源文件
|
|
53
54
|
extractFromText: true, // 是否允许从纯文本节点中提取翻译内容
|
|
54
55
|
autoImportI18n: true, // 是否自动导入 i18n 模块
|
package/lib/babel-plugin-i18n.js
CHANGED
|
@@ -2,8 +2,7 @@ const { declare } = require("@babel/helper-plugin-utils");
|
|
|
2
2
|
const { createI18nPlugin } = require("./visitors");
|
|
3
3
|
const { defaultOptions } = require("./options");
|
|
4
4
|
|
|
5
|
-
module.exports = declare((
|
|
6
|
-
api.assertVersion(7);
|
|
5
|
+
module.exports = declare((_, options) => {
|
|
7
6
|
options = { ...defaultOptions, ...options };
|
|
8
7
|
|
|
9
8
|
if (!options.enabled) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { i18nImportAstTransform } = require("./import-i18n-transform");
|
|
2
2
|
const { defaultOptions } = require("./options");
|
|
3
3
|
|
|
4
4
|
module.exports = function () {
|
|
@@ -9,6 +9,8 @@ module.exports = function () {
|
|
|
9
9
|
const {
|
|
10
10
|
translateKey: importName,
|
|
11
11
|
i18nPkgImportPath: importPath,
|
|
12
|
+
JSXElement,
|
|
13
|
+
jsx,
|
|
12
14
|
enabled
|
|
13
15
|
} = {
|
|
14
16
|
...defaultOptions,
|
|
@@ -17,126 +19,11 @@ module.exports = function () {
|
|
|
17
19
|
|
|
18
20
|
if (!enabled) return;
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
let lastImportPath = null;
|
|
23
|
-
let conflictTDefined = false;
|
|
22
|
+
const ast = path.parentPath?.node || path.node;
|
|
23
|
+
const importNames = jsx ? [importName, JSXElement] : [importName];
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// lastImportPath = path;
|
|
28
|
-
// if (
|
|
29
|
-
// path.node.source.value === importPath &&
|
|
30
|
-
// path.node.specifiers.some(
|
|
31
|
-
// spec =>
|
|
32
|
-
// t.isImportSpecifier(spec) && spec.imported.name === importName
|
|
33
|
-
// )
|
|
34
|
-
// ) {
|
|
35
|
-
// hasI18nImport = true;
|
|
36
|
-
// }
|
|
37
|
-
// },
|
|
38
|
-
ImportDeclaration(path) {
|
|
39
|
-
lastImportPath = path;
|
|
40
|
-
|
|
41
|
-
const sourcePath = path.node.source.value;
|
|
42
|
-
|
|
43
|
-
// 判断是否是其它路径导入了 $t
|
|
44
|
-
const importedTElsewhere = path.node.specifiers.some(spec => {
|
|
45
|
-
return (
|
|
46
|
-
(t.isImportSpecifier(spec) ||
|
|
47
|
-
t.isImportDefaultSpecifier(spec)) &&
|
|
48
|
-
spec.local.name === importName &&
|
|
49
|
-
sourcePath !== importPath
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
if (importedTElsewhere) {
|
|
54
|
-
conflictTDefined = true;
|
|
55
|
-
path.stop();
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 检查是否已经导入目标路径
|
|
60
|
-
if (sourcePath === importPath) {
|
|
61
|
-
hasI18nImport = true;
|
|
62
|
-
|
|
63
|
-
// 情况1:已有 import { $t } from '@/i18n'
|
|
64
|
-
const existingImport = path.node.specifiers.find(
|
|
65
|
-
spec =>
|
|
66
|
-
t.isImportSpecifier(spec) && spec.imported.name === importName
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
if (existingImport) return;
|
|
70
|
-
|
|
71
|
-
// if (
|
|
72
|
-
// existingImport &&
|
|
73
|
-
// !t.isImportSpecifier(existingImport, {
|
|
74
|
-
// local: { name: localName }
|
|
75
|
-
// })
|
|
76
|
-
// ) {
|
|
77
|
-
// // 将 $t 改为 _$t
|
|
78
|
-
// existingImport.local = t.identifier(localName);
|
|
79
|
-
// } else
|
|
80
|
-
// 情况2:已有 import i18n from '@/i18n'
|
|
81
|
-
|
|
82
|
-
// 添加 $t 到已有的 import
|
|
83
|
-
path.node.specifiers.push(
|
|
84
|
-
t.importSpecifier(
|
|
85
|
-
t.identifier(localName),
|
|
86
|
-
t.identifier(importName)
|
|
87
|
-
)
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
VariableDeclarator(path) {
|
|
92
|
-
if (
|
|
93
|
-
t.isIdentifier(path.node.id) &&
|
|
94
|
-
path.node.id.name === importName
|
|
95
|
-
) {
|
|
96
|
-
conflictTDefined = true;
|
|
97
|
-
path.stop();
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
FunctionDeclaration(path) {
|
|
102
|
-
if (
|
|
103
|
-
t.isIdentifier(path.node.id) &&
|
|
104
|
-
path.node.id.name === importName
|
|
105
|
-
) {
|
|
106
|
-
conflictTDefined = true;
|
|
107
|
-
path.stop();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// 跳过导入语句的生成
|
|
113
|
-
if (conflictTDefined) {
|
|
114
|
-
return {
|
|
115
|
-
ast,
|
|
116
|
-
needTransform: false
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// 情况4:完全没有导入 @/i18n
|
|
121
|
-
if (!hasI18nImport) {
|
|
122
|
-
const importNode = t.importDeclaration(
|
|
123
|
-
[
|
|
124
|
-
// t.importDefaultSpecifier(t.identifier("i18n")),
|
|
125
|
-
t.importSpecifier(
|
|
126
|
-
t.identifier(localName),
|
|
127
|
-
t.identifier(importName)
|
|
128
|
-
)
|
|
129
|
-
],
|
|
130
|
-
t.stringLiteral(importPath)
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if (lastImportPath) {
|
|
134
|
-
// 插入到最后一个 import 之后
|
|
135
|
-
lastImportPath.insertAfter(importNode);
|
|
136
|
-
} else {
|
|
137
|
-
// 插入到文件顶部
|
|
138
|
-
path.unshiftContainer("body", importNode);
|
|
139
|
-
}
|
|
25
|
+
for (const importName of importNames) {
|
|
26
|
+
i18nImportAstTransform(ast, importName, importPath);
|
|
140
27
|
}
|
|
141
28
|
}
|
|
142
29
|
}
|
package/lib/extract.js
CHANGED
|
@@ -170,7 +170,13 @@ function createDirectiveFromProp(prop, content) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
function addI18nImportIfNeeded(ast, options, generateCode) {
|
|
173
|
-
|
|
173
|
+
const importNames = options.jsx
|
|
174
|
+
? [options.translateKey, options.JSXElement]
|
|
175
|
+
: [options.translateKey];
|
|
176
|
+
|
|
177
|
+
for (const importName of importNames) {
|
|
178
|
+
i18nImportAstTransform(ast, importName, options.i18nPkgImportPath);
|
|
179
|
+
}
|
|
174
180
|
|
|
175
181
|
if (generateCode) {
|
|
176
182
|
return generate(ast);
|
|
@@ -7,7 +7,11 @@ function i18nImportAstTransform(ast, importName, importPath) {
|
|
|
7
7
|
let hasI18nImport = false;
|
|
8
8
|
let lastImportNode = null;
|
|
9
9
|
let needTransform = false;
|
|
10
|
-
let
|
|
10
|
+
let conflictDefined = false;
|
|
11
|
+
|
|
12
|
+
if (importName.includes(".")) {
|
|
13
|
+
importName = importName.split(".")[0];
|
|
14
|
+
}
|
|
11
15
|
|
|
12
16
|
traverse(ast, {
|
|
13
17
|
ImportDeclaration(path) {
|
|
@@ -15,63 +19,50 @@ function i18nImportAstTransform(ast, importName, importPath) {
|
|
|
15
19
|
|
|
16
20
|
const sourcePath = path.node.source.value;
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
const importedTElsewhere = path.node.specifiers.some(spec => {
|
|
22
|
+
const importedElsewhere = path.node.specifiers.some(spec => {
|
|
20
23
|
return (
|
|
21
24
|
(t.isImportSpecifier(spec) || t.isImportDefaultSpecifier(spec)) &&
|
|
22
|
-
spec.local.name === importName
|
|
23
|
-
sourcePath !== importPath
|
|
25
|
+
spec.local.name === importName
|
|
24
26
|
);
|
|
25
27
|
});
|
|
26
28
|
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
+
if (importedElsewhere) {
|
|
30
|
+
conflictDefined = true;
|
|
29
31
|
path.stop();
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
// 检查是否已经导入目标路径
|
|
34
35
|
if (sourcePath === importPath) {
|
|
35
|
-
hasI18nImport = true;
|
|
36
|
-
|
|
37
|
-
// 情况1:已有 import { $t } from '@/i18n'
|
|
38
|
-
const existImport = path.node.specifiers.some(
|
|
39
|
-
spec => t.isImportSpecifier(spec) && spec.imported.name === importName
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
if (existImport) return;
|
|
43
|
-
|
|
44
|
-
// 添加 $t 到已有的 import
|
|
45
36
|
path.node.specifiers.push(
|
|
46
37
|
t.importSpecifier(t.identifier(importName), t.identifier(importName))
|
|
47
38
|
);
|
|
39
|
+
|
|
40
|
+
hasI18nImport = true;
|
|
48
41
|
needTransform = true;
|
|
49
42
|
}
|
|
50
43
|
},
|
|
51
44
|
VariableDeclarator(path) {
|
|
52
45
|
if (t.isIdentifier(path.node.id) && path.node.id.name === importName) {
|
|
53
|
-
|
|
46
|
+
conflictDefined = true;
|
|
54
47
|
path.stop();
|
|
55
48
|
}
|
|
56
49
|
},
|
|
57
50
|
|
|
58
51
|
FunctionDeclaration(path) {
|
|
59
52
|
if (t.isIdentifier(path.node.id) && path.node.id.name === importName) {
|
|
60
|
-
|
|
53
|
+
conflictDefined = true;
|
|
61
54
|
path.stop();
|
|
62
55
|
}
|
|
63
56
|
}
|
|
64
57
|
});
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
if (conflictTDefined) {
|
|
59
|
+
if (conflictDefined) {
|
|
68
60
|
return {
|
|
69
61
|
ast,
|
|
70
62
|
needTransform: false
|
|
71
63
|
};
|
|
72
64
|
}
|
|
73
65
|
|
|
74
|
-
// 情况4:完全没有导入 @/i18n
|
|
75
66
|
if (!hasI18nImport) {
|
|
76
67
|
const importNode = t.importDeclaration(
|
|
77
68
|
[
|
|
@@ -90,7 +81,6 @@ function i18nImportAstTransform(ast, importName, importPath) {
|
|
|
90
81
|
} else {
|
|
91
82
|
ast.program.body.unshift(importNode);
|
|
92
83
|
}
|
|
93
|
-
needTransform = true;
|
|
94
84
|
}
|
|
95
85
|
|
|
96
86
|
return {
|
|
@@ -99,7 +89,7 @@ function i18nImportAstTransform(ast, importName, importPath) {
|
|
|
99
89
|
};
|
|
100
90
|
}
|
|
101
91
|
|
|
102
|
-
async function i18nImportTransform(code, path,
|
|
92
|
+
async function i18nImportTransform(code, path, importNames, importPath) {
|
|
103
93
|
const scriptContent = extractScriptContent(code, path);
|
|
104
94
|
if (!scriptContent) return code;
|
|
105
95
|
|
|
@@ -113,14 +103,19 @@ async function i18nImportTransform(code, path, importName, importPath) {
|
|
|
113
103
|
].filter(Boolean)
|
|
114
104
|
});
|
|
115
105
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
106
|
+
let transformNeeded = false;
|
|
107
|
+
|
|
108
|
+
for (const importName of importNames) {
|
|
109
|
+
const { needTransform } = i18nImportAstTransform(
|
|
110
|
+
ast,
|
|
111
|
+
importName,
|
|
112
|
+
importPath
|
|
113
|
+
);
|
|
114
|
+
transformNeeded = transformNeeded || needTransform;
|
|
115
|
+
}
|
|
121
116
|
|
|
122
117
|
// 只有当需要修改时才重新生成代码
|
|
123
|
-
if (
|
|
118
|
+
if (transformNeeded) {
|
|
124
119
|
const { code: newScript } = generate(ast);
|
|
125
120
|
return path.endsWith(".vue")
|
|
126
121
|
? code.replace(scriptContent, newScript)
|
package/lib/options.js
CHANGED
|
@@ -2,7 +2,8 @@ const { GoogleTranslator } = require("./translators");
|
|
|
2
2
|
|
|
3
3
|
const defaultOptions = {
|
|
4
4
|
translateKey: "$t", // 提取的函数的名称
|
|
5
|
-
JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa"
|
|
5
|
+
JSXElement: "Trans", // 提取的函数的 JSX 元素名称 默认为 Trans, 如:<Trans id="aaa" msg="xxx" />
|
|
6
|
+
jsx: false, // 是否启用 JSX 语法转换,开启后JSX里纯文件将转换为 <Trans id="aaa" msg="xxx" />而不是 $t("aaa")
|
|
6
7
|
rewrite: false, // 是否将提取到的内容转换为id后重写入源文件
|
|
7
8
|
extractFromText: true, // 是否允许从纯文本节点中提取翻译内容
|
|
8
9
|
autoImportI18n: true, // 是否自动导入 i18n 模块
|
package/lib/visitors.js
CHANGED
|
@@ -11,7 +11,9 @@ function isTFunction(node, option) {
|
|
|
11
11
|
t.isCallExpression(node) &&
|
|
12
12
|
(t.isIdentifier(node.callee, { name: option.translateKey }) || // $t(...)
|
|
13
13
|
(t.isMemberExpression(node.callee) &&
|
|
14
|
-
t.isIdentifier(node.callee.property, {
|
|
14
|
+
t.isIdentifier(node.callee.property, {
|
|
15
|
+
name: option.translateKey.split(".").pop()
|
|
16
|
+
}))) // xxx.$t(...)
|
|
15
17
|
);
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -136,6 +138,20 @@ function generateId(rawText, options) {
|
|
|
136
138
|
return _generateId(rawText);
|
|
137
139
|
}
|
|
138
140
|
|
|
141
|
+
function generateJSXElement(name, id, msg) {
|
|
142
|
+
// 生成 <Trans id="hashed" msg="xxxx" />
|
|
143
|
+
const openingElement = t.jsxOpeningElement(
|
|
144
|
+
t.jsxIdentifier(name),
|
|
145
|
+
[
|
|
146
|
+
t.jsxAttribute(t.jsxIdentifier("id"), t.stringLiteral(id)),
|
|
147
|
+
msg ? t.jsxAttribute(t.jsxIdentifier("msg"), t.stringLiteral(msg)) : null
|
|
148
|
+
].filter(Boolean),
|
|
149
|
+
true // self-closing
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return t.jsxElement(openingElement, null, [], true);
|
|
153
|
+
}
|
|
154
|
+
|
|
139
155
|
function createI18nVisitor(option, i18nMap) {
|
|
140
156
|
const excludedCall = [...option.excludedCall, ...EXCLUDED_CALL];
|
|
141
157
|
|
|
@@ -336,6 +352,16 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
336
352
|
i18nMap[hashed] = text;
|
|
337
353
|
}
|
|
338
354
|
|
|
355
|
+
if (option.jsx) {
|
|
356
|
+
const jsxElement = generateJSXElement(
|
|
357
|
+
option.JSXElement,
|
|
358
|
+
hashed,
|
|
359
|
+
option.keepDefaultMsg ? text : null
|
|
360
|
+
);
|
|
361
|
+
path.replaceWith(jsxElement);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
339
365
|
// 替换为表达式 {$t("hashed")}
|
|
340
366
|
path.replaceWith(
|
|
341
367
|
t.jsxExpressionContainer(
|
|
@@ -368,6 +394,19 @@ function createI18nVisitor(option, i18nMap) {
|
|
|
368
394
|
i18nMap[hashed] = value;
|
|
369
395
|
}
|
|
370
396
|
|
|
397
|
+
if (
|
|
398
|
+
option.jsx &&
|
|
399
|
+
(path.parentPath.isJSXElement() || path.parentPath.isJSXFragment())
|
|
400
|
+
) {
|
|
401
|
+
const jsxElement = generateJSXElement(
|
|
402
|
+
option.JSXElement,
|
|
403
|
+
hashed,
|
|
404
|
+
option.keepDefaultMsg ? value : null
|
|
405
|
+
);
|
|
406
|
+
path.replaceWith(jsxElement);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
371
410
|
path.replaceWith(
|
|
372
411
|
t.jsxExpressionContainer(
|
|
373
412
|
t.callExpression(
|
|
@@ -9,6 +9,8 @@ module.exports = function (source) {
|
|
|
9
9
|
includePath,
|
|
10
10
|
excludedPath,
|
|
11
11
|
translateKey: importName,
|
|
12
|
+
JSXElement,
|
|
13
|
+
jsx,
|
|
12
14
|
i18nPkgImportPath: importPath
|
|
13
15
|
} = { ...defaultOptions, ...global.getOptions() };
|
|
14
16
|
|
|
@@ -42,7 +44,7 @@ module.exports = function (source) {
|
|
|
42
44
|
return i18nImportTransform(
|
|
43
45
|
source,
|
|
44
46
|
global.resourcePath,
|
|
45
|
-
importName,
|
|
47
|
+
jsx ? [importName, JSXElement] : [importName],
|
|
46
48
|
importPath
|
|
47
49
|
);
|
|
48
50
|
};
|