td-octopus 0.1.19 → 0.2.2
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/cmds/insertEn.js +9 -0
- package/cmds/pop.js +9 -0
- package/cmds/stash.js +9 -0
- package/cmds/transLang.js +10 -0
- package/package.json +11 -4
- package/src/insertEn/index.js +41 -0
- package/src/transLang/index.js +38 -0
- package/src/transLang/single.js +234 -0
- package/src/utils/translate.js +37 -1
- package/cmds/check.js +0 -9
- package/cmds/storage.js +0 -9
- /package/src/{check → pop}/index.js +0 -0
- /package/src/{storage → stash}/index.js +0 -0
package/cmds/insertEn.js
ADDED
package/cmds/pop.js
ADDED
package/cmds/stash.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "td-octopus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "I18N tool",
|
|
5
5
|
"author": "Anthony Li",
|
|
6
6
|
"bin": {
|
|
@@ -8,10 +8,18 @@
|
|
|
8
8
|
},
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@babel/core": "^7.
|
|
11
|
+
"@babel/core": "^7.28.6",
|
|
12
|
+
"@babel/parser": "^7.28.6",
|
|
13
|
+
"@babel/traverse": "^7.28.6",
|
|
14
|
+
"@babel/types": "^7.28.6",
|
|
12
15
|
"axios": "^1.6.5",
|
|
13
16
|
"baidu-translate": "^1.3.0",
|
|
14
17
|
"colors": "^1.4.0",
|
|
18
|
+
"eslint": "^8.15.0",
|
|
19
|
+
"eslint-config-tongdun": "^1.1.11",
|
|
20
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
21
|
+
"eslint-plugin-td-rules-plugin": "^1.0.1",
|
|
22
|
+
"eslint-plugin-unused-imports": "^4.3.0",
|
|
15
23
|
"glob": "^8.0.3",
|
|
16
24
|
"google-translate": "^3.0.0",
|
|
17
25
|
"inquirer": "^8.2.4",
|
|
@@ -27,8 +35,7 @@
|
|
|
27
35
|
"update-notifier": "^4.1.0",
|
|
28
36
|
"vue-template-compiler": "^2.6.14",
|
|
29
37
|
"xlsx": "0.16.9",
|
|
30
|
-
"yargs": "^15.3.1"
|
|
31
|
-
"eslint": "^8.15.0"
|
|
38
|
+
"yargs": "^15.3.1"
|
|
32
39
|
},
|
|
33
40
|
"keywords": [
|
|
34
41
|
"I18N",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const {
|
|
4
|
+
otpPath,
|
|
5
|
+
flatObject,
|
|
6
|
+
getFileKeyValueList,
|
|
7
|
+
parseExcelArr,
|
|
8
|
+
generateExcel,
|
|
9
|
+
rewriteFiles
|
|
10
|
+
} = require('../utils/translate');
|
|
11
|
+
const syncLang = require('../utils/syncLang')
|
|
12
|
+
const { getProjectConfig } = require('../utils/index')
|
|
13
|
+
const ora = require('ora');
|
|
14
|
+
const { OCTOPUS_CONFIG_FILE } = require('../utils/const')
|
|
15
|
+
|
|
16
|
+
const spinner = ora('开始insetEn');
|
|
17
|
+
// 人工翻译后对象
|
|
18
|
+
function insertEn() {
|
|
19
|
+
(async () => {
|
|
20
|
+
const otpConfig = getProjectConfig()
|
|
21
|
+
const distLang = otpConfig && otpConfig.distLangs;
|
|
22
|
+
const obj = JSON.parse(fs.readFileSync(path.resolve(otpPath, '../transLang.json'), 'utf-8'));
|
|
23
|
+
|
|
24
|
+
if (!Array.isArray(distLang)) {
|
|
25
|
+
console.log(`请配置${OCTOPUS_CONFIG_FILE}里面的distLangs`)
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
parseExcelArr(otpPath + `/en-US/translate_en-US.xls`, function (list) {
|
|
29
|
+
let addList = Array.isArray(list) ? list.map(({key, value}) => {
|
|
30
|
+
const val = obj[key] ? String(obj[key]) : value;
|
|
31
|
+
return [key, '', '', val];
|
|
32
|
+
}) : [];
|
|
33
|
+
generateExcel(addList, otpPath + '/' + 'en-US', 'en-US');
|
|
34
|
+
})
|
|
35
|
+
spinner.succeed('从excel同步成功')
|
|
36
|
+
})()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
insertEn
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Description: 翻译
|
|
3
|
+
* @Author: 郑泳健
|
|
4
|
+
* @Date: 2022-06-01 13:56:18
|
|
5
|
+
* @LastEditors: 郑泳健
|
|
6
|
+
* @LastEditTime: 2026-01-26 18:39:38
|
|
7
|
+
*/
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const ora = require('ora');
|
|
11
|
+
const { getAllJsFiles, processFile } = require('./single')
|
|
12
|
+
const spinner = ora('开始transLang');
|
|
13
|
+
|
|
14
|
+
function transLang(dir) {
|
|
15
|
+
(async () => {
|
|
16
|
+
if(!dir){
|
|
17
|
+
spinner.fail('请指定需要转换的目录')
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
// ===== 全局 list 汇总 =====
|
|
21
|
+
const globalList = {};
|
|
22
|
+
const targetDir = path.resolve(process.cwd(), dir); // 要批量处理的根目录
|
|
23
|
+
spinner.start('正在转换')
|
|
24
|
+
const files = getAllJsFiles(targetDir);
|
|
25
|
+
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
await processFile(file, globalList);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fs.writeFileSync(path.join(process.cwd(), 'transLang.json'), JSON.stringify(globalList, null, 2), 'utf-8');
|
|
31
|
+
spinner.succeed('转换成功')
|
|
32
|
+
spinner.stop('translate成功')
|
|
33
|
+
})()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
transLang
|
|
38
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const parser = require('@babel/parser');
|
|
5
|
+
const traverse = require('@babel/traverse').default;
|
|
6
|
+
const generate = require('@babel/generator').default;
|
|
7
|
+
const t = require('@babel/types');
|
|
8
|
+
const { ESLint } = require('eslint');
|
|
9
|
+
const prettier = require('eslint-plugin-prettier');
|
|
10
|
+
const unusedImports = require('eslint-plugin-unused-imports');
|
|
11
|
+
|
|
12
|
+
// ===== 工具函数 =====
|
|
13
|
+
const astNodeToString = (node) => {
|
|
14
|
+
if (t.isStringLiteral(node)) {
|
|
15
|
+
return node.value;
|
|
16
|
+
}
|
|
17
|
+
if (t.isIdentifier(node)) {
|
|
18
|
+
return node.name;
|
|
19
|
+
}
|
|
20
|
+
if (t.isMemberExpression(node)) {
|
|
21
|
+
const objectStr = astNodeToString(node.object);
|
|
22
|
+
const propertyStr = astNodeToString(node.property);
|
|
23
|
+
if (objectStr === 'I18N') {
|
|
24
|
+
return propertyStr;
|
|
25
|
+
}
|
|
26
|
+
return `${objectStr}.${propertyStr}`;
|
|
27
|
+
}
|
|
28
|
+
return generate(node, { concise: true, comments: false }).code;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isKeyNamed(node, name) {
|
|
32
|
+
if (t.isIdentifier(node)) {
|
|
33
|
+
return node.name === name;
|
|
34
|
+
}
|
|
35
|
+
if (t.isStringLiteral(node)) {
|
|
36
|
+
return node.value === name;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ===== 单文件转换函数(接受 code 和 filePath)=====
|
|
42
|
+
const transformToChinese = (code, globalList) => {
|
|
43
|
+
let localList = {}; // 每个文件独立的 list
|
|
44
|
+
|
|
45
|
+
const ast = parser.parse(code, {
|
|
46
|
+
sourceType: 'module',
|
|
47
|
+
plugins: ['jsx', 'typescript']
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const langVariableNames = new Set();
|
|
51
|
+
|
|
52
|
+
// 第一阶段:找 getLanguage()
|
|
53
|
+
traverse(ast, {
|
|
54
|
+
VariableDeclarator(path) {
|
|
55
|
+
const { id, init } = path.node;
|
|
56
|
+
const a = t.isIdentifier(id) && t.isCallExpression(init) && t.isIdentifier(init.callee, { name: 'getLanguage' })
|
|
57
|
+
const b = t.isIdentifier(id) && t.isCallExpression(init) && t.isIdentifier(init.callee, { name: 'getLang' })
|
|
58
|
+
if (a || b) {
|
|
59
|
+
langVariableNames.add(id.name);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// 第二阶段:处理多语言对象 和 语言索引
|
|
65
|
+
traverse(ast, {
|
|
66
|
+
ObjectExpression(path) {
|
|
67
|
+
const props = path.node.properties;
|
|
68
|
+
if (props.length === 0) return;
|
|
69
|
+
|
|
70
|
+
const cnProp = props.find(
|
|
71
|
+
(prop) => t.isObjectProperty(prop) && !prop.computed && isKeyNamed(prop.key, 'cn') && !prop.method
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const enProp = props.find(
|
|
75
|
+
(prop) => t.isObjectProperty(prop) && !prop.computed && isKeyNamed(prop.key, 'en') && !prop.method
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// 仅当 cn 和 en 都是字符串字面量时,才记录映射
|
|
79
|
+
if (cnProp && enProp && t.isStringLiteral(cnProp.value) && t.isStringLiteral(enProp.value)) {
|
|
80
|
+
localList[cnProp.value.value] = enProp.value.value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (cnProp) {
|
|
84
|
+
path.replaceWith(cnProp.value); // 替换 { cn: ..., en: ... } 为 cn 值
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
MemberExpression(path) {
|
|
89
|
+
|
|
90
|
+
const node = path.node;
|
|
91
|
+
if (!node.computed) return;
|
|
92
|
+
|
|
93
|
+
// 处理 obj[langVar]
|
|
94
|
+
if (t.isIdentifier(node.property) && langVariableNames.has(node.property.name)) {
|
|
95
|
+
path.replaceWith(node.object);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 处理 obj[getLang()] 或 obj[getLanguage()]
|
|
100
|
+
if (
|
|
101
|
+
t.isCallExpression(node.property) &&
|
|
102
|
+
t.isIdentifier(node.property.callee) &&
|
|
103
|
+
['getLang', 'getLanguage'].includes(node.property.callee.name)
|
|
104
|
+
) {
|
|
105
|
+
path.replaceWith(node.object);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// 清理 getLang() 相关的变量声明和导入(保持不变)
|
|
111
|
+
Identifier(path) {
|
|
112
|
+
if (langVariableNames.has(path.node.name)) {
|
|
113
|
+
const parent = path.parentPath;
|
|
114
|
+
if (parent.isExpressionStatement()) {
|
|
115
|
+
parent.remove();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
VariableDeclarator(path) {
|
|
121
|
+
const { id } = path.node;
|
|
122
|
+
if (t.isIdentifier(id) && langVariableNames.has(id.name)) {
|
|
123
|
+
const parent = path.parentPath;
|
|
124
|
+
if (parent.isVariableDeclaration()) {
|
|
125
|
+
if (parent.node.declarations.length === 1) {
|
|
126
|
+
parent.remove();
|
|
127
|
+
} else {
|
|
128
|
+
path.remove();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 第三阶段:清理 getLanguage 导入
|
|
136
|
+
traverse(ast, {
|
|
137
|
+
ImportDeclaration(path) {
|
|
138
|
+
const specifiers = path.node.specifiers;
|
|
139
|
+
const newSpecifiers = specifiers.filter((spec) => {
|
|
140
|
+
if (t.isImportSpecifier(spec)) {
|
|
141
|
+
return !t.isIdentifier(spec.imported, { name: 'getLanguage' }) && !t.isIdentifier(spec.imported, { name: 'getLang' });
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (newSpecifiers.length === 0) {
|
|
147
|
+
path.remove();
|
|
148
|
+
} else if (newSpecifiers.length !== specifiers.length) {
|
|
149
|
+
path.node.specifiers = newSpecifiers;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const transformedCode = generate(ast, {
|
|
155
|
+
retainLines: true,
|
|
156
|
+
concise: false,
|
|
157
|
+
comments: false
|
|
158
|
+
}).code;
|
|
159
|
+
|
|
160
|
+
// 合并到全局 list
|
|
161
|
+
|
|
162
|
+
Object.assign(globalList, localList);
|
|
163
|
+
return transformedCode;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ===== ESLint 实例(复用)=====
|
|
167
|
+
const eslint = new ESLint({
|
|
168
|
+
fix: true,
|
|
169
|
+
cwd: process.cwd(),
|
|
170
|
+
useEslintrc: false,
|
|
171
|
+
plugins: {
|
|
172
|
+
'unused-imports': unusedImports
|
|
173
|
+
},
|
|
174
|
+
baseConfig: {
|
|
175
|
+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
|
176
|
+
plugins: ['prettier', 'unused-imports'],
|
|
177
|
+
rules: {
|
|
178
|
+
'unused-imports/no-unused-imports': 'error',
|
|
179
|
+
'prettier/prettier': 'error'
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
overrideConfig: {
|
|
183
|
+
extends: ["tongdun/react"],
|
|
184
|
+
plugins: ["td-rules-plugin"],
|
|
185
|
+
rules: {
|
|
186
|
+
'prettier/prettier': [
|
|
187
|
+
1,
|
|
188
|
+
{
|
|
189
|
+
endOfLine: 'auto',
|
|
190
|
+
printWidth: 140,
|
|
191
|
+
semi: true,
|
|
192
|
+
singleQuote: true,
|
|
193
|
+
tabWidth: 4,
|
|
194
|
+
trailingComma: 'none',
|
|
195
|
+
jsxBracketSameLine: true,
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ===== 递归读取所有 .js 文件 =====
|
|
203
|
+
const getAllJsFiles = (dir) => {
|
|
204
|
+
let results = [];
|
|
205
|
+
const files = fs.readdirSync(dir);
|
|
206
|
+
|
|
207
|
+
for (const file of files) {
|
|
208
|
+
const fullPath = path.join(dir, file);
|
|
209
|
+
const stat = fs.statSync(fullPath);
|
|
210
|
+
if (stat.isDirectory()) {
|
|
211
|
+
results = results.concat(getAllJsFiles(fullPath)); // 递归
|
|
212
|
+
} else if (file.endsWith('.js') && !['index.js'].includes(file)) {
|
|
213
|
+
results.push(fullPath);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return results;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ===== 处理单个文件 =====
|
|
221
|
+
const processFile = async (filePath, globalList) => {
|
|
222
|
+
const code = fs.readFileSync(filePath, 'utf-8');
|
|
223
|
+
let transformedCode = transformToChinese(code, globalList);
|
|
224
|
+
|
|
225
|
+
const results = await eslint.lintText(transformedCode, { filePath });
|
|
226
|
+
const finalCode = results[0].output || transformedCode;
|
|
227
|
+
|
|
228
|
+
fs.writeFileSync(filePath, finalCode, 'utf-8');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = {
|
|
232
|
+
processFile,
|
|
233
|
+
getAllJsFiles
|
|
234
|
+
}
|
package/src/utils/translate.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-02 10:09:01
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime:
|
|
6
|
+
* @LastEditTime: 2026-01-26 19:15:42
|
|
7
7
|
*/
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
@@ -424,6 +424,41 @@ function parseExcel(path, callback, isZhCN = false) {
|
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
function parseExcelArr (path, callback, isZhCN) {
|
|
428
|
+
if (!shell.test('-e', path)) {
|
|
429
|
+
console.log('当前目录下没有找到翻译.xls文件');
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const excelBinary = fs.readFileSync(path);
|
|
433
|
+
const excel = XLSX.read(excelBinary, {
|
|
434
|
+
type: 'buffer'
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const sheet0 = excel.Sheets[excel.SheetNames[0]];
|
|
439
|
+
const list = XLSX.utils.sheet_to_json(sheet0);
|
|
440
|
+
|
|
441
|
+
const translateMap = list.reduce((total, item) => {
|
|
442
|
+
const key = item['需要翻译的字段'];
|
|
443
|
+
let value
|
|
444
|
+
if (isZhCN) {
|
|
445
|
+
value = item['中文'];
|
|
446
|
+
} else {
|
|
447
|
+
value = item['人工翻译'];
|
|
448
|
+
}
|
|
449
|
+
total.push({
|
|
450
|
+
key,
|
|
451
|
+
value
|
|
452
|
+
})
|
|
453
|
+
return total;
|
|
454
|
+
}, []);
|
|
455
|
+
|
|
456
|
+
callback(translateMap);
|
|
457
|
+
} catch (e) {
|
|
458
|
+
console.log(e)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
427
462
|
/**
|
|
428
463
|
* 重写文件
|
|
429
464
|
* @param {*} langFlat
|
|
@@ -487,6 +522,7 @@ module.exports = {
|
|
|
487
522
|
getDistRst,
|
|
488
523
|
generateExcel,
|
|
489
524
|
parseExcel,
|
|
525
|
+
parseExcelArr,
|
|
490
526
|
rewriteFiles,
|
|
491
527
|
changeFileSuffix,
|
|
492
528
|
getNeedChangeNameFileList
|
package/cmds/check.js
DELETED
package/cmds/storage.js
DELETED
|
File without changes
|
|
File without changes
|