td-octopus 0.0.18 → 0.0.21
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/package.json +1 -1
- package/src/back/index.js +4 -2
- package/src/export/index.js +3 -2
- package/src/extract/findChineseText.js +17 -9
- package/src/extract/replace.js +33 -1
- package/src/translate/index.js +3 -2
- package/src/utils/syncLang.js +2 -3
- package/src/utils/translate.js +45 -42
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ copyright 2022 同盾
|
|
|
49
49
|
"importI18N": "import I18N from 'src/utils/I18N';",
|
|
50
50
|
"include": [], // 需要翻译的目录&也可以命令行输入参数
|
|
51
51
|
"exclude": [], // 过滤目录&文件
|
|
52
|
-
"reservedKey": ["template", "case"] // js关键词以及I18N内置方法不能作为目录名,会统一添加td作为前缀
|
|
52
|
+
"reservedKey": ["template", "case"], // js关键词以及I18N内置方法不能作为目录名,会统一添加td作为前缀
|
|
53
|
+
"tabSize": number // 如果I18N.template内部有多个变量时,用于空格锁进的个数,默认为4
|
|
53
54
|
}
|
|
54
55
|
```
|
|
55
56
|

|
package/package.json
CHANGED
package/src/back/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-01 13:56:18
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime: 2023-01-
|
|
6
|
+
* @LastEditTime: 2023-01-18 15:25:47
|
|
7
7
|
*/
|
|
8
8
|
const path = require('path')
|
|
9
9
|
const fs = require('fs')
|
|
@@ -120,7 +120,7 @@ function getValueByI18N(filePath, str, flatObj) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const matchList = matchs.reduce((total, item) => {
|
|
123
|
-
if (!
|
|
123
|
+
if (!['I18N.template', 'I18N.setLang'].includes(item)) {
|
|
124
124
|
const text = flatObj[item.replace('I18N.', '')]
|
|
125
125
|
if (text) {
|
|
126
126
|
total.push({ key: item, value: text })
|
|
@@ -152,6 +152,8 @@ function getNeedRewriteFiles(filePath, flatObj) {
|
|
|
152
152
|
if (Array.isArray(matchList) && matchList.length) {
|
|
153
153
|
sum += matchList.length;
|
|
154
154
|
matchList.forEach(({ key, value }) => {
|
|
155
|
+
// 兼容翻译的文案中有\n \r \t 这种情况
|
|
156
|
+
value = value.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
155
157
|
const replaceVal = value && value.includes("'") ? '"' + value + '"' : "'" + value + "'"
|
|
156
158
|
code = code.replace(key, replaceVal)
|
|
157
159
|
})
|
package/src/export/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-01 13:56:18
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime:
|
|
6
|
+
* @LastEditTime: 2023-06-12 11:33:34
|
|
7
7
|
*/
|
|
8
8
|
const {
|
|
9
9
|
syncFiles,
|
|
@@ -25,6 +25,7 @@ function doExport() {
|
|
|
25
25
|
(async () => {
|
|
26
26
|
const otpConfig = getProjectConfig()
|
|
27
27
|
const distLang = otpConfig && otpConfig.distLangs
|
|
28
|
+
const baiduApiKey = otpConfig && otpConfig.baiduApiKey
|
|
28
29
|
|
|
29
30
|
if (!Array.isArray(distLang)) {
|
|
30
31
|
console.log(`请配置${OCTOPUS_CONFIG_FILE}里面的distLangs`)
|
|
@@ -42,7 +43,7 @@ function doExport() {
|
|
|
42
43
|
const langFlat = flatObject(currentLangMap);
|
|
43
44
|
spinner.start('正在同步文件的key')
|
|
44
45
|
// 删除掉多余的key,增加新的key,同时提取没有翻译过的key的列表
|
|
45
|
-
const { fileKeyValueList, addList, allList } = await getAdjustLangObjAndAddList(lang, langFlat, zhCNflat);
|
|
46
|
+
const { fileKeyValueList, addList, allList } = await getAdjustLangObjAndAddList(lang, langFlat, zhCNflat, baiduApiKey, spinner);
|
|
46
47
|
spinner.succeed('同步文件的key成功')
|
|
47
48
|
// 重写文件
|
|
48
49
|
rewriteFiles(fileKeyValueList, lang);
|
|
@@ -83,10 +83,12 @@ function visit(node) {
|
|
|
83
83
|
const start = node.getStart();
|
|
84
84
|
const end = node.getEnd();
|
|
85
85
|
const range = { start, end };
|
|
86
|
+
const { line } = ast.getLineAndCharacterOfPosition(start)
|
|
86
87
|
matches.push({
|
|
87
88
|
range,
|
|
88
89
|
text,
|
|
89
|
-
isString: true
|
|
90
|
+
isString: true,
|
|
91
|
+
line
|
|
90
92
|
});
|
|
91
93
|
}
|
|
92
94
|
break;
|
|
@@ -104,11 +106,12 @@ function visit(node) {
|
|
|
104
106
|
const start = child.getStart();
|
|
105
107
|
const end = child.getEnd();
|
|
106
108
|
const range = { start, end };
|
|
107
|
-
|
|
109
|
+
const { line } = ast.getLineAndCharacterOfPosition(start)
|
|
108
110
|
matches.push({
|
|
109
111
|
range,
|
|
110
112
|
text: text.trim(),
|
|
111
|
-
isString: false
|
|
113
|
+
isString: false,
|
|
114
|
+
line
|
|
112
115
|
});
|
|
113
116
|
}
|
|
114
117
|
}
|
|
@@ -123,10 +126,12 @@ function visit(node) {
|
|
|
123
126
|
const start = node.getStart();
|
|
124
127
|
const end = node.getEnd();
|
|
125
128
|
const range = { start, end };
|
|
129
|
+
const { line } = ast.getLineAndCharacterOfPosition(start)
|
|
126
130
|
matches.push({
|
|
127
131
|
range,
|
|
128
132
|
text: code.slice(start + 1, end - 1),
|
|
129
|
-
isString: true
|
|
133
|
+
isString: true,
|
|
134
|
+
line
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
137
|
break;
|
|
@@ -139,10 +144,12 @@ function visit(node) {
|
|
|
139
144
|
const start = node.getStart();
|
|
140
145
|
const end = node.getEnd();
|
|
141
146
|
const range = { start, end };
|
|
147
|
+
const { line } = ast.getLineAndCharacterOfPosition(start)
|
|
142
148
|
matches.push({
|
|
143
149
|
range,
|
|
144
150
|
text: code.slice(start + 1, end - 1),
|
|
145
|
-
isString: true
|
|
151
|
+
isString: true,
|
|
152
|
+
line
|
|
146
153
|
});
|
|
147
154
|
}
|
|
148
155
|
break;
|
|
@@ -157,19 +164,20 @@ function visit(node) {
|
|
|
157
164
|
const start = node.getStart();
|
|
158
165
|
const end = node.getEnd();
|
|
159
166
|
const range = { start, end };
|
|
160
|
-
|
|
167
|
+
const { line } = ast.getLineAndCharacterOfPosition(start)
|
|
161
168
|
matches.push({
|
|
162
169
|
range,
|
|
163
170
|
text: text.trim(),
|
|
164
|
-
isString: false
|
|
171
|
+
isString: false,
|
|
172
|
+
line
|
|
165
173
|
});
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
}
|
|
169
177
|
|
|
170
|
-
ts.forEachChild(node, visit);
|
|
178
|
+
ts.forEachChild(node, (node) => visit(node, ast));
|
|
171
179
|
}
|
|
172
|
-
ts.forEachChild(ast, visit);
|
|
180
|
+
ts.forEachChild(ast, (node) => visit(node, ast));
|
|
173
181
|
// 过滤相同的选项
|
|
174
182
|
return matches.reduce((total, item) => {
|
|
175
183
|
const bool = total.some(i => _.isEqual(i, item));
|
package/src/extract/replace.js
CHANGED
|
@@ -156,6 +156,29 @@ function createImportI18N(filePath) {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* 判断I18N.template所在行前面有多少个空格/tab,以及类型是tab还是空格
|
|
161
|
+
* @param {*} code
|
|
162
|
+
* @param {*} text
|
|
163
|
+
* @returns { tabSize: number, isSpace: bool } || null
|
|
164
|
+
*/
|
|
165
|
+
function getLineTableSize(code, line){
|
|
166
|
+
try{
|
|
167
|
+
const list = code ? code.split('\n') : []
|
|
168
|
+
const str = list[line];
|
|
169
|
+
// 用于判断是空格还是space
|
|
170
|
+
const match = str.match(/^[ \t]+/);
|
|
171
|
+
// 判断空格/tab的个数
|
|
172
|
+
let spaces = str.match(/^\s+/);
|
|
173
|
+
return {
|
|
174
|
+
tabSize: spaces[0].length,
|
|
175
|
+
isSpace: match[0].startsWith(" ")
|
|
176
|
+
}
|
|
177
|
+
}catch(e) {
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
159
182
|
/**
|
|
160
183
|
* 更新文件
|
|
161
184
|
* @param filePath 当前文件路径
|
|
@@ -193,7 +216,16 @@ function replaceAndUpdate(filePath, arg, val, validateDuplicate, needWrite = tru
|
|
|
193
216
|
const kvPair = varInStr.map((str, index) => {
|
|
194
217
|
return `val${index + 1}: ${str.replace(/^\${([^\}]+)\}$/, '$1')}`;
|
|
195
218
|
});
|
|
196
|
-
|
|
219
|
+
|
|
220
|
+
// 多个变量情况下格式化
|
|
221
|
+
const { tabSize, isSpace } = getLineTableSize(code, arg.line) || {}
|
|
222
|
+
if(kvPair.length > 1 && !isNaN(tabSize)) {
|
|
223
|
+
const space = '\n' + (isSpace ? " " : "\t").repeat(tabSize + (isSpace ? (CONFIG.tabSize || 4) : 1))
|
|
224
|
+
const startSpace = '\n' + (isSpace ? " " : "\t").repeat(tabSize)
|
|
225
|
+
finalReplaceVal = `I18N.template(${val}, {${space}${kvPair.join(`,${space}`)}${startSpace}})`;
|
|
226
|
+
}else{
|
|
227
|
+
finalReplaceVal = `I18N.template(${val}, { ${kvPair.join(',\n')} })`;
|
|
228
|
+
}
|
|
197
229
|
|
|
198
230
|
varInStr.forEach((str, index) => {
|
|
199
231
|
finalReplaceText = finalReplaceText.replace(str, `{val${index + 1}}`);
|
package/src/translate/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @Author: 郑泳健
|
|
4
4
|
* @Date: 2022-06-01 13:56:18
|
|
5
5
|
* @LastEditors: 郑泳健
|
|
6
|
-
* @LastEditTime:
|
|
6
|
+
* @LastEditTime: 2023-06-12 11:33:51
|
|
7
7
|
*/
|
|
8
8
|
const {
|
|
9
9
|
syncFiles,
|
|
@@ -25,6 +25,7 @@ function translate() {
|
|
|
25
25
|
(async () => {
|
|
26
26
|
const otpConfig = getProjectConfig()
|
|
27
27
|
const distLang = otpConfig && otpConfig.distLangs
|
|
28
|
+
const baiduApiKey = otpConfig && otpConfig.baiduApiKey
|
|
28
29
|
|
|
29
30
|
if (!Array.isArray(distLang)) {
|
|
30
31
|
console.log(`请配置${OCTOPUS_CONFIG_FILE}里面的distLangs`)
|
|
@@ -42,7 +43,7 @@ function translate() {
|
|
|
42
43
|
const langFlat = flatObject(currentLangMap);
|
|
43
44
|
spinner.start('正在同步文件的key')
|
|
44
45
|
// 删除掉多余的key,增加新的key,同时提取没有翻译过的key的列表
|
|
45
|
-
const { fileKeyValueList, addList } = await getAdjustLangObjAndAddList(lang, langFlat, zhCNflat);
|
|
46
|
+
const { fileKeyValueList, addList } = await getAdjustLangObjAndAddList(lang, langFlat, zhCNflat, baiduApiKey, spinner);
|
|
46
47
|
spinner.succeed('同步文件的key成功')
|
|
47
48
|
// 重写文件
|
|
48
49
|
rewriteFiles(fileKeyValueList, lang);
|
package/src/utils/syncLang.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const { otpPath } = require('./translate');
|
|
3
|
+
const { getLangData } = require('../extract/getLangData')
|
|
3
4
|
|
|
4
5
|
module.exports = (lang) => {
|
|
5
6
|
try {
|
|
@@ -8,9 +9,7 @@ module.exports = (lang) => {
|
|
|
8
9
|
list.forEach((i) => {
|
|
9
10
|
const suffixCheck = ['.js', '.ts', '.jsx', 'tsx'].some(it => i.endsWith(it));
|
|
10
11
|
if (suffixCheck && !['index.js', 'index.jsx', 'index.ts', 'index.tsx'].includes(i)) {
|
|
11
|
-
const
|
|
12
|
-
const replaceStr = str.replace(/export default|\;/g, '');
|
|
13
|
-
const json = eval("(" + replaceStr + ")");
|
|
12
|
+
const json = getLangData(`${otpPath}/${lang}/${i}`)
|
|
14
13
|
const key = i.split('.')[0];
|
|
15
14
|
|
|
16
15
|
langMap[key] = json;
|
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: 2023-
|
|
6
|
+
* @LastEditTime: 2023-06-12 11:32:52
|
|
7
7
|
*/
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
@@ -130,12 +130,12 @@ function getFileKeyValueList(adjustLangObj) {
|
|
|
130
130
|
* @param {*} zhCNObj zh-CN 的key/value
|
|
131
131
|
* @returns { fileKeyValueList: [{fileName: a, value: {"b.c": xx}}], addList: [["a.b.c": "dd"]] }
|
|
132
132
|
*/
|
|
133
|
-
async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
133
|
+
async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}, baiduApiKey, spinner) {
|
|
134
134
|
const langObjKeys = Object.keys(langObj);
|
|
135
135
|
// 调整后的语言包key/value
|
|
136
136
|
const adjustLangObj = {};
|
|
137
137
|
// 需要新增的key/value
|
|
138
|
-
|
|
138
|
+
let needAddList = [];
|
|
139
139
|
// 全量的的key/value
|
|
140
140
|
const allList = [];
|
|
141
141
|
// 循环zh-CN的key,得到当前语言包的key
|
|
@@ -154,8 +154,10 @@ async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
|
154
154
|
|
|
155
155
|
adjustLangObj[key] = langObj[key] || zhCNObj[key];
|
|
156
156
|
}
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
if(baiduApiKey) {
|
|
158
|
+
spinner.text = `当前配置了百度翻译,预计翻译时间需要${(needAddList.length / 60)?.toFixed(2)}分钟,如果等不及,请先去掉百度翻译配置`
|
|
159
|
+
}
|
|
160
|
+
const addList = await combinText(needAddList, lang, spinner);
|
|
159
161
|
|
|
160
162
|
return {
|
|
161
163
|
fileKeyValueList: getFileKeyValueList(adjustLangObj),
|
|
@@ -167,9 +169,9 @@ async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
|
167
169
|
/**
|
|
168
170
|
* 合并需要翻译的中文,因为百度翻译限制一次性中文只能有3000字
|
|
169
171
|
* 因为百度免费翻译有时候会抽风,会导致翻译结果出错,为了减少没被翻译的,所以现在设置一次性翻译200字
|
|
170
|
-
* @param {*} needAddList
|
|
172
|
+
* @param {*} needAddList
|
|
171
173
|
*/
|
|
172
|
-
async function combinText(needAddList, lang) {
|
|
174
|
+
async function combinText(needAddList, lang, spinner) {
|
|
173
175
|
const otpConfig = getProjectConfig()
|
|
174
176
|
const { baiduApiKey, baiduLangMap } = otpConfig || {}
|
|
175
177
|
const { appId, appKey } = baiduApiKey || {}
|
|
@@ -183,11 +185,10 @@ async function combinText(needAddList, lang) {
|
|
|
183
185
|
if (!appId || !appKey || !toLang) {
|
|
184
186
|
return needAddList;
|
|
185
187
|
}
|
|
186
|
-
|
|
187
188
|
// 分组
|
|
188
189
|
const groupList = groupByLength(needAddList, 200)
|
|
189
190
|
// 分组翻译结果
|
|
190
|
-
const transformResultList = await getTransformResultList(groupList, appId, appKey, 'zh', toLang)
|
|
191
|
+
const transformResultList = await getTransformResultList(groupList, appId, appKey, 'zh', toLang, spinner)
|
|
191
192
|
|
|
192
193
|
return needAddList.map((i, index) => {
|
|
193
194
|
i[2] = transformResultList[index];
|
|
@@ -197,28 +198,30 @@ async function combinText(needAddList, lang) {
|
|
|
197
198
|
|
|
198
199
|
/**
|
|
199
200
|
* 对分组的结果进行翻译
|
|
200
|
-
* @param {*} groupList
|
|
201
|
-
* @param {*} appId
|
|
202
|
-
* @param {*} appKey
|
|
203
|
-
* @param {*} fromLang
|
|
204
|
-
* @param {*} toLang
|
|
205
|
-
* @returns
|
|
201
|
+
* @param {*} groupList
|
|
202
|
+
* @param {*} appId
|
|
203
|
+
* @param {*} appKey
|
|
204
|
+
* @param {*} fromLang
|
|
205
|
+
* @param {*} toLang
|
|
206
|
+
* @returns
|
|
206
207
|
*/
|
|
207
|
-
async function getTransformResultList(groupList, appId, appKey, fromLang, toLang) {
|
|
208
|
-
let translatList = []
|
|
208
|
+
async function getTransformResultList(groupList, appId, appKey, fromLang, toLang, spinner) {
|
|
209
|
+
let translatList = [];
|
|
210
|
+
let num = 1
|
|
209
211
|
for (const i of groupList) {
|
|
212
|
+
spinner.text = `翻译进度${num}/${groupList.length}`
|
|
210
213
|
const baiduResult = await baiduTranslation(appId, appKey, fromLang, toLang, JSON.stringify(i))
|
|
211
|
-
|
|
214
|
+
num++;
|
|
212
215
|
try {
|
|
213
|
-
const
|
|
214
|
-
const transResultList = JSON.parse(splitBaiduResult);
|
|
215
|
-
// 这里是判断百度翻译返回的长度是不是和传入的一样
|
|
216
|
-
const reduceNum = i.length - transResultList.length;
|
|
217
|
-
translatList = [...translatList,
|
|
216
|
+
const res = baiduResult?.[0]
|
|
217
|
+
// const transResultList = JSON.parse(splitBaiduResult);
|
|
218
|
+
// // 这里是判断百度翻译返回的长度是不是和传入的一样
|
|
219
|
+
// const reduceNum = i.length - transResultList.length;
|
|
220
|
+
translatList = [...translatList, res]
|
|
218
221
|
|
|
219
222
|
} catch (e) {
|
|
220
223
|
console.log(`百度翻译出现部分失败, 失败原因: ${e.message}`)
|
|
221
|
-
translatList = translatList.concat(
|
|
224
|
+
translatList = translatList.concat('')
|
|
222
225
|
}
|
|
223
226
|
}
|
|
224
227
|
|
|
@@ -229,32 +232,32 @@ async function getTransformResultList(groupList, appId, appKey, fromLang, toLang
|
|
|
229
232
|
* 对数组进行分组
|
|
230
233
|
* @param {*} groupList 原数组
|
|
231
234
|
* @param {*} max 最大多少字
|
|
232
|
-
* @returns
|
|
235
|
+
* @returns
|
|
233
236
|
*/
|
|
234
237
|
function groupByLength(groupList, max) {
|
|
235
238
|
const list = [[]]
|
|
236
239
|
let str = ''
|
|
237
240
|
// 变成${max}个字符一组的数组,用于一次百度翻译
|
|
238
241
|
groupList.forEach((it) => {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if ((str + text).length < max) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
} else {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
242
|
+
const [, text] = it;
|
|
243
|
+
list.push(text)
|
|
244
|
+
// if ((str + text).length < max) {
|
|
245
|
+
// list[list.length - 1] = Array.isArray(list[list.length - 1]) ? [...list[list.length - 1], text] : [text]
|
|
246
|
+
// str = str + text
|
|
247
|
+
// } else {
|
|
248
|
+
// list.push([text])
|
|
249
|
+
// str = ''
|
|
250
|
+
// }
|
|
248
251
|
})
|
|
249
252
|
return list
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
/**
|
|
253
256
|
* 百度翻译
|
|
254
|
-
* @param {*} from
|
|
255
|
-
* @param {*} to
|
|
256
|
-
* @param {*} text
|
|
257
|
-
* @returns
|
|
257
|
+
* @param {*} from
|
|
258
|
+
* @param {*} to
|
|
259
|
+
* @param {*} text
|
|
260
|
+
* @returns
|
|
258
261
|
*/
|
|
259
262
|
async function baiduTranslation(appId, appKey, from, to, text) {
|
|
260
263
|
return new Promise((resolve, reject) => {
|
|
@@ -263,15 +266,15 @@ async function baiduTranslation(appId, appKey, from, to, text) {
|
|
|
263
266
|
return baiduTranslate(appId, appKey, to, from)(text)
|
|
264
267
|
.then(data => {
|
|
265
268
|
if (data && data.trans_result) {
|
|
266
|
-
const result = data.trans_result.map(item => item.dst) ||
|
|
269
|
+
const result = data.trans_result.map(item => item.dst) || '';
|
|
267
270
|
resolve(result);
|
|
268
271
|
} else {
|
|
269
|
-
resolve('
|
|
272
|
+
resolve('')
|
|
270
273
|
}
|
|
271
274
|
}).catch(err => {
|
|
272
|
-
reject('
|
|
275
|
+
reject('');
|
|
273
276
|
});
|
|
274
|
-
},
|
|
277
|
+
}, 1100)
|
|
275
278
|
})
|
|
276
279
|
}
|
|
277
280
|
|