td-octopus 0.0.1

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,289 @@
1
+ /*
2
+ * @Description: 公用方法
3
+ * @Author: 郑泳健
4
+ * @Date: 2022-06-02 10:09:01
5
+ * @LastEditors: 郑泳健
6
+ * @LastEditTime: 2022-06-30 18:55:03
7
+ */
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const shell = require('shelljs');
11
+ const _ = require('lodash');
12
+ const XLSX = require('xlsx');
13
+ const { PROJECT_CONFIG } = require('./const')
14
+
15
+ const otpPath = path.resolve(process.cwd(), PROJECT_CONFIG.dir);
16
+
17
+ /**
18
+ * 判断对象是否为纯对象
19
+ * @param {*} obj
20
+ * @returns boolean
21
+ */
22
+ const isObj = function (obj) {
23
+ return Object.prototype.toString.call(obj) === '[object Object]';
24
+ };
25
+
26
+ /**
27
+ * 深度优先遍历对象中的所有 string 属性,即文案
28
+ */
29
+ function traverse(obj, cb) {
30
+ function traverseInner(obj, cb, path) {
31
+ _.forEach(obj, (val, key) => {
32
+ if (typeof val === 'string') {
33
+ cb(val, [...path, key].join('.'));
34
+ } else if (typeof val === 'object' && val !== null) {
35
+ traverseInner(val, cb, [...path, key]);
36
+ }
37
+ });
38
+ }
39
+
40
+ traverseInner(obj, cb, []);
41
+ }
42
+
43
+ /**
44
+ * 同步文件,删除多余的文件,增加没有的文件
45
+ * @param {*} distLangs 目录名 []
46
+ */
47
+ async function syncFiles(distLangs = []) {
48
+ const zhCNPath = otpPath + '/zh-CN';
49
+ const files = shell.ls('-A', zhCNPath);
50
+
51
+ for (const lang of distLangs) {
52
+ const currentLangPath = otpPath + '/' + lang;
53
+ // 如果目录不存在就全部复制过去
54
+ if (!shell.test('-e', currentLangPath)) {
55
+ await shell.cp('-R', zhCNPath, currentLangPath);
56
+ } else {
57
+ const currentDirFiles = await shell.ls('-A', currentLangPath);
58
+
59
+ // 删除多余的文件
60
+ for (const i of currentDirFiles) {
61
+ if (!files.includes(i)) {
62
+ await shell.rm('-rf', currentLangPath + '/' + i);
63
+ }
64
+ }
65
+
66
+ // 增加没有的文件
67
+ for (const i of files) {
68
+ if (!currentDirFiles.includes(i)) {
69
+ shell.touch(currentLangPath + '/' + i);
70
+ }
71
+ }
72
+ }
73
+ // 直接复制index.js到对应的语种目录下
74
+ await shell.cp('-R', zhCNPath + '/index.js', currentLangPath + '/index.js');
75
+ }
76
+ }
77
+
78
+ /**
79
+ * 将多层级的对象扁平化
80
+ * {a: {b: {c: '测试'}}} ==> {"a.b.c": "测试"}
81
+ * @param {*} obj 原对象
82
+ * @param {*} prefix 每一层的key都要上一层的key + 本级的key
83
+ * @param {*} result 扁平化后的结果 {"a.b.c": "测试"}
84
+ */
85
+ function flatObject(obj, prefix = '', result = {}) {
86
+ if (!isObj(obj)) {
87
+ return result;
88
+ }
89
+
90
+ Object.entries(obj).forEach(([key, value]) => {
91
+ const sumKey = prefix ? prefix + '.' + key : key;
92
+ if (!isObj(value)) {
93
+ result[sumKey] = value;
94
+ }
95
+ flatObject(value, sumKey, result);
96
+ });
97
+
98
+ return result;
99
+ }
100
+
101
+ /**
102
+ * 将文件名提取出来,因为第一个.前面的代码的是文件名 {a.b.c: "测试", a.d.e: "姓名"} => [{fileName: a, value: {b.c: "测试", d.e: "姓名"}}]
103
+ * @param {*} adjustLangObj {a.b.c: "测试", a.d.e: "姓名"}
104
+ * @returns [{fileName: a, value: {b.c: "测试", d.e: "姓名"}}]
105
+ */
106
+ function getFileKeyValueList(adjustLangObj) {
107
+ const adjustLangObjKeys = Object.keys(adjustLangObj);
108
+ const fileKeyValueList = adjustLangObjKeys.reduce((total, item) => {
109
+ const [fileName, ...rest] = item.split('.');
110
+ const index = total.findIndex((i) => i.fileName === fileName);
111
+
112
+ if (index >= 0) {
113
+ total[index].value[rest.join('.')] = adjustLangObj[item];
114
+ } else {
115
+ total.push({
116
+ fileName,
117
+ value: { [rest.join('.')]: adjustLangObj[item] }
118
+ });
119
+ }
120
+ return total;
121
+ }, []);
122
+
123
+ return fileKeyValueList;
124
+ }
125
+
126
+ /**
127
+ * 当前语言包和zh-CN语言包的diff对比后 ==> 最终语言包key/value & 需要新增的key/value
128
+ * @param {*} langObj 语言包当前的 key/value
129
+ * @param {*} zhCNObj zh-CN 的key/value
130
+ * @returns { fileKeyValueList: [{fileName: a, value: {"b.c": xx}}], addList: [["a.b.c": "dd"]] }
131
+ */
132
+ function getAdjustLangObjAndAddList(langObj = {}, zhCNObj = {}) {
133
+ const langObjKeys = Object.keys(langObj);
134
+ // 调整后的语言包key/value
135
+ const adjustLangObj = {};
136
+ // 需要新增的key/value
137
+ const addList = [];
138
+ // 循环zh-CN的key,得到当前语言包的key
139
+ for (let key in zhCNObj) {
140
+ // 4种情况下,需要将key放入到翻译对象里面去
141
+ // 1: 这个key不存在当前的语言包key/value中
142
+ // 2: 语言包的key的value类型不是string
143
+ // 3: 语言包的key的value值为""
144
+ // 4: 语言包的key的value和zh-CN的key的value一样
145
+ if (!langObjKeys.includes(key) || typeof langObj[key] !== 'string' || langObj[key] === '' || langObj[key] === zhCNObj[key]) {
146
+ addList.push([key, zhCNObj[key], '', '']);
147
+ }
148
+
149
+ adjustLangObj[key] = langObj[key] || zhCNObj[key];
150
+ }
151
+
152
+ return {
153
+ fileKeyValueList: getFileKeyValueList(adjustLangObj),
154
+ addList
155
+ };
156
+ }
157
+
158
+ /**
159
+ * 转换数据结构 {"a.b.c": "测试"} ==> {a: {b: {c: "测试"}}}
160
+ * @param {*} adjustLangObj
161
+ */
162
+ function getDistRst(adjustLangObj) {
163
+ const rst = {};
164
+ traverse(adjustLangObj, (message, key) => {
165
+ _.setWith(rst, key, message, Object);
166
+ });
167
+ return rst;
168
+ }
169
+
170
+ /**
171
+ * 生成excel
172
+ * @param {*} addList 需要翻译的列表
173
+ * @param {*} path 生成的路径
174
+ */
175
+ function generateExcel(addList, path, lang) {
176
+ const excleData = [['需要翻译的字段', '中文', '人工翻译'], ...addList];
177
+
178
+ const options = {
179
+ '!cols': [{ wpx: 100 }, { wpx: 100 }, { wpx: 100 }]
180
+ };
181
+
182
+ const worksheet = XLSX.utils.aoa_to_sheet(excleData);
183
+ worksheet['!cols'] = options['!cols'];
184
+
185
+ const workbook = XLSX.utils.book_new();
186
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
187
+ XLSX.writeFile(workbook, `${path}/translate_${lang}.xls`);
188
+ }
189
+
190
+ /**
191
+ * 解析excel
192
+ * @param {*} path excel的路径地址
193
+ * @returns {"a.b.c": "test"}
194
+ */
195
+ function parseExcel(path, callback) {
196
+ if (!shell.test('-e', path)) {
197
+ console.log('当前目录下没有找到翻译.xls文件');
198
+ return;
199
+ }
200
+ const excelBinary = fs.readFileSync(path);
201
+ const excel = XLSX.read(excelBinary, {
202
+ type: 'buffer'
203
+ });
204
+
205
+ try {
206
+ const sheet0 = excel.Sheets[excel.SheetNames[0]];
207
+ const list = XLSX.utils.sheet_to_json(sheet0);
208
+
209
+ const translateMap = list.reduce((total, item) => {
210
+ const key = item['需要翻译的字段'];
211
+ const value = item['人工翻译'];
212
+
213
+ total[key] = value;
214
+ return total;
215
+ }, {});
216
+
217
+ callback(translateMap);
218
+ } catch (e) {
219
+ console.log(e)
220
+ }
221
+ }
222
+
223
+ /**
224
+ * 重写文件
225
+ * @param {*} langFlat
226
+ * @param {*} lang
227
+ */
228
+ function rewriteFiles(fileKeyValueList, lang) {
229
+ // 将新的key/value重新写入
230
+ if (Array.isArray(fileKeyValueList) && fileKeyValueList.length) {
231
+ fileKeyValueList.forEach(({ fileName, value }) => {
232
+ const distRst = getDistRst(value);
233
+ fs.writeFileSync(`${otpPath}/${lang}/${fileName}.js`, 'export default ' + JSON.stringify(distRst, null, 4));
234
+ });
235
+ }
236
+ }
237
+
238
+ /**
239
+ * 动态修改文件名
240
+ * @param {*} filelist 需要修改后缀的文件列表, 每一项都不带后缀 string[]
241
+ * @param {*} originSuffix 原后缀
242
+ * @param {*} changedSuffix 新后缀
243
+ */
244
+ async function changeFileSuffix(filelist, originSuffix, changedSuffix) {
245
+ for (let i of filelist) {
246
+ await fs.renameSync(i + originSuffix, i + changedSuffix)
247
+ }
248
+ }
249
+
250
+ /**
251
+ * 递归获取所有要修改名字的目录
252
+ * @param {*} path 要翻译的目录
253
+ * @param {*} originSuffix 要翻译的文件原后缀
254
+ * @param {*} fileList 返回哪些文件要修改后缀
255
+ * @returns
256
+ */
257
+ function getNeedChangeNameFileList(path, originSuffix, fileList = []) {
258
+ const files = fs.readdirSync(path);
259
+
260
+ files.forEach(function (file) {
261
+ const stat = fs.statSync(path + '/' + file);
262
+ if (stat.isDirectory()) {
263
+ getNeedChangeNameFileList(path + '/' + file, originSuffix, fileList);
264
+ }
265
+ if (stat.isFile() && file.endsWith(originSuffix)) {
266
+ // 去掉文件后缀,因为后面还要转回来
267
+ const filename = file.substring(0, file.lastIndexOf('.'));
268
+ fileList.push(`${path}/${filename}`);
269
+ }
270
+ });
271
+ return fileList;
272
+ }
273
+
274
+
275
+ module.exports = {
276
+ isObj,
277
+ traverse,
278
+ otpPath,
279
+ syncFiles,
280
+ flatObject,
281
+ getAdjustLangObjAndAddList,
282
+ getFileKeyValueList,
283
+ getDistRst,
284
+ generateExcel,
285
+ parseExcel,
286
+ rewriteFiles,
287
+ changeFileSuffix,
288
+ getNeedChangeNameFileList
289
+ };