@hoenergy/hoenergy-template-pc 1.0.0

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.
Files changed (63) hide show
  1. package/.i18n_extractor.json +13 -0
  2. package/dist/components/data/table/FormaxTable/index.d.ts +7 -0
  3. package/dist/components/data/table/FormaxTable/src/BasicTable.d.ts +6 -0
  4. package/dist/components/data/table/FormaxTable/src/Table.vue.d.ts +99 -0
  5. package/dist/components/data/table/FormaxTable/src/components/settings/ColumnSetting.vue.d.ts +31 -0
  6. package/dist/components/data/table/FormaxTable/src/const.d.ts +9 -0
  7. package/dist/components/data/table/FormaxTable/src/hooks/useColumns.d.ts +10 -0
  8. package/dist/components/data/table/FormaxTable/src/hooks/useDataSource.d.ts +20 -0
  9. package/dist/components/data/table/FormaxTable/src/hooks/useLoading.d.ts +6 -0
  10. package/dist/components/data/table/FormaxTable/src/hooks/usePagination.d.ts +10 -0
  11. package/dist/components/data/table/FormaxTable/src/hooks/useTableContext.d.ts +13 -0
  12. package/dist/components/data/table/FormaxTable/src/props.d.ts +1 -0
  13. package/dist/components/data/table/FormaxTable/src/types/componentType.d.ts +1 -0
  14. package/dist/components/data/table/FormaxTable/src/types/pagination.d.ts +10 -0
  15. package/dist/components/data/table/FormaxTable/src/types/table.d.ts +36 -0
  16. package/dist/components/data/table/FormaxTable/src/types/tableAction.d.ts +23 -0
  17. package/dist/components/data/table/FormaxTable/src/utils.d.ts +11 -0
  18. package/dist/components/general/button/FormaxButton/index.d.ts +2 -0
  19. package/dist/index.d.ts +41 -0
  20. package/dist/index.js +37 -0
  21. package/dist/index.mjs +1862 -0
  22. package/dist/mock.d.ts +13 -0
  23. package/dist/style.css +1 -0
  24. package/dist/types.d.ts +34 -0
  25. package/docs/i18n-stub.mjs +12 -0
  26. package/docs/index.html +314 -0
  27. package/docs/index.js +658 -0
  28. package/docs/index.mjs +113892 -0
  29. package/docs/tailwind.css +4 -0
  30. package/docs/tailwind.generated.css +1 -0
  31. package/package.json +51 -0
  32. package/playground/App.vue +60 -0
  33. package/playground/index.html +12 -0
  34. package/playground/main.ts +12 -0
  35. package/src/components/data/table/FormaxTable/index.ts +143 -0
  36. package/src/components/data/table/FormaxTable/src/BasicTable.ts +12 -0
  37. package/src/components/data/table/FormaxTable/src/Table.vue +412 -0
  38. package/src/components/data/table/FormaxTable/src/components/TableAction.vue +155 -0
  39. package/src/components/data/table/FormaxTable/src/components/settings/ColumnSetting.vue +248 -0
  40. package/src/components/data/table/FormaxTable/src/const.ts +11 -0
  41. package/src/components/data/table/FormaxTable/src/hooks/useColumns.ts +147 -0
  42. package/src/components/data/table/FormaxTable/src/hooks/useDataSource.ts +238 -0
  43. package/src/components/data/table/FormaxTable/src/hooks/useLoading.ts +21 -0
  44. package/src/components/data/table/FormaxTable/src/hooks/usePagination.ts +65 -0
  45. package/src/components/data/table/FormaxTable/src/hooks/useTableContext.ts +23 -0
  46. package/src/components/data/table/FormaxTable/src/props.ts +77 -0
  47. package/src/components/data/table/FormaxTable/src/types/componentType.ts +9 -0
  48. package/src/components/data/table/FormaxTable/src/types/pagination.ts +10 -0
  49. package/src/components/data/table/FormaxTable/src/types/table.ts +45 -0
  50. package/src/components/data/table/FormaxTable/src/types/tableAction.ts +25 -0
  51. package/src/components/data/table/FormaxTable/src/utils.ts +65 -0
  52. package/src/components/general/button/FormaxButton/index.ts +70 -0
  53. package/src/index.ts +66 -0
  54. package/src/locales/en.json +13 -0
  55. package/src/locales/zh-CN.json +13 -0
  56. package/src/mock.ts +17 -0
  57. package/src/shims-vue.d.ts +6 -0
  58. package/src/types/i18n-auto-extractor.d.ts +3 -0
  59. package/src/types.ts +40 -0
  60. package/tailwind.config.cjs +12 -0
  61. package/tsconfig.json +16 -0
  62. package/tsup.config.ts +16 -0
  63. package/vite.config.ts +59 -0
