1 | #!/usr/bin/env node
|
2 | (async function () {
|
3 | "use script";
|
4 | process.on("uncaughtException", (err) => {
|
5 | console.error("got an error: %s", err);
|
6 | process.exitCode = 1;
|
7 | });
|
8 |
|
9 | const updateNotifier = require("update-notifier");
|
10 | const whatTime = require("what-time");
|
11 | const minimatch = require("minimatch");
|
12 | const async = require("async");
|
13 | const fs = require("fs");
|
14 | const path = require("path");
|
15 | const listMd = require("./src/util/readmd.js");
|
16 | const meow = require("meow");
|
17 |
|
18 | const mergeConfig = require("./src/config/mergeConfig.js");
|
19 |
|
20 | const {
|
21 | g,
|
22 | y,
|
23 | yow,
|
24 | m,
|
25 | b,
|
26 | r,
|
27 | relaPath,
|
28 | insert_flg,
|
29 | } = require("./src/util/util.js");
|
30 |
|
31 |
|
32 | const cli = meow(`
|
33 | Usage
|
34 | $ translateMds [folder/file name] [options]
|
35 |
|
36 | Example
|
37 | $ translateMds md/
|
38 | $ translateMds -T 'inlineCode,linkReference,link,heading' readme.md
|
39 |
|
40 | ${b("[options]")}
|
41 | ${g("-a API")} : default < baidu > ${y("{google|baidu|youdao}")}
|
42 | ${g("-f from ")} : default < auto detect >
|
43 | ${g("-t to ")} : default < zh >
|
44 | ${g("-N num ")} : default < 1 > ${y("{async number}")}
|
45 | ${g("-R rewrite")} : default < false > ${y(
|
46 | "{yes/no rewrite translate file}"
|
47 | )}
|
48 |
|
49 | 🌟${m("[high user options]")}❤️
|
50 |
|
51 | ${g("-D debug")}
|
52 | ${g("-C cache")} : default: false ${y("cache in disk")}
|
53 | ${g("-G google.com")} : default: false ${y(
|
54 | "{ cn => com with Google api }"
|
55 | )}
|
56 | ${g("-F force")} : default: false ${y(
|
57 | "{ If, translate result is no 100%, force wirte md file }"
|
58 | )}
|
59 | ${g("-M match")} : default [ ". ", "! "//...] ${y(
|
60 | "{match this str, merge translate result }"
|
61 | )}
|
62 | ${g("-S skips")} : default ["... ", "etc. ", "i.e. "] ${y(
|
63 | "{match this str will, skip merge translate result }"
|
64 | )}
|
65 | ${g("-T types")} : default ["html", "code"] ${y(
|
66 | "{pass the md AST type}"
|
67 | )}
|
68 | ${g("--timewait ")} : default < 80 > ${y(
|
69 | "{each fetch api wait time}"
|
70 | )}
|
71 | ${g("--values [path]")} : default: false ${y(
|
72 | "{write the original of wait for translate file}"
|
73 | )} ${r("[single file]")}
|
74 | ${g("--translate [path]")} : default: false ${y(
|
75 | "{use this file translate}"
|
76 | )} ${r("[single file]")}
|
77 | ${g("--text-glob [pattern]")} : default: false ${y(
|
78 | "{text must be match, then be transalte}"
|
79 | )}
|
80 | ${g("--no-disk")} : default: false ${y(
|
81 | "{do not use cached Result}"
|
82 | )}
|
83 | ${g("--cache-name [filename]")}: default: "translateMds" ${y(
|
84 | "named the cache file"
|
85 | )}
|
86 | ${g("--glob [pattern]")} : default: false ${y(
|
87 | "{file must be match, then be transalte}"
|
88 | )}
|
89 | ${g("--ignore [relative file/folder]")} : default: false ${y(
|
90 | "{ignore files/folders string, split with `,` }"
|
91 | )}
|
92 |
|
93 | `);
|
94 |
|
95 | updateNotifier({ pkg: cli.pkg }).notify();
|
96 |
|
97 |
|
98 | const dir = cli.input[0];
|
99 | if (!dir) {
|
100 | console.error(g("--> v" + cli.pkg.version), cli.help);
|
101 | process.exit(1);
|
102 | }
|
103 |
|
104 |
|
105 | const {
|
106 | debug,
|
107 | tranFr,
|
108 | tranTo,
|
109 | api,
|
110 | rewrite,
|
111 | asyncNum,
|
112 | Force,
|
113 | ignores,
|
114 | glob,
|
115 | Cache,
|
116 | } = mergeConfig(cli);
|
117 |
|
118 | const translateMds = require("./src/translateMds.js");
|
119 |
|
120 | const {
|
121 | loggerStart,
|
122 | loggerText,
|
123 | loggerStop,
|
124 | oneOra,
|
125 | } = require("./src/config/loggerConfig.js");
|
126 |
|
127 |
|
128 | const { writeDataToFile } = require("./src/util/writeDataToFile.js");
|
129 |
|
130 | console.log(b(`> ${yow(`Cache:${Cache}`)} Starting 翻译`) + r(dir));
|
131 |
|
132 |
|
133 | const getList = await listMd(path.resolve(process.cwd(), dir), {
|
134 | deep: "all",
|
135 | });
|
136 |
|
137 | console.log(b(`总文件数 ${getList.length}, 有些文件会跳过`));
|
138 |
|
139 | let Done = 0;
|
140 | const noDone = [];
|
141 | let showAsyncnum = 0;
|
142 |
|
143 | loggerStart("translate running ... >> ");
|
144 | async.mapLimit(getList, asyncNum, runTranslate, (err, IsTranslateS) => {
|
145 | loggerStop();
|
146 | if (noDone.length > 0) {
|
147 | process.exitCode = 1;
|
148 | }
|
149 | if (err) {
|
150 | throw err;
|
151 | }
|
152 |
|
153 | Done++;
|
154 | if (IsTranslateS.every((x) => Boolean(x))) {
|
155 | oneOra("All Done");
|
156 | } else {
|
157 | if (debug !== "debug") {
|
158 | oneOra(
|
159 | `Some No Done , ${yow("use")} cli-option${r(" { -D } ")} find the Err`
|
160 | );
|
161 | }
|
162 | if (!Force) {
|
163 | oneOra(
|
164 | `Or ${yow("use")} cli-option${r(
|
165 | " { -F } "
|
166 | )} Force put the translate Result`
|
167 | );
|
168 | }
|
169 | if (debug === "debug" || Force) {
|
170 | oneOra(
|
171 | `[${g("DEBUG")}:${debug === "debug"}|${g("Force")}:${Force}] mode`
|
172 | );
|
173 | }
|
174 | }
|
175 | oneOra(`time:${whatTime(process.uptime())}`);
|
176 | });
|
177 |
|
178 | |
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | async function runTranslate(value) {
|
185 | const rePath = relaPath(value);
|
186 | loggerText(`++++ <😊 > ${rePath}`);
|
187 |
|
188 | let State = true;
|
189 | Done++;
|
190 |
|
191 | const localDone = Done;
|
192 |
|
193 |
|
194 | if (value.endsWith(`.${tranTo}.md`) || !value.endsWith(".md")) {
|
195 | loggerText(b(`- 翻译的 - 或者 不是 md 文件的 ${g(rePath)}`));
|
196 | return State;
|
197 | }
|
198 | if (value.match(/\.[a-zA-Z]{2}\.md+/)) {
|
199 |
|
200 | loggerText(b(`- 有后缀为 *.国家简写.md ${g(rePath)}`));
|
201 | return State;
|
202 | }
|
203 | if (!rewrite && fs.existsSync(insert_flg(value, `.${tranTo}`, 3))) {
|
204 | loggerText(b(`已翻译, 不覆盖 ${g(rePath)}`));
|
205 | return State;
|
206 | }
|
207 | if (glob && glob.some((g) => !minimatch(value, g, { matchBase: true }))) {
|
208 | loggerText(b(`glob, no match ${g(rePath)}`));
|
209 | return State;
|
210 | }
|
211 | if (
|
212 | ignores &&
|
213 | ignores.some((ignore) => value.includes(path.resolve(ignore)))
|
214 | ) {
|
215 | loggerText(b(`ignore, ${g(rePath)}`));
|
216 | return State;
|
217 | }
|
218 |
|
219 | loggerText(`1. do 第${localDone}文件 ${rePath}`);
|
220 |
|
221 |
|
222 | showAsyncnum++;
|
223 | const startTime = new Date().getTime();
|
224 |
|
225 | const _translateMds = await translateMds(
|
226 | [value, api, tranFr, tranTo],
|
227 | debug,
|
228 | true
|
229 | );
|
230 |
|
231 |
|
232 | if (_translateMds.every((x) => !x.error && x.text) || Force) {
|
233 |
|
234 | const _tranData = _translateMds.map((x) => x.text);
|
235 |
|
236 | await writeDataToFile(_tranData, value).then((text) => loggerText(text));
|
237 | }
|
238 |
|
239 | let Err;
|
240 | for (const _t of _translateMds) {
|
241 | if (_t.error) {
|
242 | Err = _t.error;
|
243 | break;
|
244 | }
|
245 | }
|
246 |
|
247 | const endtime = new Date().getTime() - startTime;
|
248 | const humanTime = whatTime(endtime / 1000);
|
249 |
|
250 | if (State && !Err) {
|
251 | oneOra(
|
252 | `已搞定 第 ${localDone} 文件 - 并发${b(showAsyncnum)} -- ${b(
|
253 | humanTime
|
254 | )} - ${rePath}`
|
255 | );
|
256 | } else {
|
257 | State = false;
|
258 | if (!State) {
|
259 |
|
260 | noDone.push(value);
|
261 | oneOra(
|
262 | `没完成 第 ${localDone} 文件 - 并发${b(showAsyncnum)} -- ${b(
|
263 | humanTime
|
264 | )} - ${rePath} \n ${Err}`,
|
265 | "fail"
|
266 | );
|
267 | }
|
268 | }
|
269 |
|
270 | showAsyncnum--;
|
271 |
|
272 | return State;
|
273 | }
|
274 |
|
275 | process.on("exit", (_) => {
|
276 | loggerStop();
|
277 | });
|
278 | })();
|