@l10nmonster/cli 1.0.5 → 3.0.0-alpha.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.
@@ -1,689 +0,0 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
-
29
- // l10nCommands.js
30
- var l10nCommands_exports = {};
31
- __export(l10nCommands_exports, {
32
- builtInCmds: () => builtInCmds,
33
- runL10nMonster: () => runL10nMonster
34
- });
35
- module.exports = __toCommonJS(l10nCommands_exports);
36
- var path = __toESM(require("path"), 1);
37
- var util = __toESM(require("node:util"), 1);
38
- var winston = __toESM(require("winston"), 1);
39
- var import_core8 = require("@l10nmonster/core");
40
-
41
- // analyze.js
42
- var import_fs = require("fs");
43
- var import_core = require("@l10nmonster/core");
44
-
45
- // shared.js
46
- var import_helpers = require("@l10nmonster/helpers");
47
- var consoleColor = {
48
- red: "\x1B[31m",
49
- yellow: "\x1B[33m",
50
- green: "\x1B[32m",
51
- reset: "\x1B[0m",
52
- dim: "\x1B[2m",
53
- bright: "\x1B[1m"
54
- };
55
- function printContent(contentPairs) {
56
- for (const [prj, uc] of Object.entries(contentPairs)) {
57
- console.log(`Project: ${prj}`);
58
- for (const [rid, content] of Object.entries(uc)) {
59
- console.log(` \u2023 ${rid}`);
60
- for (const [sid, str] of Object.entries(content)) {
61
- console.log(` \u2219 ${consoleColor.dim}${sid}:${consoleColor.reset} ${str.color}${str.confidence ? `[${str.confidence.toFixed(2)}] ` : ""}${sid === str.txt ? "\u2263" : str.txt}${consoleColor.reset}`);
62
- }
63
- }
64
- }
65
- }
66
- function printRequest(req) {
67
- const untranslatedContent = {};
68
- for (const tu of req.tus) {
69
- const prj = tu.prj || "default";
70
- untranslatedContent[prj] ??= {};
71
- untranslatedContent[prj][tu.rid] ??= {};
72
- const confidence = 1;
73
- untranslatedContent[prj][tu.rid][tu.sid] = {
74
- confidence,
75
- txt: import_helpers.utils.flattenNormalizedSourceV1(tu.nsrc)[0],
76
- // eslint-disable-next-line no-nested-ternary
77
- color: confidence <= 0.1 ? consoleColor.red : confidence <= 0.2 ? consoleColor.yellow : consoleColor.green
78
- };
79
- }
80
- printContent(untranslatedContent);
81
- }
82
- function printResponse(req, res, showPair) {
83
- const translations = res.tus.reduce((p, c) => (p[c.guid] = c.ntgt, p), {});
84
- let matchedTranslations = 0;
85
- const translatedContent = {};
86
- for (const tu of req.tus) {
87
- const prj = tu.prj || "default";
88
- translatedContent[prj] ??= {};
89
- translatedContent[prj][tu.rid] ??= {};
90
- if (translations[tu.guid]) {
91
- const key = showPair ? import_helpers.utils.flattenNormalizedSourceV1(tu.nsrc)[0] : tu.sid;
92
- translatedContent[prj][tu.rid][key] = {
93
- txt: import_helpers.utils.flattenNormalizedSourceV1(translations[tu.guid])[0],
94
- color: consoleColor.green
95
- };
96
- matchedTranslations++;
97
- }
98
- }
99
- if (req.tus.length !== res.tus.length || req.tus.length !== matchedTranslations) {
100
- console.log(`${consoleColor.red}${req.tus.length} TU in request, ${res.tus.length} TU in response, ${matchedTranslations} matching translations${consoleColor.reset}`);
101
- }
102
- printContent(translatedContent);
103
- }
104
-
105
- // analyze.js
106
- var analyze = class {
107
- static help = {
108
- description: "content reports and validation.",
109
- arguments: [
110
- ["[analyzer]", "name of the analyzer to run"],
111
- ["[params...]", "optional parameters to the analyzer"]
112
- ],
113
- options: [
114
- ["-l, --lang <language>", "target language to analyze (if TM analyzer)"],
115
- ["--filter <filter>", "use the specified tu filter"],
116
- ["--output <filename>", "filename to write the analysis to)"]
117
- ]
118
- };
119
- static async action(monsterManager, options) {
120
- try {
121
- if (options.analyzer) {
122
- const analysis = await (0, import_core.analyzeCmd)(monsterManager, options.analyzer, options.params, options.lang, options.filter);
123
- const header = analysis.head;
124
- if (options.output) {
125
- const rows = header ? [header, ...analysis.body].map((row) => row.join(",")) : analysis.body;
126
- rows.push("\n");
127
- (0, import_fs.writeFileSync)(options.output, rows.join("\n"));
128
- } else {
129
- if (header) {
130
- const groups = analysis.groupBy;
131
- let previousGroup;
132
- for (const row of analysis.body) {
133
- const columns = row.map((col, idx) => [col, idx]);
134
- if (groups) {
135
- const currentGroup = columns.filter(([col, idx]) => groups.includes(header[idx]));
136
- const currentGroupSmashed = currentGroup.map(([col, idx]) => col).join("|");
137
- if (currentGroupSmashed !== previousGroup) {
138
- previousGroup = currentGroupSmashed;
139
- console.log(currentGroup.map(([col, idx]) => `${consoleColor.dim}${header[idx]}: ${consoleColor.reset}${consoleColor.bright}${col}${consoleColor.reset}`).join(" "));
140
- }
141
- }
142
- const currentData = columns.filter(([col, idx]) => (!groups || !groups.includes(header[idx])) && col !== null && col !== void 0);
143
- console.log(currentData.map(([col, idx]) => ` ${consoleColor.dim}${header[idx]}: ${consoleColor.reset}${col}`).join(""));
144
- }
145
- } else {
146
- console.log(analysis.body.join("\n"));
147
- }
148
- }
149
- } else {
150
- console.log("Available analyzers:");
151
- for (const [name, analyzer] of Object.entries(monsterManager.analyzers)) {
152
- console.log(` ${typeof analyzer.prototype.processSegment === "function" ? "(src)" : " (tu)"} ${consoleColor.bright}${name} ${analyzer.helpParams ?? ""}${consoleColor.reset} ${analyzer.help}`);
153
- }
154
- }
155
- } catch (e) {
156
- console.error(`Failed to analyze: ${e.stack || e}`);
157
- }
158
- }
159
- };
160
-
161
- // job.js
162
- var import_core2 = require("@l10nmonster/core");
163
- var job = class {
164
- static help = {
165
- description: "show request/response/pairs of a job or push/delete jobs.",
166
- arguments: [
167
- ["<operation>", "operation to perform on job", ["req", "res", "pairs", "push", "delete"]]
168
- ],
169
- requiredOptions: [
170
- ["-g, --jobGuid <guid>", "guid of job"]
171
- ]
172
- };
173
- static async action(monsterManager, options) {
174
- const op = options.operation;
175
- const jobGuid = options.jobGuid;
176
- if (op === "req") {
177
- const req = await monsterManager.jobStore.getJobRequest(jobGuid);
178
- if (req) {
179
- console.log(`Showing request of job ${jobGuid} ${req.sourceLang} -> ${req.targetLang}`);
180
- printRequest(req);
181
- } else {
182
- console.error("Could not fetch the specified job");
183
- }
184
- } else if (op === "res") {
185
- const req = await monsterManager.jobStore.getJobRequest(jobGuid);
186
- const res = await monsterManager.jobStore.getJob(jobGuid);
187
- if (req && res) {
188
- console.log(`Showing response of job ${jobGuid} ${req.sourceLang} -> ${req.targetLang} (${res.translationProvider}) ${res.status}`);
189
- printResponse(req, res);
190
- } else {
191
- console.error("Could not fetch the specified job");
192
- }
193
- } else if (op === "pairs") {
194
- const req = await monsterManager.jobStore.getJobRequest(jobGuid);
195
- const res = await monsterManager.jobStore.getJob(jobGuid);
196
- if (req && res) {
197
- console.log(`Showing source-target pairs of job ${jobGuid} ${req.sourceLang} -> ${req.targetLang} (${res.translationProvider}) ${res.status}`);
198
- printResponse(req, res, true);
199
- } else {
200
- console.error("Could not fetch the specified job");
201
- }
202
- } else if (op === "push") {
203
- console.log(`Pushing job ${jobGuid}...`);
204
- try {
205
- const pushResponse = await (0, import_core2.jobPushCmd)(monsterManager, jobGuid);
206
- console.log(`${pushResponse.num.toLocaleString()} translations units requested -> status: ${pushResponse.status}`);
207
- } catch (e) {
208
- console.error(`Failed to push job: ${e.stack ?? e}`);
209
- }
210
- } else if (op === "delete") {
211
- console.log(`Deleting job ${jobGuid}...`);
212
- try {
213
- const res = await monsterManager.jobStore.getJob(jobGuid);
214
- if (res) {
215
- console.error(`Can only delete blocked/failed jobs. This job has status: ${res.status}`);
216
- } else {
217
- await monsterManager.jobStore.deleteJobRequest(jobGuid);
218
- }
219
- } catch (e) {
220
- console.error(`Failed to push job: ${e.stack ?? e}`);
221
- }
222
- } else {
223
- console.error(`Invalid operation: ${op}`);
224
- }
225
- }
226
- };
227
-
228
- // jobs.js
229
- var import_core3 = require("@l10nmonster/core");
230
- var jobs = class {
231
- static help = {
232
- description: "unfinished jobs status.",
233
- options: [
234
- ["-l, --lang <language>", "only get jobs for the target language"]
235
- ]
236
- };
237
- static async action(monsterManager, options) {
238
- const limitToLang = options.lang;
239
- const jobs2 = await (0, import_core3.jobsCmd)(monsterManager, { limitToLang });
240
- for (const [lang, jobManifests] of Object.entries(jobs2)) {
241
- if (jobManifests.length > 0) {
242
- console.log(`Target language ${consoleColor.bright}${lang}${consoleColor.reset}:`);
243
- for (const mf of jobManifests) {
244
- const numUnits = mf.inflight?.length ?? mf.tus?.length ?? 0;
245
- const lastModified = new Date(mf.updatedAt);
246
- console.log(` Job ${mf.jobGuid}: status ${consoleColor.bright}${mf.status}${consoleColor.reset} ${numUnits.toLocaleString()} ${mf.sourceLang} units with ${mf.translationProvider} - ${lastModified.toDateString()} ${lastModified.toLocaleTimeString()}`);
247
- }
248
- }
249
- }
250
- }
251
- };
252
-
253
- // monster.js
254
- var monster = class {
255
- static help = {
256
- description: "test configuration and warm up caches",
257
- options: [
258
- ["-l, --lang <language>", "target languages to warm up"]
259
- ]
260
- };
261
- static async action(monsterManager, options) {
262
- console.log(
263
- " _.------. .----.__\n / \\_. ._ /---.__ \\\n | O O |\\\\___ //| / `\\ |\n | .vvvvv. | ) `(/ | | o o \\|\n / | | |/ \\ | /| ./| .vvvvv. |\\\n / `^^^^^' / _ _ `|_ || / /| | | | \\\n ./ /| | O) O ) \\|| //' | `^vvvv' |/\\\\\n / / | \\ / | | ~ \\ | \\\\\n \\ / | / \\ Y /' | \\ | | ~\n `' | _ | `._/' | | \\ 7 /\n _.-'-' `-'-'| |`-._/ / \\ _ / . |\n __.-' \\ \\ . / \\_. \\ -|_/\\/ `--.|_\n --' \\ \\ | / | | `-\n \\uU \\UU/ | / :F_P:"
264
- );
265
- console.time("Initialization time");
266
- const resourceHandles = await monsterManager.rm.getResourceHandles();
267
- const targetLangs = monsterManager.getTargetLangs(options.lang);
268
- console.log(`Resources: ${resourceHandles.length}`);
269
- console.log(`Possible languages: ${targetLangs.join(", ")}`);
270
- console.log("Translation Memories:");
271
- const availableLangPairs = (await monsterManager.jobStore.getAvailableLangPairs()).sort();
272
- for (const [sourceLang, targetLang] of availableLangPairs) {
273
- const tm = await monsterManager.tmm.getTM(sourceLang, targetLang);
274
- console.log(` - ${sourceLang} / ${targetLang} (${tm.guids.length} entries)`);
275
- }
276
- console.timeEnd("Initialization time");
277
- const printCapabilities = (cap) => `${Object.entries(cap).map(([cmd, available]) => `${available ? consoleColor.green : consoleColor.red}${cmd}`).join(" ")}${consoleColor.reset}`;
278
- console.log(`
279
- Your config allows the following commands: ${printCapabilities(monsterManager.capabilities)}`);
280
- if (Object.keys(monsterManager.capabilitiesByChannel).length > 1) {
281
- Object.entries(monsterManager.capabilitiesByChannel).forEach(([channel, cap]) => console.log(` - ${channel}: ${printCapabilities(cap)}`));
282
- }
283
- }
284
- };
285
-
286
- // pull.js
287
- var import_core4 = require("@l10nmonster/core");
288
- var pull = class {
289
- static help = {
290
- description: "receive outstanding translation jobs.",
291
- options: [
292
- ["--partial", "commit partial deliveries"],
293
- ["-l, --lang <language>", "only get jobs for the target language"]
294
- ]
295
- };
296
- static async action(monsterManager, options) {
297
- const limitToLang = options.lang;
298
- const partial = options.partial;
299
- console.log(`Pulling pending translations...`);
300
- const stats = await (0, import_core4.pullCmd)(monsterManager, { limitToLang, partial });
301
- console.log(`Checked ${stats.numPendingJobs.toLocaleString()} pending jobs, ${stats.doneJobs.toLocaleString()} done jobs, ${stats.newPendingJobs.toLocaleString()} pending jobs created, ${stats.translatedStrings.toLocaleString()} translated strings found`);
302
- }
303
- };
304
-
305
- // push.js
306
- var import_core5 = require("@l10nmonster/core");
307
- var push = class {
308
- static help = {
309
- description: "push source content upstream (send to translation).",
310
- options: [
311
- ["-l, --lang <language>", "target language to push"],
312
- ["--filter <filter>", "use the specified tu filter"],
313
- ["--driver <untranslated|source|tm|job:jobGuid>", "driver of translations need to be pushed (default: untranslated)"],
314
- ["--leverage", "eliminate internal repetitions from untranslated driver"],
315
- ["--refresh", "refresh existing translations without requesting new ones"],
316
- ["--provider <name,...>", "use the specified translation providers"],
317
- ["--instructions <instructions>", "send the specified translation instructions"],
318
- ["--dryrun", "simulate translating and compare with existing translations"]
319
- ]
320
- };
321
- static async action(monsterManager, options) {
322
- const limitToLang = options.lang;
323
- const tuFilter = options.filter;
324
- const driverOption = options.driver ?? "untranslated";
325
- const driver = {};
326
- if (driverOption.indexOf("job:") === 0) {
327
- driver.jobGuid = driverOption.split(":")[1];
328
- } else if (["untranslated", "source", "tm"].includes(driverOption)) {
329
- driver[driverOption] = true;
330
- } else {
331
- throw `invalid ${driverOption} driver`;
332
- }
333
- const refresh = options.refresh;
334
- const leverage = options.leverage;
335
- const dryRun = options.dryrun;
336
- const instructions = options.instructions;
337
- console.log(`Pushing content upstream...${dryRun ? " (dry run)" : ""}`);
338
- try {
339
- if (dryRun) {
340
- const status2 = await (0, import_core5.pushCmd)(monsterManager, { limitToLang, tuFilter, driver, refresh, leverage, dryRun, instructions });
341
- for (const langStatus of status2) {
342
- console.log(`
343
- Dry run of ${langStatus.sourceLang} -> ${langStatus.targetLang} push:`);
344
- printRequest(langStatus);
345
- }
346
- } else {
347
- const providerList = (options.provider ?? "default").split(",");
348
- for (const provider of providerList) {
349
- const translationProviderName = provider.toLowerCase() === "default" ? void 0 : provider;
350
- const status2 = await (0, import_core5.pushCmd)(monsterManager, { limitToLang, tuFilter, driver, refresh, translationProviderName, leverage, dryRun, instructions });
351
- if (status2.length > 0) {
352
- for (const ls of status2) {
353
- if (ls.minimumJobSize === void 0) {
354
- console.log(`job ${ls.jobGuid} with ${ls.num.toLocaleString()} translations received for language ${consoleColor.bright}${ls.targetLang}${consoleColor.reset} from provider ${consoleColor.bright}${ls.provider}${consoleColor.reset} -> status: ${consoleColor.bright}${ls.status}${consoleColor.reset}`);
355
- } else {
356
- console.log(`${ls.num.toLocaleString()} translations units for language ${ls.targetLang} not sent to provider ${consoleColor.bright}${ls.provider}${consoleColor.reset} because you need at least ${ls.minimumJobSize}`);
357
- }
358
- }
359
- } else {
360
- console.log("Nothing to push!");
361
- break;
362
- }
363
- }
364
- }
365
- } catch (e) {
366
- console.error(`Failed to push: ${e.stack || e}`);
367
- }
368
- }
369
- };
370
-
371
- // snap.js
372
- var import_core6 = require("@l10nmonster/core");
373
- var snap = class {
374
- static help = {
375
- description: "commits a snapshot of sources in normalized format.",
376
- options: [
377
- ["--maxSegments <number>", "threshold to break up snapshots into chunks"]
378
- ]
379
- };
380
- static async action(monsterManager, options) {
381
- console.log(`Taking a snapshot of sources...`);
382
- const numSources = await (0, import_core6.snapCmd)(monsterManager, options);
383
- console.log(`${numSources} sources committed`);
384
- }
385
- };
386
-
387
- // status.js
388
- var import_fs2 = require("fs");
389
- var import_core7 = require("@l10nmonster/core");
390
- function computeTotals(totals, partial) {
391
- for (const [k, v] of Object.entries(partial)) {
392
- if (typeof v === "object") {
393
- totals[k] ??= {};
394
- computeTotals(totals[k], v);
395
- } else {
396
- totals[k] ??= 0;
397
- totals[k] += v;
398
- }
399
- }
400
- }
401
- function printLeverage(leverage, detailed) {
402
- const totalStrings = leverage.translated + leverage.pending + leverage.untranslated + leverage.internalRepetitions;
403
- detailed && console.log(` - total strings for target language: ${totalStrings.toLocaleString()} (${leverage.translatedWords.toLocaleString()} translated words)`);
404
- for (const [q, num] of Object.entries(leverage.translatedByQ).sort((a, b) => b[1] - a[1])) {
405
- detailed && console.log(` - translated strings @ quality ${q}: ${num.toLocaleString()}`);
406
- }
407
- leverage.pending && console.log(` - strings pending translation: ${leverage.pending.toLocaleString()} (${leverage.pendingWords.toLocaleString()} words)`);
408
- leverage.untranslated && console.log(` - untranslated unique strings: ${leverage.untranslated.toLocaleString()} (${leverage.untranslatedChars.toLocaleString()} chars - ${leverage.untranslatedWords.toLocaleString()} words - $${(leverage.untranslatedWords * 0.2).toFixed(2)})`);
409
- leverage.internalRepetitions && console.log(` - untranslated repeated strings: ${leverage.internalRepetitions.toLocaleString()} (${leverage.internalRepetitionWords.toLocaleString()} words)`);
410
- }
411
- var status = class {
412
- static help = {
413
- description: "translation status of content.",
414
- options: [
415
- ["-l, --lang <language>", "only get status of target language"],
416
- ["-a, --all", "show information for all projects, not just untranslated ones"],
417
- ["--output <filename>", "write status to the specified file"]
418
- ]
419
- };
420
- static async action(monsterManager, options) {
421
- const limitToLang = options.lang;
422
- const all = Boolean(options.all);
423
- const output = options.output;
424
- const status2 = await (0, import_core7.statusCmd)(monsterManager, { limitToLang });
425
- if (output) {
426
- (0, import_fs2.writeFileSync)(output, JSON.stringify(status2, null, " "), "utf8");
427
- } else {
428
- console.log(`${consoleColor.reset}${status2.numSources.toLocaleString()} translatable resources`);
429
- for (const [lang, langStatus] of Object.entries(status2.lang)) {
430
- console.log(`
431
- ${consoleColor.bright}Language ${lang}${consoleColor.reset} (minimum quality: ${langStatus.leverage.minimumQuality})`);
432
- const totals = {};
433
- const prjLeverage = Object.entries(langStatus.leverage.prjLeverage).sort((a, b) => a[0] > b[0] ? 1 : -1);
434
- for (const [prj, leverage] of prjLeverage) {
435
- computeTotals(totals, leverage);
436
- const untranslated = leverage.pending + leverage.untranslated + leverage.internalRepetitions;
437
- if (leverage.translated + untranslated > 0) {
438
- (all || untranslated > 0) && console.log(` Project: ${consoleColor.bright}${prj}${consoleColor.reset}`);
439
- printLeverage(leverage, all);
440
- }
441
- }
442
- if (prjLeverage.length > 1) {
443
- console.log(` Total:`);
444
- printLeverage(totals, true);
445
- }
446
- }
447
- }
448
- return status2;
449
- }
450
- };
451
-
452
- // tmexport.js
453
- var fs = __toESM(require("fs/promises"), 1);
454
- var import_helpers2 = require("@l10nmonster/helpers");
455
- var tmexport = class {
456
- static help = {
457
- description: "export translation memory as a json job.",
458
- options: [
459
- ["-l, --lang <language>", "target language to export"],
460
- ["--filter <filter>", "use the specified tu filter"],
461
- ["--prjsplit", "split target files by project"]
462
- ]
463
- };
464
- static async action(monsterManager, options) {
465
- const prjsplit = options.prjsplit;
466
- console.log(`Exporting TM for ${consoleColor.bright}${options.lang ? options.lang : "all languages"}${consoleColor.reset}...`);
467
- let tuFilterFunction;
468
- if (options.filter) {
469
- tuFilterFunction = monsterManager.tuFilters[import_helpers2.utils.fixCaseInsensitiveKey(monsterManager.tuFilters, options.filter)];
470
- if (!tuFilterFunction) {
471
- throw `Couldn't find ${options.filter} tu filter`;
472
- }
473
- }
474
- const files = [];
475
- const desiredTargetLangs = new Set(monsterManager.getTargetLangs(options.lang));
476
- const availableLangPairs = (await monsterManager.jobStore.getAvailableLangPairs()).filter((pair) => desiredTargetLangs.has(pair[1]));
477
- for (const [sourceLang, targetLang] of availableLangPairs) {
478
- const tusByPrj = {};
479
- const tm = await monsterManager.tmm.getTM(sourceLang, targetLang);
480
- tm.guids.forEach((guid) => {
481
- const tu = tm.getEntryByGuid(guid);
482
- if (!tuFilterFunction || tuFilterFunction(tu)) {
483
- if (!prjsplit || !l10nmonster.prj || l10nmonster.prj.includes(tu.prj)) {
484
- const prj = prjsplit && tu?.prj || "default";
485
- tusByPrj[prj] ??= [];
486
- tusByPrj[prj].push(tu);
487
- }
488
- }
489
- });
490
- for (const [prj, tus] of Object.entries(tusByPrj)) {
491
- const jobGuid = `tmexport_${prjsplit ? `${prj}_` : ""}${sourceLang}_${targetLang}`;
492
- const jobReq = {
493
- sourceLang,
494
- targetLang,
495
- jobGuid,
496
- updatedAt: (l10nmonster.regression ? /* @__PURE__ */ new Date("2022-05-30T00:00:00.000Z") : /* @__PURE__ */ new Date()).toISOString(),
497
- status: "created",
498
- tus: []
499
- };
500
- const jobRes = {
501
- ...jobReq,
502
- translationProvider: "TMExport",
503
- status: "done",
504
- tus: []
505
- };
506
- for (const tu of tus) {
507
- try {
508
- jobReq.tus.push(l10nmonster.TU.asSource(tu));
509
- } catch (e) {
510
- l10nmonster.logger.info(e.stack ?? e);
511
- }
512
- if (tu.inflight) {
513
- l10nmonster.logger.info(`Warning: in-flight translation unit ${tu.guid} can't be exported`);
514
- } else {
515
- try {
516
- jobRes.tus.push(l10nmonster.TU.asTarget(tu));
517
- } catch (e) {
518
- l10nmonster.logger.info(e.stack ?? e);
519
- }
520
- }
521
- }
522
- const filename = `TMExport_${sourceLang}_${targetLang}_job_${jobGuid}`;
523
- await fs.writeFile(`${filename}-req.json`, JSON.stringify(jobReq, null, " "), "utf8");
524
- await fs.writeFile(`${filename}-done.json`, JSON.stringify(jobRes, null, " "), "utf8");
525
- files.push(filename);
526
- }
527
- }
528
- console.log(`Generated files: ${files.join(", ")}`);
529
- }
530
- };
531
-
532
- // translate.js
533
- function computeDelta(currentTranslations, newTranslations) {
534
- const delta = [];
535
- const newGstrMap = Object.fromEntries(newTranslations.segments.map((seg) => [seg.sid, seg.gstr]));
536
- const seenIds = /* @__PURE__ */ new Set();
537
- for (const seg of currentTranslations.segments) {
538
- seenIds.add(seg.sid);
539
- const newGstr = newGstrMap[seg.sid];
540
- if (seg.gstr !== newGstr) {
541
- delta.push({ id: seg.sid, l: seg.gstr, r: newGstr });
542
- }
543
- }
544
- newTranslations.segments.filter((seg) => !seenIds.has(seg.sid)).forEach((seg) => delta.push({ id: seg.sid, r: seg.gstr }));
545
- return delta;
546
- }
547
- async function compareToExisting(monsterManager, resHandle, targetLang, translatedRes) {
548
- let currentTranslations;
549
- let delta;
550
- const channel = monsterManager.rm.getChannel(resHandle.channel);
551
- try {
552
- currentTranslations = await channel.getExistingTranslatedResource(resHandle, targetLang);
553
- if (translatedRes) {
554
- const newTranslations = await channel.makeResourceHandleFromObject(resHandle).loadResourceFromRaw(translatedRes, { isSource: false });
555
- delta = computeDelta(currentTranslations, newTranslations);
556
- }
557
- } catch (e) {
558
- l10nmonster.logger.verbose(`Couldn't fetch ${targetLang} resource for ${resHandle.channel}:${resHandle.id}: ${e.stack ?? e}`);
559
- }
560
- const bundleChanges = currentTranslations ? translatedRes ? delta.length > 0 ? "changed" : "unchanged" : "deleted" : translatedRes ? "new" : "void";
561
- return [bundleChanges, delta];
562
- }
563
- function printChanges(resHandle, targetLang, bundleChanges, delta) {
564
- if (bundleChanges === "changed") {
565
- console.log(`
566
- ${consoleColor.yellow}Changed translated bundle ${resHandle.channel}:${resHandle.id} for ${targetLang}${consoleColor.reset}`);
567
- for (const change of delta) {
568
- change.l !== void 0 && console.log(`${consoleColor.red}- ${change.id}: ${change.l}${consoleColor.reset}`);
569
- change.r !== void 0 && console.log(`${consoleColor.green}+ ${change.id}: ${change.r}${consoleColor.reset}`);
570
- }
571
- } else if (bundleChanges === "new") {
572
- console.log(`
573
- ${consoleColor.green}New translated bundle ${resHandle.channel}:${resHandle.id} for ${targetLang}${consoleColor.reset}`);
574
- } else if (bundleChanges === "deleted") {
575
- console.log(`
576
- ${consoleColor.green}Deleted translated bundle ${resHandle.channel}:${resHandle.id} for ${targetLang}${consoleColor.reset}`);
577
- }
578
- }
579
- function printSummary(response) {
580
- console.log("Translation summary:");
581
- for (const [lang, langStatus] of Object.entries(response.lang)) {
582
- const summary = {};
583
- for (const resourceStatus of langStatus.resourceStatus) {
584
- summary[resourceStatus.status] = (summary[resourceStatus.status] ?? 0) + 1;
585
- }
586
- console.log(` - ${lang}: ${Object.entries(summary).sort().map(([k, v]) => `${k}(${v})`).join(", ")}`);
587
- }
588
- }
589
- var translate = class {
590
- static help = {
591
- description: "generate translated resources based on latest source and translations.",
592
- arguments: [
593
- ["[mode]", "commit all/changed/none of the translations", ["all", "delta", "dryrun"]]
594
- ],
595
- options: [
596
- ["-l, --lang <language>", "target language to translate"]
597
- ]
598
- };
599
- static async action(monsterManager, options) {
600
- const mode = (options.mode ?? "all").toLowerCase();
601
- console.log(`Generating translated resources for ${consoleColor.bright}${options.lang ? options.lang : "all languages"}${consoleColor.reset}... (${mode} mode)`);
602
- const response = { lang: {} };
603
- const targetLangs = monsterManager.getTargetLangs(options.lang);
604
- const allResources = await monsterManager.rm.getAllResources({ keepRaw: true });
605
- for await (const resHandle of allResources) {
606
- for (const targetLang of targetLangs) {
607
- if (resHandle.targetLangs.includes(targetLang) && (l10nmonster.prj === void 0 || l10nmonster.prj.includes(resHandle.prj))) {
608
- const resourceStatus = { id: resHandle.id };
609
- const tm = await monsterManager.tmm.getTM(resHandle.sourceLang, targetLang);
610
- const translatedRes = await resHandle.generateTranslatedRawResource(tm);
611
- let bundleChanges, delta;
612
- if (mode === "delta" || mode === "dryrun") {
613
- [bundleChanges, delta] = await compareToExisting(monsterManager, resHandle, targetLang, translatedRes);
614
- resourceStatus.status = bundleChanges;
615
- resourceStatus.delta = delta;
616
- }
617
- if (mode === "dryrun") {
618
- printChanges(resHandle, targetLang, bundleChanges, delta);
619
- } else if (mode === "all" || bundleChanges === "changed" || bundleChanges === "new" || bundleChanges === "deleted") {
620
- const translatedResourceId = await monsterManager.rm.getChannel(resHandle.channel).commitTranslatedResource(targetLang, resHandle.id, translatedRes);
621
- resourceStatus.status = translatedRes === null ? "deleted" : "generated";
622
- resourceStatus.translatedId = translatedResourceId;
623
- l10nmonster.logger.verbose(`Committed translated resource: ${translatedResourceId}`);
624
- } else {
625
- l10nmonster.logger.verbose(`Delta mode skipped translation of bundle ${resHandle.channel}:${resHandle.id} for ${targetLang}`);
626
- resourceStatus.status = "skipped";
627
- }
628
- response.lang[targetLang] ??= { resourceStatus: [] };
629
- response.lang[targetLang].resourceStatus.push(resourceStatus);
630
- }
631
- }
632
- }
633
- printSummary(response);
634
- return response;
635
- }
636
- };
637
-
638
- // l10nCommands.js
639
- function createLogger2(verboseOption) {
640
- const verboseLevel = verboseOption === void 0 || verboseOption === 0 ? "error" : (
641
- // eslint-disable-next-line no-nested-ternary
642
- verboseOption === 1 ? "warn" : verboseOption === true || verboseOption === 2 ? "info" : "verbose"
643
- );
644
- return winston.createLogger({
645
- level: verboseLevel,
646
- transports: [
647
- new winston.transports.Console({
648
- format: winston.format.combine(
649
- winston.format.ms(),
650
- winston.format.timestamp(),
651
- winston.format.printf(({ level, message, timestamp, ms }) => `${consoleColor.green}${timestamp.substr(11, 12)} (${ms}) [${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB] ${level}: ${typeof message === "string" ? message : util.inspect(message)}${consoleColor.reset}`)
652
- )
653
- })
654
- ]
655
- });
656
- }
657
- function createHandler(mm, globalOptions, action) {
658
- return (opts) => action(mm, { ...globalOptions, ...opts });
659
- }
660
- var builtInCmds = [analyze, job, jobs, monster, pull, push, snap, status, tmexport, translate];
661
- async function runL10nMonster(relativePath, globalOptions, cb) {
662
- const configPath = path.resolve(".", relativePath);
663
- global.l10nmonster ??= {};
664
- l10nmonster.logger = createLogger2(globalOptions.verbose);
665
- l10nmonster.env = process.env;
666
- const mm = await (0, import_core8.createMonsterManager)(configPath, globalOptions);
667
- const l10n = {
668
- withMonsterManager: (cb2) => cb2(mm)
669
- };
670
- [...builtInCmds, ...mm.extensionCmds].forEach((Cmd) => l10n[Cmd.name] = createHandler(mm, globalOptions, Cmd.action));
671
- let response;
672
- try {
673
- response = await cb(l10n);
674
- } catch (e) {
675
- response = { error: e.stack ?? e };
676
- } finally {
677
- mm && await mm.shutdown();
678
- }
679
- if (response?.error) {
680
- throw response.error;
681
- }
682
- return response;
683
- }
684
- // Annotate the CommonJS export names for ESM import in node:
685
- 0 && (module.exports = {
686
- builtInCmds,
687
- runL10nMonster
688
- });
689
- //# sourceMappingURL=l10nCommands.cjs.map