package/docs/index.js ADDED
@@ -0,0 +1,658 @@
1
+ #!/usr/bin/env node
2
+ import enquirer from 'enquirer';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { program } from 'commander';
6
+ import chalk from 'chalk';
7
+ import CsvReadableStream from 'csv-reader';
8
+ import { createArrayCsvWriter } from 'csv-writer';
9
+ import crypto from 'node:crypto';
10
+ import ora from 'ora';
11
+ import { Translator } from 'google-translate-api-x';
12
+ import md5 from 'js-md5';
13
+
14
+ const langIsoList = [
15
+ {"name": "英语", "value": "en"},
16
+ {"name": "中文(繁体)", "value": "zh-TW"},
17
+ {"name": "阿布哈兹语", "value": "ab"},
18
+ {"name": "亚齐语", "value": "ace"},
19
+ {"name": "阿乔利语", "value": "ach"},
20
+ {"name": "南非荷兰语", "value": "af"},
21
+ {"name": "阿尔巴尼亚语", "value": "sq"},
22
+ {"name": "阿卢尔语", "value": "alz"},
23
+ {"name": "Amharic", "value": "am"},
24
+ {"name": "阿拉伯语", "value": "ar"},
25
+ {"name": "亚美尼亚语", "value": "hy"},
26
+ {"name": "阿萨姆语", "value": "as"},
27
+ {"name": "阿瓦德语", "value": "awa"},
28
+ {"name": "艾马拉语", "value": "ay"},
29
+ {"name": "阿塞拜疆语", "value": "az"},
30
+ {"name": "巴厘语", "value": "ban"},
31
+ {"name": "班巴拉语", "value": "bm"},
32
+ {"name": "巴什基尔语", "value": "ba"},
33
+ {"name": "巴斯克语", "value": "eu"},
34
+ {"name": "巴塔克卡罗语", "value": "btx"},
35
+ {"name": "巴塔克西马隆贡语", "value": "bts"},
36
+ {"name": "巴塔克托巴语", "value": "bbc"},
37
+ {"name": "白俄罗斯语", "value": "be"},
38
+ {"name": "Bemba", "value": "bem"},
39
+ {"name": "孟加拉语", "value": "bn"},
40
+ {"name": "Betawi", "value": "bew"},
41
+ {"name": "博杰普尔语", "value": "bho"},
42
+ {"name": "Bikol", "value": "bik"},
43
+ {"name": "波斯尼亚语", "value": "bs"},
44
+ {"name": "布列塔尼语", "value": "br"},
45
+ {"name": "保加利亚语", "value": "bg"},
46
+ {"name": "布里亚特语", "value": "bua"},
47
+ {"name": "粤语", "value": "yue"},
48
+ {"name": "加泰罗尼亚语", "value": "ca"},
49
+ {"name": "宿务语", "value": "ceb"},
50
+ {"name": "齐切瓦语(尼扬贾语)", "value": "ny"},
51
+ {"name": "简体中文", "value": "zh-CN"},
52
+ {"name": "楚瓦什语", "value": "cv"},
53
+ {"name": "科西嘉语", "value": "co"},
54
+ {"name": "克里米亚鞑靼语", "value": "crh"},
55
+ {"name": "克罗地亚语", "value": "hr"},
56
+ {"name": "捷克语", "value": "cs"},
57
+ {"name": "丹麦语", "value": "da"},
58
+ {"name": "Dinka", "value": "din"},
59
+ {"name": "第维埃语", "value": "dv"},
60
+ {"name": "多格来语", "value": "doi"},
61
+ {"name": "敦贝语", "value": "dov"},
62
+ {"name": "荷兰语", "value": "nl"},
63
+ {"name": "宗卡语", "value": "dz"},
64
+ {"name": "世界语", "value": "eo"},
65
+ {"name": "爱沙尼亚语", "value": "et"},
66
+ {"name": "Ewe", "value": "ee"},
67
+ {"name": "斐济语", "value": "fj"},
68
+ {"name": "芬兰语", "value": "fi"},
69
+ {"name": "法语", "value": "fr"},
70
+ {"name": "法语(加拿大)", "value": "fr-CA"},
71
+ {"name": "弗里斯兰语", "value": "fy"},
72
+ {"name": "富拉语", "value": "ff"},
73
+ {"name": "加 (Ga) 语", "value": "gaa"},
74
+ {"name": "加利西亚语", "value": "gl"},
75
+ {"name": "干达语(卢干达语)", "value": "lg"},
76
+ {"name": "格鲁吉亚语", "value": "ka"},
77
+ {"name": "德语", "value": "de"},
78
+ {"name": "希腊语", "value": "el"},
79
+ {"name": "瓜拉尼语", "value": "gn"},
80
+ {"name": "古吉拉特语", "value": "gu"},
81
+ {"name": "海地克里奥尔语", "value": "ht"},
82
+ {"name": "哈卡钦语", "value": "cnh"},
83
+ {"name": "Hausa", "value": "ha"},
84
+ {"name": "夏威夷语", "value": "haw"},
85
+ {"name": "希利盖农语", "value": "hil"},
86
+ {"name": "印地语", "value": "hi"},
87
+ {"name": "苗语", "value": "hmn"},
88
+ {"name": "匈牙利语", "value": "hu"},
89
+ {"name": "洪斯吕克语", "value": "hrx"},
90
+ {"name": "冰岛语", "value": "is"},
91
+ {"name": "Igbo", "value": "ig"},
92
+ {"name": "伊洛果语", "value": "ilo"},
93
+ {"name": "印度尼西亚语", "value": "id"},
94
+ {"name": "爱尔兰语", "value": "ga"},
95
+ {"name": "意大利语", "value": "it"},
96
+ {"name": "日语", "value": "ja"},
97
+ {"name": "卡纳达语", "value": "kn"},
98
+ {"name": "邦板牙语", "value": "pam"},
99
+ {"name": "哈萨克语", "value": "kk"},
100
+ {"name": "高棉语", "value": "km"},
101
+ {"name": "Kiga", "value": "cgg"},
102
+ {"name": "卢旺达语", "value": "rw"},
103
+ {"name": "吉土巴语", "value": "ktu"},
104
+ {"name": "贡根语", "value": "gom"},
105
+ {"name": "韩语", "value": "ko"},
106
+ {"name": "Krio", "value": "kri"},
107
+ {"name": "库尔德语(库尔曼吉语)", "value": "ku"},
108
+ {"name": "库尔德语(索拉尼语)", "value": "ckb"},
109
+ {"name": "吉尔吉斯语", "value": "ky"},
110
+ {"name": "老挝语", "value": "lo"},
111
+ {"name": "拉特加莱语", "value": "ltg"},
112
+ {"name": "拉丁语", "value": "la"},
113
+ {"name": "拉脱维亚语", "value": "lv"},
114
+ {"name": "利古里亚语", "value": "lij"},
115
+ {"name": "林堡语", "value": "li"},
116
+ {"name": "林加拉语", "value": "ln"},
117
+ {"name": "立陶宛语", "value": "lt"},
118
+ {"name": "伦巴第语", "value": "lmo"},
119
+ {"name": "Luo", "value": "luo"},
120
+ {"name": "卢森堡语", "value": "lb"},
121
+ {"name": "马其顿语", "value": "mk"},
122
+ {"name": "迈蒂利语", "value": "mai"},
123
+ {"name": "马卡萨", "value": "mak"},
124
+ {"name": "马尔加什语", "value": "mg"},
125
+ {"name": "马来语", "value": "ms"},
126
+ {"name": "马来语(爪夷文)", "value": "ms-Arab"},
127
+ {"name": "马拉雅拉姆语", "value": "ml"},
128
+ {"name": "马耳他语", "value": "mt"},
129
+ {"name": "毛利语", "value": "mi"},
130
+ {"name": "马拉地语", "value": "mr"},
131
+ {"name": "草原马里语", "value": "chm"},
132
+ {"name": "梅泰语(曼尼普尔语)", "value": "mni-Mtei"},
133
+ {"name": "米南语", "value": "min"},
134
+ {"name": "米佐语", "value": "lus"},
135
+ {"name": "蒙古语", "value": "mn"},
136
+ {"name": "缅甸语", "value": "my"},
137
+ {"name": "恩德贝莱语(南部)", "value": "nr"},
138
+ {"name": "尼泊尔语(尼瓦尔语)", "value": "new"},
139
+ {"name": "尼泊尔语", "value": "ne"},
140
+ {"name": "北索托语(塞佩蒂语)", "value": "nso"},
141
+ {"name": "挪威语", "value": "no"},
142
+ {"name": "努尔语", "value": "nus"},
143
+ {"name": "奥克斯坦语", "value": "oc"},
144
+ {"name": "奥里亚语(奥里亚)", "value": "or"},
145
+ {"name": "Oromo", "value": "om"},
146
+ {"name": "邦阿西楠语", "value": "pag"},
147
+ {"name": "Papiamento", "value": "pap"},
148
+ {"name": "Pashto", "value": "ps"},
149
+ {"name": "波斯语", "value": "fa"},
150
+ {"name": "波兰语", "value": "pl"},
151
+ {"name": "葡萄牙语", "value": "pt"},
152
+ {"name": "葡萄牙语(葡萄牙)", "value": "pt-PT"},
153
+ {"name": "旁遮普语", "value": "pa"},
154
+ {"name": "旁遮普语(沙木基文)", "value": "pa-Arab"},
155
+ {"name": "克丘亚语", "value": "qu"},
156
+ {"name": "罗姆语", "value": "rom"},
157
+ {"name": "罗马尼亚语", "value": "ro"},
158
+ {"name": "Rundi", "value": "rn"},
159
+ {"name": "俄语", "value": "ru"},
160
+ {"name": "萨摩亚语", "value": "sm"},
161
+ {"name": "Sango", "value": "sg"},
162
+ {"name": "梵语", "value": "sa"},
163
+ {"name": "苏格兰盖尔语", "value": "gd"},
164
+ {"name": "塞尔维亚语", "value": "sr"},
165
+ {"name": "塞索托语", "value": "st"},
166
+ {"name": "塞舌尔克里奥尔语", "value": "crs"},
167
+ {"name": "掸语", "value": "shn"},
168
+ {"name": "修纳语", "value": "sn"},
169
+ {"name": "西西里语", "value": "scn"},
170
+ {"name": "西里西亚语", "value": "szl"},
171
+ {"name": "信德语", "value": "sd"},
172
+ {"name": "僧伽罗语", "value": "si"},
173
+ {"name": "斯洛伐克语", "value": "sk"},
174
+ {"name": "斯洛文尼亚语", "value": "sl"},
175
+ {"name": "索马里语", "value": "so"},
176
+ {"name": "西班牙语", "value": "es"},
177
+ {"name": "巽他语", "value": "su"},
178
+ {"name": "斯瓦希里语", "value": "sw"},
179
+ {"name": "斯瓦特语", "value": "ss"},
180
+ {"name": "瑞典语", "value": "sv"},
181
+ {"name": "塔吉克语", "value": "tg"},
182
+ {"name": "泰米尔语", "value": "ta"},
183
+ {"name": "鞑靼语", "value": "tt"},
184
+ {"name": "泰卢固语", "value": "te"},
185
+ {"name": "德顿语", "value": "tet"},
186
+ {"name": "泰语", "value": "th"},
187
+ {"name": "提格里尼亚语", "value": "ti"},
188
+ {"name": "聪加语", "value": "ts"},
189
+ {"name": "茨瓦纳语", "value": "tn"},
190
+ {"name": "土耳其语", "value": "tr"},
191
+ {"name": "土库曼语", "value": "tk"},
192
+ {"name": "契维语(阿坎语)", "value": "ak"},
193
+ {"name": "乌克兰语", "value": "uk"},
194
+ {"name": "乌尔都语", "value": "ur"},
195
+ {"name": "维吾尔语", "value": "ug"},
196
+ {"name": "乌兹别克语", "value": "uz"},
197
+ {"name": "越南语", "value": "vi"},
198
+ {"name": "威尔士语", "value": "cy"},
199
+ {"name": "科萨语", "value": "xh"},
200
+ {"name": "意第绪语", "value": "yi"},
201
+ {"name": "约鲁巴语", "value": "yo"},
202
+ {"name": "尤卡坦玛雅语", "value": "yua"},
203
+ {"name": "祖鲁语", "value": "zu"}
204
+ ];
205
+
206
+ const baiduLangMap={
207
+ "af": "afr",
208
+ "sq": "alb",
209
+ "am": "amh",
210
+ "ar": "ara",
211
+ "hy": "arm",
212
+ "az": "aze",
213
+ "eu": "baq",
214
+ "be": "bel",
215
+ "bn": "ben",
216
+ "bs": "bos",
217
+ "bg": "bul",
218
+ "my": "bur",
219
+ "ca": "cat",
220
+ "ny": "nya",
221
+ "zh-CN": "zh",
222
+ "zh-TW": "cht",
223
+ "co": "cos",
224
+ "hr": "hrv",
225
+ "da": "dan",
226
+ "dv": "div",
227
+ "eo": "epo",
228
+ "et": "est",
229
+ "fi": "fin",
230
+ "fr": "fra",
231
+ "fy": "fry",
232
+ "gl": "glg",
233
+ "ka": "geo",
234
+ "gu": "guj",
235
+ "ht": "hat",
236
+ "ha": "hau",
237
+ "is": "ice",
238
+ "ig": "ibo",
239
+ "ga": "gle",
240
+ "ja": "jp",
241
+ "kn": "kan",
242
+ "kk": "kaz",
243
+ "km": "khm",
244
+ "rw": "kin",
245
+ "ko": "kor",
246
+ "ku": "kur",
247
+ "ky": "kir",
248
+ "lo": "lao",
249
+ "la": "lat",
250
+ "lv": "lav",
251
+ "lt": "lit",
252
+ "lb": "ltz",
253
+ "mk": "mac",
254
+ "mg": "mlg",
255
+ "ms": "may",
256
+ "ml": "mal",
257
+ "mt": "mlt",
258
+ "mi": "mao",
259
+ "mr": "mar",
260
+ "mn": "mon",
261
+ "ne": "nep",
262
+ "no": "nor",
263
+ "ps": "pus",
264
+ "fa": "per",
265
+ "pa": "pan",
266
+ "ro": "rom",
267
+ "sm": "smo",
268
+ "gd": "gla",
269
+ "sr": "srp",
270
+ "st": "sot",
271
+ "sn": "sna",
272
+ "sd": "snd",
273
+ "si": "sin",
274
+ "sl": "slo",
275
+ "so": "som",
276
+ "es": "spa",
277
+ "su": "sun",
278
+ "sw": "swa",
279
+ "sv": "swe",
280
+ "tg": "tgk",
281
+ "ta": "tam",
282
+ "te": "tel",
283
+ "uk": "ukr",
284
+ "ur": "urd",
285
+ "uz": "uzb",
286
+ "vi": "vie",
287
+ "cy": "wel",
288
+ "xh": "xho",
289
+ "yi": "yid",
290
+ "yo": "yor",
291
+ "zu": "zul"
292
+ };
293
+
294
+ class BaiduTranslator{
295
+ constructor(config, options) {
296
+ this.config = config;
297
+ this.options = options;
298
+ this.options.from=this.getLang(this.options.from);
299
+ this.options.to=this.getLang(this.options.to);
300
+ }
301
+ getLang(lang) {
302
+ return baiduLangMap[lang] || lang
303
+ }
304
+ generateSign(q, salt) {
305
+ return md5(this.config.baidu.appid + q + salt + this.config.baidu.secret);
306
+ }
307
+ async translate(zhs) {
308
+ const salt = Date.now();
309
+ const text = zhs.join('\n');
310
+ const sign = this.generateSign(text, salt);
311
+
312
+ const response = await fetch('https://fanyi-api.baidu.com/api/trans/vip/translate', {
313
+ method: 'POST',
314
+ headers: {
315
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
316
+ },
317
+ body: new URLSearchParams({
318
+ q: text,
319
+ from: this.options.from,
320
+ to: this.options.to,
321
+ appid: this.config.baidu.appid,
322
+ salt,
323
+ sign
324
+ })
325
+ }).then(res=>res.json());
326
+ if(response.error_code){
327
+ if(response.error_code==='58001'){
328
+ throw new Error(`百度翻译不支持语言:${this.options.to}, 请使用谷歌翻译`)
329
+ }else {
330
+ throw new Error(`百度翻译错误:${response.error_code}, ${response.error_msg}`)
331
+ }
332
+ }
333
+ response.trans_result
334
+ .forEach((item) => {
335
+ item.text=item.dst;
336
+ });
337
+ return response.trans_result
338
+ }
339
+ }
340
+
341
+ const getTranslator= (config,params)=> {
342
+ if(config.translateBy === 'baidu'){
343
+ return new BaiduTranslator(config, params);
344
+ }
345
+ return new Translator(params);
346
+ };
347
+
348
+ /* eslint-disable prettier/prettier */
349
+
350
+ const getContentMd5 = (content,config) => {
351
+ const hash = crypto.createHash('md5');
352
+ hash.update(content);
353
+ const hashHex = hash.digest('hex');
354
+ return hashHex.slice(0, config.keyCount);
355
+ };
356
+
357
+ const translate = (zhs,lang,config) => {
358
+ const translator = getTranslator(config,{from: 'zh-CN', to: lang, forceBatch: false});
359
+ return translator.translate(zhs)
360
+ .then((res) =>res.map(item=>item.text))
361
+ };
362
+
363
+
364
+ const zhSet = new Set();
365
+
366
+ function ensureFileExists(filePath, content = '') {
367
+ try {
368
+ if(fs.existsSync(filePath)) return
369
+ // 获取目标文件的目录路径
370
+ const dir = path.dirname(filePath);
371
+
372
+ // 递归创建目录(如果不存在)
373
+ fs.mkdirSync(dir, { recursive: true });
374
+
375
+ // 创建文件(如果不存在)
376
+ fs.writeFileSync(filePath, content, { flag: 'wx' });
377
+ } catch (err) {
378
+ console.error('创建文件失败:', err);
379
+ }
380
+ }
381
+
382
+ const updateLangFile = async (config,lang) => {
383
+ const i18nLocales = path.resolve(process.cwd(), config.localePath);
384
+ const zhJsonPath = path.join(i18nLocales, 'zh-CN.json');
385
+ const toJsonPath = path.join(i18nLocales, `${lang}.json`);
386
+ ensureFileExists(zhJsonPath);
387
+ ensureFileExists(toJsonPath);
388
+ const zhJson = JSON.parse(fs.readFileSync(zhJsonPath, 'utf-8') || '{}');
389
+ const toJson = JSON.parse(fs.readFileSync(toJsonPath, 'utf-8') || '{}');
390
+ const untranslates = [];
391
+ const keys = [];
392
+ for (const zh of zhSet) {
393
+ const key = getContentMd5(zh,config);
394
+ keys.push(key);
395
+ if (!zhJson[key]) {
396
+ zhJson[key] = zh;
397
+ }
398
+ if (!toJson[key]) {
399
+ untranslates.push({
400
+ key,
401
+ zh
402
+ });
403
+ }
404
+ }
405
+ if(!config.onlyExtract && untranslates.length > 0){
406
+ const result = await translate(untranslates.map(t => t.zh),lang,config);
407
+ untranslates.forEach((item, index) => {
408
+ toJson[item.key] = result[index];
409
+ });
410
+ }
411
+ const nextzh = {};
412
+ const nexten = {};
413
+ keys.forEach(key => {
414
+ nextzh[key] = zhJson[key];
415
+ nexten[key] = toJson[key];
416
+ });
417
+ fs.writeFileSync(toJsonPath, JSON.stringify(nexten, null, 2));
418
+ fs.writeFileSync(zhJsonPath, JSON.stringify(nextzh, null, 2));
419
+ };
420
+
421
+ function traverseDirectory(dir, callback) {
422
+ const files= fs.readdirSync(dir, { withFileTypes: true });
423
+ files.forEach(file => {
424
+ const filePath = path.join(dir, file.name);
425
+
426
+ if (file.isDirectory()) {
427
+ // 如果是目录,递归遍历
428
+ traverseDirectory(filePath, callback);
429
+ } else {
430
+ // 如果是文件,执行回调函数
431
+ callback(filePath, file);
432
+ }
433
+ });
434
+ }
435
+ const spinner = ora();
436
+ async function startTranslate(config){
437
+ let startMsg= '中文提取中...';
438
+ if(!config.onlyExtract){
439
+ const needLadder = config.translateBy === 'google';
440
+ startMsg=needLadder?'中文提取翻译中,请确保能科学上网...':'中文提取翻译中...';
441
+ }
442
+ spinner.start(startMsg);
443
+ config.scanPath.split(',').forEach(scanPath => {
444
+ traverseDirectory(path.resolve(process.cwd(), scanPath), (id) => {
445
+ if (id.match(new RegExp(`\.(${config.fileType})$`))) {
446
+ const code = fs.readFileSync(id, 'utf-8');
447
+ const matches = Array.from(code.matchAll(/\$at\(\s*(['"])(.*?)\1/g));
448
+ if (!matches.length) return
449
+ matches.forEach((item) => {
450
+ zhSet.add(item[2]);
451
+ });
452
+ }
453
+ });
454
+ });
455
+ try{
456
+ for(let i=0;i<config.langs.length;i++){
457
+ await updateLangFile(config,config.langs[i]);
458
+ }
459
+ spinner.stop();
460
+ console.log(chalk.greenBright('操作成功~'));
461
+ }catch(e){
462
+ spinner.stop();
463
+ console.error(e);
464
+ console.log(chalk.redBright('翻译失败😔'));
465
+ }
466
+ }
467
+
468
+ const langIsoMap=langIsoList.reduce((pre,item)=>{
469
+ pre[item.name]=item.value;
470
+ return pre;
471
+ },{});
472
+ async function prompt() {
473
+ try {
474
+ const answer = await enquirer.prompt([
475
+ {
476
+ type: "select",
477
+ name: "langs",
478
+ multiple: true,
479
+ required: true,
480
+ message: "需要将中文翻译为(默认英文):",
481
+ choices:langIsoList
482
+ },
483
+ {
484
+ type: "text",
485
+ name: "scanPath",
486
+ default: "src",
487
+ message: "扫描的文件目录(多个目录用英文逗号分隔):",
488
+ },
489
+ {
490
+ type: "text",
491
+ name: "fileType",
492
+ default: "vue|ts|js|jsx|tsx",
493
+ message: "扫描的文件类型后缀:",
494
+ },
495
+ {
496
+ type: "text",
497
+ name: "localePath",
498
+ default: "src/locales",
499
+ message: "翻译文件存放路径:",
500
+ },
501
+ {
502
+ type: "text",
503
+ name: "keyCount",
504
+ default: 10,
505
+ message: "md5作为键截取前几位(过短可能导致键重复,最长32位):",
506
+ },
507
+ {
508
+ type: "confirm",
509
+ name: "onlyExtract",
510
+ default: false,
511
+ message: "是否只提取中文不翻译",
512
+ },
513
+ ]);
514
+ if(!answer.onlyExtract){
515
+ Object.assign(answer,await enquirer.prompt({
516
+ type: "select",
517
+ name: "translateBy",
518
+ required: true,
519
+ default: "google",
520
+ message: "选择翻译方式:",
521
+ choices:[
522
+ {name:'google',message:'谷歌翻译'},
523
+ {name:'baidu',message:'百度翻译'},
524
+ ]
525
+ }));
526
+ if(answer.translateBy==='baidu'){
527
+ Object.assign(answer,{
528
+ baidu:await enquirer.prompt([
529
+ {
530
+ type: "text",
531
+ name: "appid",
532
+ message: "百度翻译开放平台获取的APPID(https://api.fanyi.baidu.com/manage/developer):",
533
+ },
534
+ {
535
+ type: "text",
536
+ name: "secret",
537
+ message: "百度翻译api密钥:",
538
+ },
539
+ ])
540
+ });
541
+ }
542
+ }
543
+ if(answer.langs.length===0){
544
+ answer.langs.push('en');
545
+ }else {
546
+ answer.langs = answer.langs.map(lang=>langIsoMap[lang]);
547
+ }
548
+ answer.keyCount = parseInt(answer.keyCount);
549
+ return answer;
550
+ } catch (e) {
551
+ console.log("Aborted.");
552
+ process.exit(1);
553
+ }
554
+ }
555
+
556
+ const configPath = path.resolve(process.cwd(), '.i18n_extractor.json');
557
+
558
+ async function generateConfig(){
559
+ const answer= await prompt();
560
+ fs.writeFileSync(configPath,JSON.stringify(answer,null,2));
561
+ return answer
562
+ }
563
+ /**
564
+ * 检测配置文件,没有需要生成
565
+ */
566
+ async function getConfig(){
567
+ if(!fs.existsSync(configPath)){
568
+ return await generateConfig()
569
+ }
570
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
571
+ }
572
+
573
+ async function registerCommands(){
574
+ program
575
+ .name('i18n-auto-extractor')
576
+ .description('i18n自动化翻译工具')
577
+ .version('1.3.0');
578
+
579
+ program
580
+ .command('init')
581
+ .description('初始化配置文件')
582
+ .action(async () => {
583
+ generateConfig();
584
+ });
585
+
586
+ program
587
+ .command('at')
588
+ .description('提取翻译文案')
589
+ .action(async () => {
590
+ const config = await getConfig();
591
+ startTranslate(config);
592
+ });
593
+
594
+ program
595
+ .command('export')
596
+ .description('导出翻译结果csv文件')
597
+ .action(async () => {
598
+ const config = await getConfig();
599
+ const i18nLocales = path.resolve(process.cwd(), config.localePath);
600
+ const langs=['zh-CN', ...config.langs];
601
+ const csvPath=path.resolve(i18nLocales, 'i18n.csv');
602
+ const csvWriter = createArrayCsvWriter({
603
+ path: csvPath,
604
+ header: ['hash', ...langs]
605
+ });
606
+ const langMap = {};
607
+ langs.forEach(lang => {
608
+ if(!langMap[lang]){
609
+ langMap[lang] = JSON.parse(fs.readFileSync(path.join(i18nLocales, `${lang}.json`), 'utf8') || '{}');
610
+ }
611
+ });
612
+ const records = [];
613
+ for(const hash in langMap['zh-CN']){
614
+ records.push([hash,...langs.map(lang=>langMap[lang][hash])]);
615
+ }
616
+ csvWriter.writeRecords(records); // returns a promise
617
+ console.log(chalk.greenBright('导出成功,请查看:'), chalk.blueBright(csvPath));
618
+ });
619
+
620
+ program
621
+ .command('import')
622
+ .argument('[file]', 'csv文件路径')
623
+ .description('导入翻译结果csv文件')
624
+ .action(async (file) => {
625
+ const config = await getConfig();
626
+ const i18nLocales = path.resolve(process.cwd(), config.localePath);
627
+ const csvPath = file || path.resolve(i18nLocales, 'i18n.csv');
628
+ const inputStream = fs.createReadStream(csvPath, 'utf8');
629
+ const langMap = {};
630
+ inputStream
631
+ .on('error',function(error){
632
+ console.error(error);
633
+ console.log(chalk.redBright('导入失败:请通过file参数提供csv文件路径!'));
634
+ })
635
+ .pipe(new CsvReadableStream({asObject: true}))
636
+ .on('data', function (row) {
637
+ Object.keys(row).forEach(key=>{
638
+ if(key !== 'hash'){
639
+ if(!langMap[key]){
640
+ langMap[key] = {};
641
+ }
642
+ langMap[key][row.hash] = row[key];
643
+ }
644
+ });
645
+ })
646
+ .on('end', function () {
647
+ Object.keys(langMap).forEach(lang=>{
648
+ const langFile = path.join(i18nLocales,lang+'.json');
649
+ fs.writeFileSync(langFile,JSON.stringify(langMap[lang],null,2));
650
+ });
651
+ console.log(chalk.greenBright('导入成功!'));
652
+ });
653
+ });
654
+
655
+ program.parse(process.argv);
656
+ }
657
+
658
+ await registerCommands();