td-octopus 0.0.13 → 0.0.15
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 +1 -0
- package/cmds/export.js +9 -0
- package/package.json +1 -1
- package/src/back/index.js +192 -0
- package/src/export/index.js +61 -0
- package/src/extract/findChineseText.js +8 -3
- package/src/utils/translate.js +7 -1
package/README.md
CHANGED
package/cmds/export.js
ADDED
package/package.json
CHANGED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Description: 将国际化转换回去
|
|
3
|
+
* @Author: 郑泳健
|
|
4
|
+
* @Date: 2022-06-01 13:56:18
|
|
5
|
+
* @LastEditors: 郑泳健
|
|
6
|
+
* @LastEditTime: 2023-01-11 11:07:41
|
|
7
|
+
*/
|
|
8
|
+
const path = require('path')
|
|
9
|
+
const fs = require('fs')
|
|
10
|
+
const ts = require('typescript');
|
|
11
|
+
const { getSpecifiedFiles, isFile } = require('../utils/file')
|
|
12
|
+
const syncLang = require('../utils/syncLang')
|
|
13
|
+
const { flatObject } = require('../utils/translate');
|
|
14
|
+
const { failInfo, highlightText } = require('../utils/colors');
|
|
15
|
+
const { getProjectConfig } = require('../utils/index')
|
|
16
|
+
|
|
17
|
+
// 匹配I18N.
|
|
18
|
+
const I18N_REGEX = /I18N(\.[a-zA-Z0-9_]+)+/g
|
|
19
|
+
|
|
20
|
+
// 匹配I18N.template()里面的内容
|
|
21
|
+
const TEMPLATE_INNER_REGEX = /I18N\.template\(([\s\S]*?})\)/g
|
|
22
|
+
|
|
23
|
+
// 匹配{}之间的内容
|
|
24
|
+
const JSON_KEY_REGEX = /{([\s\S]*?)}/g
|
|
25
|
+
|
|
26
|
+
// 匹配{}之间的value
|
|
27
|
+
const JSON_VALYR_REGEX = /(?<=: )[^,}]+/g
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取需要back的所有文件目录
|
|
31
|
+
* @returns
|
|
32
|
+
*/
|
|
33
|
+
function getFilePaths() {
|
|
34
|
+
const CONFIG = getProjectConfig()
|
|
35
|
+
const dirArr = CONFIG.include && CONFIG.include.length > 0 ? CONFIG.include : ['./'];
|
|
36
|
+
let filePaths = []
|
|
37
|
+
|
|
38
|
+
dirArr.forEach(i => {
|
|
39
|
+
const dirPath = path.resolve(process.cwd(), i);
|
|
40
|
+
const files = getSpecifiedFiles(dirPath)
|
|
41
|
+
filePaths = filePaths.concat(files)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return filePaths.filter(file => {
|
|
45
|
+
let flag = false;
|
|
46
|
+
for (let index = 0; index < CONFIG.fileSuffix.length; index++) {
|
|
47
|
+
const element = CONFIG.fileSuffix[index];
|
|
48
|
+
flag = file.endsWith(element);
|
|
49
|
+
if (flag) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return (isFile(file) && flag);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 获取I18N.template里面{}每个value对应的value
|
|
59
|
+
* @param {*} str {val1: h, val2: m, val3: s}
|
|
60
|
+
* @returns [h, m, s]
|
|
61
|
+
*/
|
|
62
|
+
function getTemplateValue(str) {
|
|
63
|
+
try {
|
|
64
|
+
const values = str.match(JSON_VALYR_REGEX)
|
|
65
|
+
return values;
|
|
66
|
+
} catch (e) {
|
|
67
|
+
throw e;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 如果文件中存在I18N.template,先转换一下
|
|
73
|
+
* @param {*} code
|
|
74
|
+
* @param {*} flatObj
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
function transformTemplate(filePath, code, flatObj) {
|
|
78
|
+
try {
|
|
79
|
+
const templates = code.match(TEMPLATE_INNER_REGEX) || [];
|
|
80
|
+
let sum = Array.isArray(templates) ? templates.length : 0;
|
|
81
|
+
if (Array.isArray(templates) && templates.length) {
|
|
82
|
+
templates.forEach(i => {
|
|
83
|
+
const [jsonStr] = i ? i.match(JSON_KEY_REGEX) : []
|
|
84
|
+
const keys = getTemplateValue(jsonStr)
|
|
85
|
+
let matchList = getValueByI18N(filePath, i, flatObj) || [];
|
|
86
|
+
matchText = matchList[0] && matchList[0]['value']
|
|
87
|
+
if (matchText) {
|
|
88
|
+
matchText = matchText.replace(/{(val\d+)}/g, (match, group, index) => {
|
|
89
|
+
if(!keys[0]) {
|
|
90
|
+
throw new Error(`${filePath}文件中的 ${highlightText(i)} 转换有问题,请手动检查`)
|
|
91
|
+
}
|
|
92
|
+
const res = '${' + keys[0].trim() + '}'
|
|
93
|
+
keys.splice(0, 1)
|
|
94
|
+
return res
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
code = code.replace(i, '`' + matchText + '`')
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
return { code, sum }
|
|
102
|
+
} catch (e) {
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 获取I18N.xx.xx所对应的中文
|
|
109
|
+
* @param {*} filePath 文件路径
|
|
110
|
+
* @param {*} str
|
|
111
|
+
* @param {*} flatObj
|
|
112
|
+
* @returns { key: I18N.xx, value: '测试' }
|
|
113
|
+
*/
|
|
114
|
+
function getValueByI18N(filePath, str, flatObj) {
|
|
115
|
+
try {
|
|
116
|
+
const matchs = str.match(I18N_REGEX);
|
|
117
|
+
if (!Array.isArray(matchs) || !matchs.length) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const matchList = matchs.reduce((total, item) => {
|
|
122
|
+
if (!item.includes('I18N.template') && !item.includes('I18N.setLang')) {
|
|
123
|
+
const text = flatObj[item.replace('I18N.', '')]
|
|
124
|
+
if (text) {
|
|
125
|
+
total.push({ key: item, value: text })
|
|
126
|
+
} else {
|
|
127
|
+
throw new Error(`${filePath}文件中的 ${highlightText(item)} 未发现有对应的中文文案,请检查`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return total
|
|
131
|
+
}, [])
|
|
132
|
+
|
|
133
|
+
return matchList
|
|
134
|
+
} catch (e) {
|
|
135
|
+
throw e;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 获取需要被重写的文件
|
|
141
|
+
* @param {*} filePath 文件路径
|
|
142
|
+
* @param {*} flatObj 所有key对应的中文
|
|
143
|
+
* @returns
|
|
144
|
+
*/
|
|
145
|
+
function getNeedRewriteFiles(filePath, flatObj) {
|
|
146
|
+
try {
|
|
147
|
+
const str = fs.readFileSync(filePath, 'utf-8');
|
|
148
|
+
let { code, sum } = transformTemplate(filePath, str, flatObj)
|
|
149
|
+
|
|
150
|
+
let matchList = getValueByI18N(filePath, code, flatObj) || [];
|
|
151
|
+
if (Array.isArray(matchList) && matchList.length) {
|
|
152
|
+
sum += matchList.length;
|
|
153
|
+
matchList.forEach(({ key, value }) => {
|
|
154
|
+
code = code.replace(key, "'" + value + "'")
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
filePath,
|
|
160
|
+
code,
|
|
161
|
+
sum
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
throw e;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 同步不同的语言包
|
|
169
|
+
function back() {
|
|
170
|
+
try {
|
|
171
|
+
const filePaths = getFilePaths()
|
|
172
|
+
const zhCN = syncLang('zh-CN');
|
|
173
|
+
const zhCNFlat = flatObject(zhCN);
|
|
174
|
+
/** 获取需要被重写的文件 */
|
|
175
|
+
const needRewriteFiles = filePaths.map(i => getNeedRewriteFiles(i, zhCNFlat))
|
|
176
|
+
|
|
177
|
+
needRewriteFiles.forEach(({ filePath, code, sum }) => {
|
|
178
|
+
if (sum > 0) {
|
|
179
|
+
console.log(`正在对 ${highlightText(filePath)} 文件进行回滚操作, 共计${highlightText(sum)}处`);
|
|
180
|
+
|
|
181
|
+
fs.writeFileSync(filePath, code)
|
|
182
|
+
console.log(`${highlightText(filePath)} 文件回滚完毕`);
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
} catch (e) {
|
|
186
|
+
failInfo(e.message)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = {
|
|
191
|
+
back
|
|
192
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Description: 翻译
|
|
3
|
+
* @Author: 郑泳健
|
|
4
|
+
* @Date: 2022-06-01 13:56:18
|
|
5
|
+
* @LastEditors: 郑泳健
|
|
6
|
+
* @LastEditTime: 2022-12-28 15:47:31
|
|
7
|
+
*/
|
|
8
|
+
const {
|
|
9
|
+
syncFiles,
|
|
10
|
+
otpPath,
|
|
11
|
+
flatObject,
|
|
12
|
+
getAdjustLangObjAndAddList,
|
|
13
|
+
generateExcel,
|
|
14
|
+
rewriteFiles,
|
|
15
|
+
} = require('../utils/translate');
|
|
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('开始export');
|
|
22
|
+
|
|
23
|
+
// 同步不同的语言包
|
|
24
|
+
function doExport() {
|
|
25
|
+
(async () => {
|
|
26
|
+
const otpConfig = getProjectConfig()
|
|
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('正在同步文件')
|
|
34
|
+
await syncFiles(distLang);
|
|
35
|
+
spinner.succeed('同步文件成功')
|
|
36
|
+
|
|
37
|
+
const zhCN = syncLang('zh-CN');
|
|
38
|
+
const zhCNflat = flatObject(zhCN);
|
|
39
|
+
|
|
40
|
+
for (const lang of distLang) {
|
|
41
|
+
const currentLangMap = syncLang(lang);
|
|
42
|
+
const langFlat = flatObject(currentLangMap);
|
|
43
|
+
spinner.start('正在同步文件的key')
|
|
44
|
+
// 删除掉多余的key,增加新的key,同时提取没有翻译过的key的列表
|
|
45
|
+
const { fileKeyValueList, addList, allList } = await getAdjustLangObjAndAddList(lang, langFlat, zhCNflat);
|
|
46
|
+
spinner.succeed('同步文件的key成功')
|
|
47
|
+
// 重写文件
|
|
48
|
+
rewriteFiles(fileKeyValueList, lang);
|
|
49
|
+
spinner.start(`正在生成${lang} excel`)
|
|
50
|
+
// 生成excel
|
|
51
|
+
generateExcel(allList, otpPath + '/' + lang, lang);
|
|
52
|
+
spinner.succeed(`生成${lang} excel成功`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
spinner.stop('translate成功')
|
|
56
|
+
})()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
doExport
|
|
61
|
+
}
|
|
@@ -164,15 +164,20 @@ function visit(node) {
|
|
|
164
164
|
isString: false
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
|
-
break;
|
|
168
167
|
}
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
ts.forEachChild(node, visit);
|
|
172
171
|
}
|
|
173
172
|
ts.forEachChild(ast, visit);
|
|
174
|
-
|
|
175
|
-
return matches
|
|
173
|
+
// 过滤相同的选项
|
|
174
|
+
return matches.reduce((total, item) => {
|
|
175
|
+
const bool = total.some(i => _.isEqual(i, item));
|
|
176
|
+
if(!bool) {
|
|
177
|
+
total.push(item)
|
|
178
|
+
}
|
|
179
|
+
return total;
|
|
180
|
+
}, []);
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
/**
|
package/src/utils/translate.js
CHANGED
|
@@ -136,6 +136,8 @@ async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
|
136
136
|
const adjustLangObj = {};
|
|
137
137
|
// 需要新增的key/value
|
|
138
138
|
const needAddList = [];
|
|
139
|
+
// 全量的的key/value
|
|
140
|
+
const allList = [];
|
|
139
141
|
// 循环zh-CN的key,得到当前语言包的key
|
|
140
142
|
for (let key in zhCNObj) {
|
|
141
143
|
// 4种情况下,需要将key放入到翻译对象里面去
|
|
@@ -147,6 +149,9 @@ async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
|
147
149
|
needAddList.push([key, zhCNObj[key], '', '']);
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
// allList.push([key, zhCNObj[key], '', _.upperFirst(langObj[key])]);
|
|
153
|
+
allList.push([key, zhCNObj[key], '', langObj[key]]);
|
|
154
|
+
|
|
150
155
|
adjustLangObj[key] = langObj[key] || zhCNObj[key];
|
|
151
156
|
}
|
|
152
157
|
|
|
@@ -154,7 +159,8 @@ async function getAdjustLangObjAndAddList(lang, langObj = {}, zhCNObj = {}) {
|
|
|
154
159
|
|
|
155
160
|
return {
|
|
156
161
|
fileKeyValueList: getFileKeyValueList(adjustLangObj),
|
|
157
|
-
addList
|
|
162
|
+
addList,
|
|
163
|
+
allList
|
|
158
164
|
};
|
|
159
165
|
}
|
|
160
166
|
|