i18n-easy 1.0.0
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 +527 -0
- package/esm/assets/const.js +7 -0
- package/esm/ast_transformer.d.ts +19 -0
- package/esm/ast_transformer.js +54 -0
- package/esm/eslint/react/i18n_easy_plugin.d.ts +3 -0
- package/esm/eslint/react/i18n_easy_plugin.js +111 -0
- package/esm/eslint/react/i18n_utils.d.ts +61 -0
- package/esm/eslint/react/i18n_utils.js +115 -0
- package/esm/eslint/react/report_and_fix.d.ts +67 -0
- package/esm/eslint/react/report_and_fix.js +118 -0
- package/esm/eslint/vue/i18n_easy_plugin.d.ts +5 -0
- package/esm/eslint/vue/i18n_easy_plugin.js +141 -0
- package/esm/eslint/vue/i18n_utils.d.ts +65 -0
- package/esm/eslint/vue/i18n_utils.js +133 -0
- package/esm/eslint/vue/report_and_fix.d.ts +8 -0
- package/esm/eslint/vue/report_and_fix.js +169 -0
- package/esm/file_processor_utils.d.ts +14 -0
- package/esm/file_processor_utils.js +68 -0
- package/esm/i18n_easy.d.ts +77 -0
- package/esm/i18n_easy.js +171 -0
- package/esm/index.d.ts +4 -0
- package/esm/index.js +9 -0
- package/esm/key_map.d.ts +14 -0
- package/esm/key_map.js +51 -0
- package/esm/patch.d.ts +5 -0
- package/esm/progressBus.d.ts +7 -0
- package/esm/progressBus.js +25 -0
- package/esm/translate.d.ts +34 -0
- package/esm/translate.js +107 -0
- package/esm/type.d.ts +94 -0
- package/esm/type.js +0 -0
- package/esm/utils.d.ts +54 -0
- package/esm/utils.js +102 -0
- package/lib/assets/const.js +32 -0
- package/lib/ast_transformer.js +88 -0
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +5 -0
- package/lib/eslint/react/i18n_easy_plugin.js +131 -0
- package/lib/eslint/react/i18n_utils.js +145 -0
- package/lib/eslint/react/report_and_fix.js +156 -0
- package/lib/eslint/vue/i18n_easy_plugin.js +161 -0
- package/lib/eslint/vue/i18n_utils.js +164 -0
- package/lib/eslint/vue/report_and_fix.js +203 -0
- package/lib/file_processor_utils.js +109 -0
- package/lib/i18n_easy.js +205 -0
- package/lib/index.js +35 -0
- package/lib/key_map.js +71 -0
- package/lib/package.json +3 -0
- package/lib/patch.d.ts +5 -0
- package/lib/progressBus.js +50 -0
- package/lib/translate.js +127 -0
- package/lib/type.js +17 -0
- package/lib/utils.js +142 -0
- package/package.json +68 -0
- package/types/assets/const.d.ts +2 -0
- package/types/ast_transformer.d.ts +19 -0
- package/types/eslint/react/i18n_easy_plugin.d.ts +3 -0
- package/types/eslint/react/i18n_utils.d.ts +61 -0
- package/types/eslint/react/report_and_fix.d.ts +67 -0
- package/types/eslint/vue/i18n_easy_plugin.d.ts +5 -0
- package/types/eslint/vue/i18n_utils.d.ts +65 -0
- package/types/eslint/vue/report_and_fix.d.ts +8 -0
- package/types/file_processor_utils.d.ts +14 -0
- package/types/i18n_easy.d.ts +77 -0
- package/types/index.d.ts +4 -0
- package/types/key_map.d.ts +14 -0
- package/types/progressBus.d.ts +6 -0
- package/types/translate.d.ts +34 -0
- package/types/type.d.ts +94 -0
- package/types/utils.d.ts +54 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/eslint/vue/i18n_utils.ts
|
|
20
|
+
var i18n_utils_exports = {};
|
|
21
|
+
__export(i18n_utils_exports, {
|
|
22
|
+
callNameNotExist: () => callNameNotExist,
|
|
23
|
+
canCollectOperateNodes: () => canCollectOperateNodes,
|
|
24
|
+
createI18nIgnoreChecker: () => createI18nIgnoreChecker,
|
|
25
|
+
getI18nCallMethod: () => getI18nCallMethod,
|
|
26
|
+
getInjectNode: () => getInjectNode,
|
|
27
|
+
getLastImportOrFirstNotImportNode: () => getLastImportOrFirstNotImportNode,
|
|
28
|
+
getLeadingSpaces: () => getLeadingSpaces,
|
|
29
|
+
isScriptSetup: () => isScriptSetup
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(i18n_utils_exports);
|
|
32
|
+
var import_utils = require("../../utils.js");
|
|
33
|
+
function createI18nIgnoreChecker(context, templateBody) {
|
|
34
|
+
const ignoreLines = [];
|
|
35
|
+
const jsComments = context.sourceCode.getAllComments();
|
|
36
|
+
const templateComments = templateBody ? templateBody.comments : [];
|
|
37
|
+
for (const comment of [...jsComments, ...templateComments]) {
|
|
38
|
+
if (["HTMLComment", "Line", "Block"].includes(comment.type) && comment.value.trim().includes("@i18n-ignore")) {
|
|
39
|
+
ignoreLines.push([comment.loc.start.line, comment.loc.end.line]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return function isIgnored(node) {
|
|
43
|
+
const nodeStartLine = node.loc.start.line;
|
|
44
|
+
const nodeEndLine = node.loc.end.line;
|
|
45
|
+
if (nodeStartLine !== nodeEndLine) return false;
|
|
46
|
+
return ignoreLines.some(([_start, end]) => end === nodeEndLine - 1);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function getScope(context, node) {
|
|
50
|
+
if (node.type === "Program") {
|
|
51
|
+
const programScope = context.sourceCode.getScope(node);
|
|
52
|
+
return programScope.type == "global" ? programScope.childScopes[0] : programScope;
|
|
53
|
+
}
|
|
54
|
+
if (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
55
|
+
return context.sourceCode.getScope(node);
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function callNameNotExist(context, node, codeType) {
|
|
60
|
+
if (!node) return false;
|
|
61
|
+
const { callMethodName } = context.settings.i18nInfo[codeType] || {};
|
|
62
|
+
if (!callMethodName) return false;
|
|
63
|
+
const scope = getScope(context, node);
|
|
64
|
+
if (!scope) return false;
|
|
65
|
+
return scope.variables.some((ref) => ref.name === callMethodName) === false;
|
|
66
|
+
}
|
|
67
|
+
function getLastImportOrFirstNotImportNode(context) {
|
|
68
|
+
const src = context.sourceCode;
|
|
69
|
+
const body = src.ast.body;
|
|
70
|
+
let lastImport = body.filter((n) => n.type === "ImportDeclaration").pop();
|
|
71
|
+
if (lastImport) {
|
|
72
|
+
return {
|
|
73
|
+
node: lastImport,
|
|
74
|
+
type: "lastImport"
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
let firstNonImport = null;
|
|
78
|
+
const allNodes = [...body];
|
|
79
|
+
const commentsAsNodes = src.getAllComments();
|
|
80
|
+
const sorted = [...allNodes, ...commentsAsNodes].sort((a, b) => {
|
|
81
|
+
if (a.range && b.range) {
|
|
82
|
+
return a.range[0] - b.range[0];
|
|
83
|
+
}
|
|
84
|
+
return 0;
|
|
85
|
+
});
|
|
86
|
+
firstNonImport = sorted.find((n) => n.type !== "ImportDeclaration");
|
|
87
|
+
if (firstNonImport) {
|
|
88
|
+
return {
|
|
89
|
+
node: firstNonImport,
|
|
90
|
+
type: "firstNonImport"
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function getInjectNode(context, node) {
|
|
96
|
+
const commentNodes = context.sourceCode.getAllComments();
|
|
97
|
+
const comments = commentNodes.filter((item) => {
|
|
98
|
+
if (item.range && node.range) {
|
|
99
|
+
return item.range[0] >= node.range[0] && item.range[1] <= node.range[1];
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
});
|
|
103
|
+
const sorted = [...comments, ...node.body].sort((a, b) => {
|
|
104
|
+
if (a.range && b.range) {
|
|
105
|
+
return a.range[0] - b.range[0];
|
|
106
|
+
}
|
|
107
|
+
return 0;
|
|
108
|
+
});
|
|
109
|
+
return sorted[0];
|
|
110
|
+
}
|
|
111
|
+
function canCollectOperateNodes(node, isIgnored) {
|
|
112
|
+
if (isIgnored(node)) return false;
|
|
113
|
+
switch (node.type) {
|
|
114
|
+
case "VText":
|
|
115
|
+
if (!(0, import_utils.hasChineseChar)(node.value)) return false;
|
|
116
|
+
break;
|
|
117
|
+
case "VAttribute":
|
|
118
|
+
if (!node.value) return false;
|
|
119
|
+
if (node.value.type !== "VLiteral") return false;
|
|
120
|
+
if (!(0, import_utils.hasChineseChar)(node.value.value)) return false;
|
|
121
|
+
break;
|
|
122
|
+
case "TemplateLiteral":
|
|
123
|
+
if (!node.quasis.some((q) => (0, import_utils.hasChineseChar)(q.value.cooked))) return false;
|
|
124
|
+
break;
|
|
125
|
+
case "Literal":
|
|
126
|
+
if (!(0, import_utils.hasChineseChar)(node.value)) return false;
|
|
127
|
+
break;
|
|
128
|
+
case "JSXText":
|
|
129
|
+
if (!(0, import_utils.hasChineseChar)(node.value)) return false;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
function getLeadingSpaces(context, node) {
|
|
135
|
+
if (!node.loc) return "";
|
|
136
|
+
const line = context.sourceCode.lines[node.loc.start.line - 1];
|
|
137
|
+
return line.slice(0, node.loc.start.column);
|
|
138
|
+
}
|
|
139
|
+
function getI18nCallMethod(callMethodName, key, value) {
|
|
140
|
+
if (value) {
|
|
141
|
+
return `${callMethodName}('${key}', { ${value} })`;
|
|
142
|
+
}
|
|
143
|
+
return `${callMethodName}('${key}')`;
|
|
144
|
+
}
|
|
145
|
+
function isScriptSetup(context) {
|
|
146
|
+
var _a, _b;
|
|
147
|
+
const df = (_b = (_a = context.sourceCode.parserServices).getDocumentFragment) == null ? void 0 : _b.call(_a);
|
|
148
|
+
if (!df) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const scripts = df.children.filter((e) => e.type === "VElement" && e.name === "script");
|
|
152
|
+
return scripts.some((node) => node.startTag.attributes.some((i) => i.key.name = "setup"));
|
|
153
|
+
}
|
|
154
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
155
|
+
0 && (module.exports = {
|
|
156
|
+
callNameNotExist,
|
|
157
|
+
canCollectOperateNodes,
|
|
158
|
+
createI18nIgnoreChecker,
|
|
159
|
+
getI18nCallMethod,
|
|
160
|
+
getInjectNode,
|
|
161
|
+
getLastImportOrFirstNotImportNode,
|
|
162
|
+
getLeadingSpaces,
|
|
163
|
+
isScriptSetup
|
|
164
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/eslint/vue/report_and_fix.ts
|
|
30
|
+
var report_and_fix_exports = {};
|
|
31
|
+
__export(report_and_fix_exports, {
|
|
32
|
+
batchFixAll: () => batchFixAll
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(report_and_fix_exports);
|
|
35
|
+
var import_i18n_utils = require("./i18n_utils.js");
|
|
36
|
+
var import_key_map = __toESM(require("../../key_map.js"));
|
|
37
|
+
var keyMapper = import_key_map.default.getInstance();
|
|
38
|
+
function generateReplaceKeyFixFun(context, node, codeType) {
|
|
39
|
+
const { callMethodName } = context.settings.i18nInfo[codeType] || {};
|
|
40
|
+
if (!callMethodName) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
switch (node.type) {
|
|
44
|
+
case "VText":
|
|
45
|
+
return (fixer) => {
|
|
46
|
+
const value = node.value;
|
|
47
|
+
const key = keyMapper.generateKey(value);
|
|
48
|
+
const replaceStr = value.replace(value.trim(), `{{ ${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, key)} }}`);
|
|
49
|
+
return fixer.replaceTextRange(node.range, replaceStr);
|
|
50
|
+
};
|
|
51
|
+
case "VAttribute":
|
|
52
|
+
return (fixer) => {
|
|
53
|
+
const value = node.value.value;
|
|
54
|
+
const key = keyMapper.generateKey(value);
|
|
55
|
+
const afterReplaceKey = value.replace(value.trim(), key);
|
|
56
|
+
const replaceStr = `:${node.key.name}="${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, afterReplaceKey)}"`;
|
|
57
|
+
return fixer.replaceTextRange(node.range, replaceStr);
|
|
58
|
+
};
|
|
59
|
+
case "TemplateLiteral":
|
|
60
|
+
return (fixer) => {
|
|
61
|
+
const str = node.quasis.map((q, index) => q.tail ? q.value.cooked : `${q.value.cooked}{arg${index + 1}}`).join("");
|
|
62
|
+
const key = keyMapper.generateKey(str);
|
|
63
|
+
const expressionsArr = node.expressions;
|
|
64
|
+
if (expressionsArr.length) {
|
|
65
|
+
const paramsStr = expressionsArr.map((i, index) => `arg${index + 1}: ${context.sourceCode.getText(i)}`).join(", ");
|
|
66
|
+
const value = context.sourceCode.getText(node);
|
|
67
|
+
const afterReplaceKey = value.replace(value.trim(), key);
|
|
68
|
+
const replaceStr2 = `${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, afterReplaceKey, paramsStr)}`;
|
|
69
|
+
return fixer.replaceTextRange(node.range, replaceStr2);
|
|
70
|
+
}
|
|
71
|
+
const replaceStr = `${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, key)}`;
|
|
72
|
+
return fixer.replaceTextRange(node.range, replaceStr);
|
|
73
|
+
};
|
|
74
|
+
case "Literal":
|
|
75
|
+
return (fixer) => {
|
|
76
|
+
const value = node.value;
|
|
77
|
+
const key = keyMapper.generateKey(value);
|
|
78
|
+
const str = node.parent.type === "JSXAttribute" ? `{ ${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, key)} }` : `${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, key)}`;
|
|
79
|
+
const replaceStr = value.replace(value.trim(), str);
|
|
80
|
+
return fixer.replaceTextRange(node.range, replaceStr);
|
|
81
|
+
};
|
|
82
|
+
case "JSXText":
|
|
83
|
+
return (fixer) => {
|
|
84
|
+
const value = node.value;
|
|
85
|
+
const key = keyMapper.generateKey(value);
|
|
86
|
+
const replaceStr = value.replace(value.trim(), `{ ${(0, import_i18n_utils.getI18nCallMethod)(callMethodName, key)} }`);
|
|
87
|
+
return fixer.replaceTextRange(node.range, replaceStr);
|
|
88
|
+
};
|
|
89
|
+
default:
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function generateInjectImportFixFun(context, codeType) {
|
|
94
|
+
const { injectImport } = context.settings.i18nInfo[codeType] || {};
|
|
95
|
+
if (!injectImport) return null;
|
|
96
|
+
return (fixer) => {
|
|
97
|
+
const res = (0, import_i18n_utils.getLastImportOrFirstNotImportNode)(context);
|
|
98
|
+
if (!res) return null;
|
|
99
|
+
const { node: injectNode, type } = res;
|
|
100
|
+
const indent = (0, import_i18n_utils.getLeadingSpaces)(context, injectNode);
|
|
101
|
+
if (type === "firstNonImport") {
|
|
102
|
+
return fixer.insertTextBeforeRange(injectNode.range, `${injectImport}
|
|
103
|
+
${indent}`);
|
|
104
|
+
}
|
|
105
|
+
return fixer.insertTextAfterRange(injectNode.range, `
|
|
106
|
+
${indent}${injectImport}`);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function generateInjectCodeToTopLevelFixFun(context, codeType) {
|
|
110
|
+
const { injectCodeToTopLevel } = context.settings.i18nInfo[codeType] || {};
|
|
111
|
+
if (!injectCodeToTopLevel) return null;
|
|
112
|
+
return (fixer) => {
|
|
113
|
+
const res = (0, import_i18n_utils.getLastImportOrFirstNotImportNode)(context);
|
|
114
|
+
if (!res) return null;
|
|
115
|
+
const { node: injectNode, type } = res;
|
|
116
|
+
const indent = (0, import_i18n_utils.getLeadingSpaces)(context, injectNode);
|
|
117
|
+
if (type === "firstNonImport") {
|
|
118
|
+
return fixer.insertTextBeforeRange(injectNode.range, `${injectCodeToTopLevel}
|
|
119
|
+
${indent}`);
|
|
120
|
+
}
|
|
121
|
+
return fixer.insertTextAfterRange(injectNode.range, `
|
|
122
|
+
${indent}${injectCodeToTopLevel}`);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function generateInjectHooksCallFixFun(context, node, codeType) {
|
|
126
|
+
const { injectHooksCall } = context.settings.i18nInfo[codeType] || {};
|
|
127
|
+
if (!injectHooksCall) return null;
|
|
128
|
+
return (fixer) => {
|
|
129
|
+
const blockStatementNode = (node == null ? void 0 : node.type) === "Program" ? node : node == null ? void 0 : node.body;
|
|
130
|
+
const injectNode = (0, import_i18n_utils.getInjectNode)(context, blockStatementNode);
|
|
131
|
+
const indent = (0, import_i18n_utils.getLeadingSpaces)(context, injectNode);
|
|
132
|
+
return fixer.insertTextBeforeRange(injectNode.range, `${injectHooksCall}
|
|
133
|
+
${indent}`);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function batchFixAll(context, state) {
|
|
137
|
+
const allNodeList = [...state.templateTextNodeList, ...state.scriptTextNodeList, ...state.jsTextNodeList, ...state.jsxTextNodeList];
|
|
138
|
+
if (!allNodeList.length) return;
|
|
139
|
+
const textNodeFixes = [];
|
|
140
|
+
const injectImportFixes = [];
|
|
141
|
+
const injectCodeToTopLevelFixes = [];
|
|
142
|
+
const injectHooksCallFixes = [];
|
|
143
|
+
const injectFlag = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const item of allNodeList) {
|
|
145
|
+
textNodeFixes.push(generateReplaceKeyFixFun(context, item.textNode, item.inSetup ? "scriptSetup" : item.codeType));
|
|
146
|
+
switch (item.codeType) {
|
|
147
|
+
case "script":
|
|
148
|
+
if (item.inSetup) {
|
|
149
|
+
if ((0, import_i18n_utils.callNameNotExist)(context, item.setUpNode, "scriptSetup") && !injectFlag.has("scriptSetup")) {
|
|
150
|
+
injectFlag.set("scriptSetup", true);
|
|
151
|
+
injectHooksCallFixes.push(generateInjectHooksCallFixFun(context, item.setUpNode, "scriptSetup"));
|
|
152
|
+
injectImportFixes.push(generateInjectImportFixFun(context, "scriptSetup"));
|
|
153
|
+
injectCodeToTopLevelFixes.push(generateInjectCodeToTopLevelFixFun(context, "scriptSetup"));
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if ((0, import_i18n_utils.callNameNotExist)(context, context.sourceCode.ast, "script") && !injectFlag.has("script")) {
|
|
158
|
+
injectFlag.set("script", true);
|
|
159
|
+
injectImportFixes.push(generateInjectImportFixFun(context, "script"));
|
|
160
|
+
injectCodeToTopLevelFixes.push(generateInjectCodeToTopLevelFixFun(context, "script"));
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
case "jsx":
|
|
164
|
+
if (item.inSetup) {
|
|
165
|
+
if ((0, import_i18n_utils.callNameNotExist)(context, item.setUpNode, "scriptSetup") && !injectFlag.has("scriptSetup")) {
|
|
166
|
+
injectFlag.set("scriptSetup", true);
|
|
167
|
+
injectHooksCallFixes.push(generateInjectHooksCallFixFun(context, item.setUpNode, "scriptSetup"));
|
|
168
|
+
injectImportFixes.push(generateInjectImportFixFun(context, "scriptSetup"));
|
|
169
|
+
injectCodeToTopLevelFixes.push(generateInjectCodeToTopLevelFixFun(context, "scriptSetup"));
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
if ((0, import_i18n_utils.callNameNotExist)(context, context.sourceCode.ast, "jsx") && !injectFlag.has("jsx")) {
|
|
174
|
+
injectFlag.set("jsx", true);
|
|
175
|
+
injectImportFixes.push(generateInjectImportFixFun(context, "jsx"));
|
|
176
|
+
injectCodeToTopLevelFixes.push(generateInjectCodeToTopLevelFixFun(context, "jsx"));
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case "js":
|
|
180
|
+
if ((0, import_i18n_utils.callNameNotExist)(context, context.sourceCode.ast, "js") && !injectFlag.has("js")) {
|
|
181
|
+
injectFlag.set("js", true);
|
|
182
|
+
injectImportFixes.push(generateInjectImportFixFun(context, "js"));
|
|
183
|
+
injectCodeToTopLevelFixes.push(generateInjectCodeToTopLevelFixFun(context, "js"));
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const fixes = [...textNodeFixes, ...injectImportFixes, ...injectCodeToTopLevelFixes, ...injectHooksCallFixes].filter((item) => item !== null);
|
|
191
|
+
if (!fixes.length) return;
|
|
192
|
+
context.report({
|
|
193
|
+
loc: context.sourceCode.ast.loc,
|
|
194
|
+
message: "存在中文字符,可以进行国际化替换",
|
|
195
|
+
fix: (fixer) => {
|
|
196
|
+
return fixes.map((f) => f(fixer)).filter(Boolean);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
201
|
+
0 && (module.exports = {
|
|
202
|
+
batchFixAll
|
|
203
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/file_processor_utils.ts
|
|
30
|
+
var file_processor_utils_exports = {};
|
|
31
|
+
__export(file_processor_utils_exports, {
|
|
32
|
+
fileExists: () => fileExists,
|
|
33
|
+
getFileContent: () => getFileContent,
|
|
34
|
+
getFileType: () => getFileType,
|
|
35
|
+
getModifiedFiles: () => getModifiedFiles,
|
|
36
|
+
isDescendant: () => isDescendant,
|
|
37
|
+
readFile: () => readFile,
|
|
38
|
+
writeFile: () => writeFile,
|
|
39
|
+
writeI18nFile: () => writeI18nFile
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(file_processor_utils_exports);
|
|
42
|
+
var import_node_fs = require("node:fs");
|
|
43
|
+
var import_node_path = __toESM(require("node:path"));
|
|
44
|
+
var import_const = require("./assets/const.js");
|
|
45
|
+
var import_json5 = __toESM(require("json5"));
|
|
46
|
+
var import_simple_git = require("simple-git");
|
|
47
|
+
async function getModifiedFiles() {
|
|
48
|
+
const raw = await (0, import_simple_git.simpleGit)().diff(["--cached", "--name-only", "--diff-filter=AM"]);
|
|
49
|
+
const files = raw.split("\n").filter(Boolean);
|
|
50
|
+
return files.map((item) => import_node_path.default.resolve(item));
|
|
51
|
+
}
|
|
52
|
+
async function readFile(filePath) {
|
|
53
|
+
return await import_node_fs.promises.readFile(filePath, "utf8");
|
|
54
|
+
}
|
|
55
|
+
function getFileType(filePath) {
|
|
56
|
+
return import_node_path.default.extname(filePath).slice(1);
|
|
57
|
+
}
|
|
58
|
+
async function writeFile(filePath, content) {
|
|
59
|
+
await import_node_fs.promises.writeFile(import_node_path.default.resolve(filePath), content, "utf8");
|
|
60
|
+
}
|
|
61
|
+
async function fileExists(filePath) {
|
|
62
|
+
return Promise.resolve(import_node_fs.promises.stat(import_node_path.default.resolve(filePath)).then(() => true).catch(() => false));
|
|
63
|
+
}
|
|
64
|
+
async function writeI18nFile(filePath, content) {
|
|
65
|
+
const fileType = getFileType(filePath);
|
|
66
|
+
if (!["js", "ts", "json"].includes(fileType)) {
|
|
67
|
+
throw new Error(`国际化语言文件${filePath}}仅支持 JS、TS、JSON格式`);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const { exportPrefix, code } = await getFileContent(filePath, fileType);
|
|
71
|
+
const allContent = Object.assign(code, content);
|
|
72
|
+
await writeFile(filePath, `${exportPrefix} ${JSON.stringify(allContent, null, 2)}`);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(`国际化语言文件${filePath}写入内容失败,${error}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function getFileContent(filePath, fileType) {
|
|
78
|
+
const fullPath = import_node_path.default.resolve(filePath);
|
|
79
|
+
const isExist = await fileExists(filePath);
|
|
80
|
+
const exportPrefix = fileType === "json" ? "" : "export default";
|
|
81
|
+
if (!isExist) {
|
|
82
|
+
return { exportPrefix, code: {} };
|
|
83
|
+
}
|
|
84
|
+
const code = await readFile(fullPath);
|
|
85
|
+
const result = code.match(import_const.checkReg);
|
|
86
|
+
if (!result) {
|
|
87
|
+
return { exportPrefix, code: {} };
|
|
88
|
+
}
|
|
89
|
+
if (fileType === "json") {
|
|
90
|
+
return { exportPrefix: "", code: JSON.parse(code) };
|
|
91
|
+
}
|
|
92
|
+
return { exportPrefix: result[1], code: import_json5.default.parse(result[2]) };
|
|
93
|
+
}
|
|
94
|
+
function isDescendant(file, dir) {
|
|
95
|
+
const f = import_node_path.default.resolve(file).toLowerCase();
|
|
96
|
+
const d = import_node_path.default.resolve(dir).toLowerCase();
|
|
97
|
+
return f !== d && f.startsWith(d + "/");
|
|
98
|
+
}
|
|
99
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
100
|
+
0 && (module.exports = {
|
|
101
|
+
fileExists,
|
|
102
|
+
getFileContent,
|
|
103
|
+
getFileType,
|
|
104
|
+
getModifiedFiles,
|
|
105
|
+
isDescendant,
|
|
106
|
+
readFile,
|
|
107
|
+
writeFile,
|
|
108
|
+
writeI18nFile
|
|
109
|
+
});
|
package/lib/i18n_easy.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/i18n_easy.ts
|
|
30
|
+
var i18n_easy_exports = {};
|
|
31
|
+
__export(i18n_easy_exports, {
|
|
32
|
+
I18nEasy: () => I18nEasy
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(i18n_easy_exports);
|
|
35
|
+
var import_const = require("./assets/const.js");
|
|
36
|
+
var import_key_map = __toESM(require("./key_map.js"));
|
|
37
|
+
var import_ast_transformer = require("./ast_transformer.js");
|
|
38
|
+
var import_node_path = __toESM(require("node:path"));
|
|
39
|
+
var import_utils = require("./utils.js");
|
|
40
|
+
var import_file_processor_utils = require("./file_processor_utils.js");
|
|
41
|
+
var import_progressBus = require("./progressBus.js");
|
|
42
|
+
var import_translate = __toESM(require("./translate.js"));
|
|
43
|
+
var I18nEasy = class {
|
|
44
|
+
/**
|
|
45
|
+
* vue框架中将文件内容转换成i18n写法并翻译
|
|
46
|
+
* @param filePath 文件绝对路径
|
|
47
|
+
* @param options 配置项
|
|
48
|
+
*/
|
|
49
|
+
async fileToI18nVue(filePath, options) {
|
|
50
|
+
const { keyType = "pinyin", i18nInfo, customIgnore } = options;
|
|
51
|
+
const fileType = (0, import_file_processor_utils.getFileType)(filePath);
|
|
52
|
+
if (!(0, import_utils.isSupportFileType)(fileType, "vue")) {
|
|
53
|
+
throw new Error(`${filePath}出错了,不支持的文件类型:${fileType}`);
|
|
54
|
+
}
|
|
55
|
+
const fileContent = await (0, import_file_processor_utils.readFile)(filePath);
|
|
56
|
+
const isVueScriptSrc = await (0, import_utils.isVueScriptSrcImport)(filePath, fileType);
|
|
57
|
+
const astTransformer = new import_ast_transformer.AstTransformer();
|
|
58
|
+
const keyMapper = import_key_map.default.getInstance();
|
|
59
|
+
keyMapper.setKeyType(keyType);
|
|
60
|
+
keyMapper.clearCharMap();
|
|
61
|
+
const fixedResult = astTransformer.convertFileContent({
|
|
62
|
+
fileContent,
|
|
63
|
+
fileName: import_node_path.default.basename(filePath),
|
|
64
|
+
fileType: isVueScriptSrc ? "vue" : fileType,
|
|
65
|
+
type: "vue",
|
|
66
|
+
i18nInfo,
|
|
67
|
+
customIgnore
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
filePath,
|
|
71
|
+
oldFileContent: fileContent,
|
|
72
|
+
keyValueMap: Array.from(keyMapper.charList),
|
|
73
|
+
...fixedResult
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* react框架中将文件内容转换成i18n写法并翻译
|
|
78
|
+
* @param filePath 文件绝对路径
|
|
79
|
+
* @param options 配置项
|
|
80
|
+
*/
|
|
81
|
+
async fileToI18nReact(filePath, options) {
|
|
82
|
+
const { keyType = "pinyin", i18nInfo } = options;
|
|
83
|
+
const fileType = (0, import_file_processor_utils.getFileType)(filePath);
|
|
84
|
+
if (!(0, import_utils.isSupportFileType)(fileType, "react")) {
|
|
85
|
+
throw new Error(`${filePath}出错了,不支持的文件类型:${fileType}`);
|
|
86
|
+
}
|
|
87
|
+
const fileContent = await (0, import_file_processor_utils.readFile)(filePath);
|
|
88
|
+
const astTransformer = new import_ast_transformer.AstTransformer();
|
|
89
|
+
const keyMapper = import_key_map.default.getInstance();
|
|
90
|
+
keyMapper.setKeyType(keyType);
|
|
91
|
+
keyMapper.clearCharMap();
|
|
92
|
+
const fixedResult = astTransformer.convertFileContent({
|
|
93
|
+
fileContent,
|
|
94
|
+
fileName: import_node_path.default.basename(filePath),
|
|
95
|
+
fileType,
|
|
96
|
+
type: "react",
|
|
97
|
+
i18nInfo
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
filePath,
|
|
101
|
+
oldFileContent: fileContent,
|
|
102
|
+
keyValueMap: Array.from(keyMapper.charList),
|
|
103
|
+
...fixedResult
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 将文件内容转换成i18n写法
|
|
108
|
+
* @param filePath 文件绝对路径
|
|
109
|
+
* @param options 配置项
|
|
110
|
+
*/
|
|
111
|
+
async fileToI18n(filePath, options) {
|
|
112
|
+
const { type } = options;
|
|
113
|
+
if (type === "vue") {
|
|
114
|
+
return this.fileToI18nVue(filePath, options);
|
|
115
|
+
} else if (type === "react") {
|
|
116
|
+
return this.fileToI18nReact(filePath, options);
|
|
117
|
+
} else {
|
|
118
|
+
throw new Error(`不支持的框架类型:${type}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 将文件内容转换成i18n写法并翻译
|
|
123
|
+
* @param filePath 文件绝对路径
|
|
124
|
+
* @param options 配置项
|
|
125
|
+
*/
|
|
126
|
+
async fileToI18nAndTranslate(filePath, options) {
|
|
127
|
+
const { type, keyType = "pinyin", i18nInfo, lang, apiKey } = options;
|
|
128
|
+
const result = await this.fileToI18n(filePath, { type, keyType, i18nInfo });
|
|
129
|
+
const translator = import_translate.default.getInstance();
|
|
130
|
+
translator.setApiKey(apiKey);
|
|
131
|
+
const arr = result.fixed ? result.keyValueMap : [];
|
|
132
|
+
const translateResult = await translator.translate(arr, lang);
|
|
133
|
+
return {
|
|
134
|
+
...result,
|
|
135
|
+
translateResult
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 将文件内容转换成i18n写法并翻译,支持批量
|
|
140
|
+
* @param filePath 文件绝对路径
|
|
141
|
+
* @param options 配置项
|
|
142
|
+
*/
|
|
143
|
+
async fileToI18nAndTranslateBatch(filePaths, options) {
|
|
144
|
+
const { type, keyType = "pinyin", i18nInfo, lang, apiKey, customIgnore } = options;
|
|
145
|
+
const result = await Promise.all(filePaths.map((filePath) => this.fileToI18n(filePath, { type, keyType, i18nInfo, customIgnore })));
|
|
146
|
+
const translator = import_translate.default.getInstance();
|
|
147
|
+
translator.setApiKey(apiKey);
|
|
148
|
+
const arr = result.filter((item) => item.fixed).flatMap((i) => i.keyValueMap);
|
|
149
|
+
const translateResult = await translator.translate(arr, lang);
|
|
150
|
+
return {
|
|
151
|
+
transformResult: result,
|
|
152
|
+
translateResult
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* @description 获取需要处理的文件信息
|
|
157
|
+
* @returns 文件信息
|
|
158
|
+
*/
|
|
159
|
+
async getPendingFiles() {
|
|
160
|
+
const filePaths = await (0, import_file_processor_utils.getModifiedFiles)();
|
|
161
|
+
if (filePaths.length === 0) {
|
|
162
|
+
(0, import_progressBus.log)(`暂存区暂无需要处理的文件`, "info");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const configContext = await (0, import_utils.searchFileContent)(import_node_path.default.resolve(), import_const.configFileNames);
|
|
166
|
+
if (!configContext) {
|
|
167
|
+
(0, import_progressBus.log)(`未在目录${import_node_path.default.resolve()}下找到配置文件`, "info");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const { apiKey, projects } = configContext;
|
|
171
|
+
const result = projects.map((item) => {
|
|
172
|
+
const projectPath = import_node_path.default.resolve(item.projectPath);
|
|
173
|
+
const files = filePaths.filter((filePath) => (0, import_file_processor_utils.isDescendant)(filePath, projectPath) && (0, import_utils.isSupportFileType)((0, import_file_processor_utils.getFileType)(filePath), item.type));
|
|
174
|
+
return { ...item, files, projectPath, apiKey };
|
|
175
|
+
});
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* @description 对git暂存区的文件进行i18n转换和翻译 并将翻译结果写入语言文件
|
|
180
|
+
* @returns {Promise<void>}
|
|
181
|
+
*/
|
|
182
|
+
async transformModifiedFiles() {
|
|
183
|
+
(0, import_progressBus.log)(`提示:对于vue框架,仅支持vue、js、jsx、ts、tsx文件,对于react框架;仅支持js、jsx、ts、tsx文件,其他文件类型将被忽略...
|
|
184
|
+
`, "info");
|
|
185
|
+
const pendingFiles = await this.getPendingFiles();
|
|
186
|
+
if (!pendingFiles) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
for (const item of pendingFiles) {
|
|
190
|
+
const { projectPath, files, apiKey, i18nInfo, lang, type, keyType, customIgnore } = item;
|
|
191
|
+
(0, import_progressBus.log)(`开始处理目录${projectPath}下文件...
|
|
192
|
+
`, "info");
|
|
193
|
+
const { translateResult, transformResult } = await this.fileToI18nAndTranslateBatch(files, { i18nInfo, apiKey, lang: lang.map((i) => i.name), type, keyType, customIgnore });
|
|
194
|
+
const fixed = transformResult.filter((item2) => item2.fixed);
|
|
195
|
+
const notFixed = transformResult.filter((item2) => !item2.fixed);
|
|
196
|
+
await Promise.all(fixed.map(({ filePath, newFileContent }) => (0, import_file_processor_utils.writeFile)(filePath, newFileContent)));
|
|
197
|
+
await Promise.all(lang.map(({ name, i18nFilePath }) => translateResult[name] ? (0, import_file_processor_utils.writeI18nFile)(i18nFilePath, translateResult[name]) : null).filter(Boolean));
|
|
198
|
+
(0, import_progressBus.log)(`目录${projectPath}下支持的文件已处理完成;共${files.length}个文件,已处理${fixed.length}个文件,未处理${notFixed.length}个文件...`, "success");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
203
|
+
0 && (module.exports = {
|
|
204
|
+
I18nEasy
|
|
205
|
+
});
|