td-octopus 0.0.1 → 0.0.3
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/import.js +5 -5
- package/package.json +2 -2
- package/src/import/index.js +10 -17
- package/src/translate/index.js +22 -14
- package/src/utils/const.js +3 -3
- package/src/utils/translate.js +122 -8
package/cmds/import.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { importExcel } = require('../src/import');
|
|
2
2
|
|
|
3
|
-
exports.command = 'import
|
|
3
|
+
exports.command = 'import';
|
|
4
4
|
|
|
5
|
-
exports.describe = 'import
|
|
5
|
+
exports.describe = 'import 将excel中人工翻译的部分替换未翻译的key';
|
|
6
6
|
|
|
7
|
-
exports.handler = (
|
|
8
|
-
|
|
7
|
+
exports.handler = () => {
|
|
8
|
+
importExcel()
|
|
9
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "td-octopus",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "I18N tool",
|
|
5
5
|
"author": "Anthony Li",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@babel/core": "^7.18.2",
|
|
12
|
-
"baidu-translate": "^1.
|
|
12
|
+
"baidu-translate": "^1.3.0",
|
|
13
13
|
"colors": "^1.4.0",
|
|
14
14
|
"glob": "^8.0.3",
|
|
15
15
|
"google-translate": "^3.0.0",
|
package/src/import/index.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @Description:
|
|
2
|
+
* @Description: 讲excel的内容导入会json
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-26 11:29:33
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime: 2022-06
|
|
6
|
+
* @LastEditTime: 2022-07-06 14:24:58
|
|
7
7
|
*/
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
8
|
const {
|
|
11
9
|
otpPath,
|
|
12
10
|
flatObject,
|
|
@@ -16,30 +14,24 @@ const {
|
|
|
16
14
|
} = require('../utils/translate');
|
|
17
15
|
const syncLang = require('../utils/syncLang')
|
|
18
16
|
const { getProjectConfig } = require('../utils/index')
|
|
19
|
-
|
|
17
|
+
const ora = require('ora');
|
|
20
18
|
const { OCTOPUS_CONFIG_FILE } = require('../utils/const')
|
|
21
19
|
|
|
20
|
+
const spinner = ora('开始import');
|
|
22
21
|
// 人工翻译后对象
|
|
23
|
-
function
|
|
22
|
+
function importExcel() {
|
|
24
23
|
(async () => {
|
|
25
|
-
const list = str ? str.split(',') : undefined;
|
|
26
24
|
const otpConfig = getProjectConfig()
|
|
27
25
|
const distLang = otpConfig && otpConfig.distLangs
|
|
28
26
|
|
|
29
|
-
if (
|
|
30
|
-
console.log('参数必须为各语言用逗号隔开,例如en-US,zh-TW')
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (!str && !Array.isArray(distLang)) {
|
|
27
|
+
if (!Array.isArray(distLang)) {
|
|
34
28
|
console.log(`请配置${OCTOPUS_CONFIG_FILE}里面的distLangs`)
|
|
35
29
|
return;
|
|
36
30
|
}
|
|
37
31
|
|
|
38
32
|
await syncLang();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
arr.forEach((lang) => {
|
|
33
|
+
spinner.start('正在从excel开始同步')
|
|
34
|
+
distLang.forEach((lang) => {
|
|
43
35
|
parseExcel(otpPath + `/${lang}/translate_${lang}.xls`, function (translateMap) {
|
|
44
36
|
const { default: currentLangMap } = require(`../temp/${lang}`);
|
|
45
37
|
const langFlat = flatObject(currentLangMap);
|
|
@@ -54,9 +46,10 @@ function excel(str) {
|
|
|
54
46
|
rewriteFiles(fileKeyValueList, lang);
|
|
55
47
|
});
|
|
56
48
|
})
|
|
49
|
+
spinner.succeed('从excel同步成功')
|
|
57
50
|
})()
|
|
58
51
|
}
|
|
59
52
|
|
|
60
53
|
module.exports = {
|
|
61
|
-
|
|
54
|
+
importExcel
|
|
62
55
|
}
|
package/src/translate/index.js
CHANGED
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-01 13:56:18
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime: 2022-06
|
|
6
|
+
* @LastEditTime: 2022-07-06 14:21:43
|
|
7
7
|
*/
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
8
|
const {
|
|
11
9
|
syncFiles,
|
|
12
10
|
otpPath,
|
|
@@ -15,37 +13,47 @@ const {
|
|
|
15
13
|
generateExcel,
|
|
16
14
|
rewriteFiles,
|
|
17
15
|
} = require('../utils/translate');
|
|
18
|
-
const { getProjectConfig } = require('../utils/index')
|
|
19
16
|
const syncLang = require('../utils/syncLang')
|
|
17
|
+
const { getProjectConfig } = require('../utils/index')
|
|
18
|
+
const ora = require('ora');
|
|
19
|
+
const { OCTOPUS_CONFIG_FILE } = require('../utils/const')
|
|
20
|
+
|
|
21
|
+
const spinner = ora('开始translate');
|
|
20
22
|
|
|
21
23
|
// 同步不同的语言包
|
|
22
24
|
function translate() {
|
|
23
25
|
(async () => {
|
|
24
26
|
const otpConfig = getProjectConfig()
|
|
25
27
|
const distLang = otpConfig && otpConfig.distLangs
|
|
28
|
+
|
|
29
|
+
if (!Array.isArray(distLang)) {
|
|
30
|
+
console.log(`请配置${OCTOPUS_CONFIG_FILE}里面的distLangs`)
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
spinner.start('正在同步文件')
|
|
26
34
|
await syncFiles(distLang);
|
|
35
|
+
spinner.succeed('同步文件成功')
|
|
27
36
|
await syncLang();
|
|
28
37
|
|
|
29
38
|
const { default: zhCN } = require('../temp/zh-CN');
|
|
30
39
|
const zhCNflat = flatObject(zhCN);
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
console.log('请配置otp-config.json里面的distLangs')
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
distLang.forEach((lang) => {
|
|
41
|
+
for (const lang of distLang) {
|
|
38
42
|
const { default: currentLangMap } = require(`../temp/${lang}`);
|
|
39
43
|
const langFlat = flatObject(currentLangMap);
|
|
40
|
-
|
|
44
|
+
spinner.start('正在同步文件的key')
|
|
41
45
|
// 删除掉多余的key,增加新的key,同时提取没有翻译过的key的列表
|
|
42
|
-
const { fileKeyValueList, addList } = getAdjustLangObjAndAddList(langFlat, zhCNflat);
|
|
43
|
-
|
|
46
|
+
const { fileKeyValueList, addList } = await getAdjustLangObjAndAddList(lang, langFlat, zhCNflat);
|
|
47
|
+
spinner.succeed('同步文件的key成功')
|
|
44
48
|
// 重写文件
|
|
45
49
|
rewriteFiles(fileKeyValueList, lang);
|
|
50
|
+
spinner.start(`正在生成${lang} excel`)
|
|
46
51
|
// 生成excel
|
|
47
52
|
generateExcel(addList, otpPath + '/' + lang, lang);
|
|
48
|
-
|
|
53
|
+
spinner.succeed(`生成${lang} excel成功`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
spinner.stop('translate成功')
|
|
49
57
|
})()
|
|
50
58
|
}
|
|
51
59
|
|
package/src/utils/const.js
CHANGED
package/src/utils/translate.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @Description:
|
|
2
|
+
* @Description: 翻译公用方法
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-02 10:09:01
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime: 2022-06
|
|
6
|
+
* @LastEditTime: 2022-07-06 11:24:00
|
|
7
7
|
*/
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const baiduTranslate = require('baidu-translate');
|
|
10
11
|
const shell = require('shelljs');
|
|
11
12
|
const _ = require('lodash');
|
|
12
13
|
const XLSX = require('xlsx');
|
|
13
|
-
const {
|
|
14
|
+
const { getProjectConfig } = require('../utils/index');
|
|
14
15
|
|
|
15
|
-
const otpPath = path.resolve(process.cwd(),
|
|
16
|
+
const otpPath = path.resolve(process.cwd(), getProjectConfig().otpDir);
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* 判断对象是否为纯对象
|
|
@@ -129,12 +130,12 @@ function getFileKeyValueList(adjustLangObj) {
|
|
|
129
130
|
* @param {*} zhCNObj zh-CN 的key/value
|
|
130
131
|
* @returns { fileKeyValueList: [{fileName: a, value: {"b.c": xx}}], addList: [["a.b.c": "dd"]] }
|
|
131
132
|
*/
|
|
132
|
-
function getAdjustLangObjAndAddList(langObj = {}, zhCNObj = {}) {
|
|
133
|
+
async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
133
134
|
const langObjKeys = Object.keys(langObj);
|
|
134
135
|
// 调整后的语言包key/value
|
|
135
136
|
const adjustLangObj = {};
|
|
136
137
|
// 需要新增的key/value
|
|
137
|
-
const
|
|
138
|
+
const needAddList = [];
|
|
138
139
|
// 循环zh-CN的key,得到当前语言包的key
|
|
139
140
|
for (let key in zhCNObj) {
|
|
140
141
|
// 4种情况下,需要将key放入到翻译对象里面去
|
|
@@ -143,18 +144,131 @@ function getAdjustLangObjAndAddList(langObj = {}, zhCNObj = {}) {
|
|
|
143
144
|
// 3: 语言包的key的value值为""
|
|
144
145
|
// 4: 语言包的key的value和zh-CN的key的value一样
|
|
145
146
|
if (!langObjKeys.includes(key) || typeof langObj[key] !== 'string' || langObj[key] === '' || langObj[key] === zhCNObj[key]) {
|
|
146
|
-
|
|
147
|
+
needAddList.push([key, zhCNObj[key], '', '']);
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
adjustLangObj[key] = langObj[key] || zhCNObj[key];
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
const addList = await combinText(needAddList, lang);
|
|
154
|
+
|
|
152
155
|
return {
|
|
153
156
|
fileKeyValueList: getFileKeyValueList(adjustLangObj),
|
|
154
157
|
addList
|
|
155
158
|
};
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
/**
|
|
162
|
+
* 合并需要翻译的中文,因为百度翻译限制一次性中文只能有3000字
|
|
163
|
+
* 因为百度免费翻译有时候会抽风,会导致翻译结果出错,为了减少没被翻译的,所以现在设置一次性翻译200字
|
|
164
|
+
* @param {*} needAddList
|
|
165
|
+
*/
|
|
166
|
+
async function combinText(needAddList, lang) {
|
|
167
|
+
const otpConfig = getProjectConfig()
|
|
168
|
+
const { baiduApiKey, baiduLangMap } = otpConfig || {}
|
|
169
|
+
const { appId, appKey } = baiduApiKey || {}
|
|
170
|
+
const toLang = baiduLangMap?.[lang] || '';
|
|
171
|
+
|
|
172
|
+
if (!Array.isArray(needAddList)) {
|
|
173
|
+
return []
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 如果不配置百度翻译就直接返回
|
|
177
|
+
if (!appId || !appKey || !toLang) {
|
|
178
|
+
return needAddList;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 分组
|
|
182
|
+
const groupList = groupByLength(needAddList, 200)
|
|
183
|
+
// 分组翻译结果
|
|
184
|
+
const transformResultList = await getTransformResultList(groupList, appId, appKey, 'zh', toLang)
|
|
185
|
+
|
|
186
|
+
return needAddList.map((i, index) => {
|
|
187
|
+
i[2] = transformResultList[index];
|
|
188
|
+
return i
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 对分组的结果进行翻译
|
|
194
|
+
* @param {*} groupList
|
|
195
|
+
* @param {*} appId
|
|
196
|
+
* @param {*} appKey
|
|
197
|
+
* @param {*} fromLang
|
|
198
|
+
* @param {*} toLang
|
|
199
|
+
* @returns
|
|
200
|
+
*/
|
|
201
|
+
async function getTransformResultList(groupList, appId, appKey, fromLang, toLang) {
|
|
202
|
+
let translatList = []
|
|
203
|
+
for (const i of groupList) {
|
|
204
|
+
const baiduResult = await baiduTranslation(appId, appKey, fromLang, toLang, JSON.stringify(i))
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const splitBaiduResult = baiduResult?.[0]
|
|
208
|
+
const transResultList = JSON.parse(splitBaiduResult);
|
|
209
|
+
// 这里是判断百度翻译返回的长度是不是和传入的一样
|
|
210
|
+
const reduceNum = i.length - transResultList.length;
|
|
211
|
+
translatList = [...translatList, ...transResultList].concat(Array(reduceNum).fill(''))
|
|
212
|
+
|
|
213
|
+
} catch (e) {
|
|
214
|
+
console.log(`百度翻译出现部分失败, 失败原因: ${e.message}`)
|
|
215
|
+
translatList = translatList.concat(Array(i.length).fill(''))
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return translatList
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 对数组进行分组
|
|
224
|
+
* @param {*} groupList 原数组
|
|
225
|
+
* @param {*} max 最大多少字
|
|
226
|
+
* @returns
|
|
227
|
+
*/
|
|
228
|
+
function groupByLength(groupList, max) {
|
|
229
|
+
const list = [[]]
|
|
230
|
+
let str = ''
|
|
231
|
+
// 变成${max}个字符一组的数组,用于一次百度翻译
|
|
232
|
+
groupList.forEach((it) => {
|
|
233
|
+
let [, text] = it;
|
|
234
|
+
|
|
235
|
+
if ((str + text).length < max) {
|
|
236
|
+
list[list.length - 1] = Array.isArray(list[list.length - 1]) ? [...list[list.length - 1], text] : [text]
|
|
237
|
+
str = str + text
|
|
238
|
+
} else {
|
|
239
|
+
list.push([text])
|
|
240
|
+
str = ''
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
return list
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 百度翻译
|
|
248
|
+
* @param {*} from
|
|
249
|
+
* @param {*} to
|
|
250
|
+
* @param {*} text
|
|
251
|
+
* @returns
|
|
252
|
+
*/
|
|
253
|
+
async function baiduTranslation(appId, appKey, from, to, text) {
|
|
254
|
+
return new Promise((resolve, reject) => {
|
|
255
|
+
// 延迟1000ms是因为百度翻译对调用频率有限制
|
|
256
|
+
setTimeout(() => {
|
|
257
|
+
return baiduTranslate(appId, appKey, to, from)(text)
|
|
258
|
+
.then(data => {
|
|
259
|
+
if (data && data.trans_result) {
|
|
260
|
+
const result = data.trans_result.map(item => item.dst) || [];
|
|
261
|
+
resolve(result);
|
|
262
|
+
} else {
|
|
263
|
+
resolve('[]')
|
|
264
|
+
}
|
|
265
|
+
}).catch(err => {
|
|
266
|
+
reject('[]');
|
|
267
|
+
});
|
|
268
|
+
}, 1000)
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
|
|
158
272
|
/**
|
|
159
273
|
* 转换数据结构 {"a.b.c": "测试"} ==> {a: {b: {c: "测试"}}}
|
|
160
274
|
* @param {*} adjustLangObj
|
|
@@ -173,7 +287,7 @@ function getDistRst(adjustLangObj) {
|
|
|
173
287
|
* @param {*} path 生成的路径
|
|
174
288
|
*/
|
|
175
289
|
function generateExcel(addList, path, lang) {
|
|
176
|
-
const excleData = [['需要翻译的字段', '中文', '人工翻译'], ...addList];
|
|
290
|
+
const excleData = [['需要翻译的字段', '中文', '百度翻译', '人工翻译'], ...addList];
|
|
177
291
|
|
|
178
292
|
const options = {
|
|
179
293
|
'!cols': [{ wpx: 100 }, { wpx: 100 }, { wpx: 100 }]
|