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.
@@ -0,0 +1,9 @@
1
+ const { insertEn } = require('../src/insertEn');
2
+
3
+ exports.command = 'insertEn';
4
+
5
+ exports.describe = 'insertEn 将constants/lang塞入到translate_en-US';
6
+
7
+ exports.handler = () => {
8
+ insertEn()
9
+ }
package/cmds/pop.js ADDED
@@ -0,0 +1,9 @@
1
+ const { main } = require('../src/pop');
2
+
3
+ exports.command = 'pop';
4
+
5
+ exports.describe = 'pop 将项目中缺少的key, 原始项目的.octopus复制到当前项目public目录下,会帮你自助将缺失的key补充上去';
6
+
7
+ exports.handler = () => {
8
+ main()
9
+ }
package/cmds/stash.js ADDED
@@ -0,0 +1,9 @@
1
+ const { main } = require('../src/stash');
2
+
3
+ exports.command = 'stash';
4
+
5
+ exports.describe = 'stash 将项目中用到的中英文存储到缓存中';
6
+
7
+ exports.handler = () => {
8
+ main()
9
+ }
@@ -0,0 +1,10 @@
1
+ const { transLang } = require('../src/transLang');
2
+
3
+ exports.command = 'transLang [dirPath]';
4
+
5
+ exports.describe = 'transLang 将某些工程里面constants/lang提取到.octopus';
6
+
7
+ exports.handler = (argv) => {
8
+ const { dirPath } = argv
9
+ transLang(dirPath)
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "td-octopus",
3
- "version": "0.1.19",
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.18.2",
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
+ }
@@ -3,7 +3,7 @@
3
3
  * @Author: 郑泳健
4
4
  * @Date: 2022-06-02 10:09:01
5
5
  * @LastEditors: 郑泳健
6
- * @LastEditTime: 2023-06-12 11:32:52
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
@@ -1,9 +0,0 @@
1
- const { main } = require('../src/check');
2
-
3
- exports.command = 'check';
4
-
5
- exports.describe = 'check 将项目中缺少的key, 原始项目的.octopus复制到当前项目public目录下,会帮你自助将缺失的key补充上去';
6
-
7
- exports.handler = () => {
8
- main()
9
- }
package/cmds/storage.js DELETED
@@ -1,9 +0,0 @@
1
- const { main } = require('../src/storage');
2
-
3
- exports.command = 'storage';
4
-
5
- exports.describe = 'storage 将项目中用到的中英文存储到缓存中';
6
-
7
- exports.handler = () => {
8
- main()
9
- }
File without changes
File without changes