@better-translate/cli 1.0.0 → 2.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.
- package/README.md +1 -1
- package/dist/bin.js +74 -12
- package/dist/{chunk-JFSWNLL6.js → chunk-NRZIFUEW.js} +623 -102
- package/dist/cli.d.ts.map +1 -1
- package/dist/extract.d.ts +3 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# @better-translate/cli
|
|
2
2
|
|
|
3
|
-
`@better-translate/cli` generates translated message files and
|
|
3
|
+
`@better-translate/cli` extracts marked source strings into your source locale file, generates translated message files, and localizes markdown. Use it when you want Better Translate to create or update locale files for you.
|
|
4
4
|
|
|
5
5
|
Full docs: [better-translate-placeholder.com/en/docs/cli](https://better-translate-placeholder.com/en/docs/cli)
|
package/dist/bin.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
extractProject,
|
|
3
4
|
generateProject
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NRZIFUEW.js";
|
|
5
6
|
|
|
6
7
|
// src/cli.ts
|
|
7
8
|
import pc2 from "picocolors";
|
|
@@ -96,6 +97,33 @@ ${pc.magenta("\u25C6")} locale: ${pc.bold(localeMatch[1])}`);
|
|
|
96
97
|
console.log(pc.dim(message));
|
|
97
98
|
return;
|
|
98
99
|
}
|
|
100
|
+
const rewriteMatch = message.match(/^rewrote (.+)/);
|
|
101
|
+
if (rewriteMatch) {
|
|
102
|
+
const shortPath = (rewriteMatch[1] ?? "").split("/").slice(-3).join("/");
|
|
103
|
+
spinner.succeed(`${pc.green("\u2713")} ${shortPath}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const updatedMsgMatch = message.match(/^updated messages (.+)/);
|
|
107
|
+
if (updatedMsgMatch) {
|
|
108
|
+
const shortPath = (updatedMsgMatch[1] ?? "").split("/").slice(-3).join("/");
|
|
109
|
+
spinner.stopAndPersist({ symbol: pc.blue("\u25C6"), text: shortPath });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (message.startsWith("warn ")) {
|
|
113
|
+
spinner.stopAndPersist({ symbol: pc.yellow("\u26A0"), text: pc.yellow(message.slice(5)) });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const extractSummaryMatch = message.match(
|
|
117
|
+
/^processed \d+ (?:file|files) and synced \d+ (?:key|keys)\./
|
|
118
|
+
);
|
|
119
|
+
if (extractSummaryMatch) {
|
|
120
|
+
spinner.stop();
|
|
121
|
+
console.log(
|
|
122
|
+
pc.bold(pc.green(`
|
|
123
|
+
\u2713 ${message.charAt(0).toUpperCase()}${message.slice(1)}`))
|
|
124
|
+
);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
99
127
|
console.log(message);
|
|
100
128
|
},
|
|
101
129
|
error(message) {
|
|
@@ -108,12 +136,14 @@ ${pc.magenta("\u25C6")} locale: ${pc.bold(localeMatch[1])}`);
|
|
|
108
136
|
function usage() {
|
|
109
137
|
return [
|
|
110
138
|
"Usage:",
|
|
139
|
+
" bt extract [--config ./better-translate.config.ts] [--dry-run] [--max-length 40]",
|
|
111
140
|
" bt generate [--config ./better-translate.config.ts] [--dry-run]"
|
|
112
141
|
].join("\n");
|
|
113
142
|
}
|
|
114
|
-
function
|
|
143
|
+
function parseCommonArgs(argv) {
|
|
115
144
|
let configPath;
|
|
116
145
|
let dryRun = false;
|
|
146
|
+
let maxLength;
|
|
117
147
|
for (let index = 0; index < argv.length; index += 1) {
|
|
118
148
|
const arg = argv[index];
|
|
119
149
|
if (arg === "--dry-run") {
|
|
@@ -129,11 +159,28 @@ function parseArgs(argv) {
|
|
|
129
159
|
index += 1;
|
|
130
160
|
continue;
|
|
131
161
|
}
|
|
162
|
+
if (arg === "--max-length") {
|
|
163
|
+
const value = argv[index + 1];
|
|
164
|
+
if (!value) {
|
|
165
|
+
throw new Error("--max-length requires a number.");
|
|
166
|
+
}
|
|
167
|
+
if (!/^\d+$/.test(value)) {
|
|
168
|
+
throw new Error("--max-length must be a positive integer.");
|
|
169
|
+
}
|
|
170
|
+
const parsed = Number.parseInt(value, 10);
|
|
171
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
172
|
+
throw new Error("--max-length must be a positive integer.");
|
|
173
|
+
}
|
|
174
|
+
maxLength = parsed;
|
|
175
|
+
index += 1;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
132
178
|
throw new Error(`Unknown argument "${arg}".`);
|
|
133
179
|
}
|
|
134
180
|
return {
|
|
135
181
|
configPath,
|
|
136
|
-
dryRun
|
|
182
|
+
dryRun,
|
|
183
|
+
maxLength
|
|
137
184
|
};
|
|
138
185
|
}
|
|
139
186
|
async function runCli(argv = process.argv.slice(2), options = {}) {
|
|
@@ -144,24 +191,39 @@ async function runCli(argv = process.argv.slice(2), options = {}) {
|
|
|
144
191
|
stdout(usage());
|
|
145
192
|
return command ? 0 : 1;
|
|
146
193
|
}
|
|
147
|
-
if (command !== "generate") {
|
|
194
|
+
if (command !== "extract" && command !== "generate") {
|
|
148
195
|
stderr(`Unknown command "${command}".
|
|
149
196
|
${usage()}`);
|
|
150
197
|
return 1;
|
|
151
198
|
}
|
|
152
199
|
try {
|
|
153
|
-
const parsed =
|
|
200
|
+
const parsed = parseCommonArgs(args);
|
|
201
|
+
if (command === "generate" && parsed.maxLength !== void 0) {
|
|
202
|
+
stderr(`--max-length is not valid for "generate".
|
|
203
|
+
${usage()}`);
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
154
206
|
console.log(pc2.bold("\n better-translate\n"));
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
207
|
+
if (command === "extract") {
|
|
208
|
+
await extractProject({
|
|
209
|
+
configPath: parsed.configPath,
|
|
210
|
+
cwd: options.cwd,
|
|
211
|
+
dryRun: parsed.dryRun,
|
|
212
|
+
logger: createSpinnerLogger(),
|
|
213
|
+
maxLength: parsed.maxLength
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
await generateProject({
|
|
217
|
+
configPath: parsed.configPath,
|
|
218
|
+
cwd: options.cwd,
|
|
219
|
+
dryRun: parsed.dryRun,
|
|
220
|
+
logger: createSpinnerLogger()
|
|
221
|
+
});
|
|
222
|
+
}
|
|
161
223
|
return 0;
|
|
162
224
|
} catch (error) {
|
|
163
225
|
stderr(
|
|
164
|
-
`Better Translate
|
|
226
|
+
`Better Translate ${command} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
165
227
|
);
|
|
166
228
|
return 1;
|
|
167
229
|
}
|
|
@@ -322,21 +322,630 @@ async function loadCliConfig(options = {}) {
|
|
|
322
322
|
};
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
// src/extract.ts
|
|
326
|
+
import { readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
327
|
+
import path5 from "path";
|
|
328
|
+
import ts2 from "typescript";
|
|
329
|
+
|
|
330
|
+
// src/messages.ts
|
|
331
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
332
|
+
import path4 from "path";
|
|
333
|
+
import {
|
|
334
|
+
createTranslationJsonSchema
|
|
335
|
+
} from "@better-translate/core";
|
|
336
|
+
function getExportedMessages(module, sourceLocale) {
|
|
337
|
+
const value = module.default ?? module[sourceLocale];
|
|
338
|
+
assert(
|
|
339
|
+
value !== void 0,
|
|
340
|
+
`The source translation module must export a default object or a named "${sourceLocale}" export.`
|
|
341
|
+
);
|
|
342
|
+
assertTranslationMessages(
|
|
343
|
+
value,
|
|
344
|
+
"The source translation file must export nested objects with string leaves only."
|
|
345
|
+
);
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
348
|
+
async function loadSourceMessages(sourcePath, sourceLocale) {
|
|
349
|
+
const extension = path4.extname(sourcePath);
|
|
350
|
+
const sourceText = await readFile2(sourcePath, "utf8");
|
|
351
|
+
if (extension === ".json") {
|
|
352
|
+
const parsed = JSON.parse(sourceText);
|
|
353
|
+
assertTranslationMessages(
|
|
354
|
+
parsed,
|
|
355
|
+
"The source JSON file must contain nested objects with string leaves only."
|
|
356
|
+
);
|
|
357
|
+
return {
|
|
358
|
+
format: "json",
|
|
359
|
+
keyPaths: flattenTranslationKeys(parsed),
|
|
360
|
+
messages: parsed,
|
|
361
|
+
schema: createTranslationJsonSchema(parsed),
|
|
362
|
+
sourceText,
|
|
363
|
+
sourcePath
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
assert(
|
|
367
|
+
extension === ".ts",
|
|
368
|
+
`Unsupported source translation extension "${extension}". Use .json or .ts.`
|
|
369
|
+
);
|
|
370
|
+
const module = await importModule(sourcePath);
|
|
371
|
+
const messages = getExportedMessages(module, sourceLocale);
|
|
372
|
+
return {
|
|
373
|
+
format: "ts",
|
|
374
|
+
keyPaths: flattenTranslationKeys(messages),
|
|
375
|
+
messages,
|
|
376
|
+
schema: createTranslationJsonSchema(messages),
|
|
377
|
+
sourceText,
|
|
378
|
+
sourcePath
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function replaceLocaleSegment(basename, sourceLocale, targetLocale) {
|
|
382
|
+
if (basename === sourceLocale) {
|
|
383
|
+
return targetLocale;
|
|
384
|
+
}
|
|
385
|
+
const escapedSourceLocale = sourceLocale.replace(
|
|
386
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
387
|
+
"\\$&"
|
|
388
|
+
);
|
|
389
|
+
const pattern = new RegExp(`(^|[._-])${escapedSourceLocale}(?=$|[._-])`);
|
|
390
|
+
const match = basename.match(pattern);
|
|
391
|
+
if (!match) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return basename.replace(pattern, `${match[1]}${targetLocale}`);
|
|
395
|
+
}
|
|
396
|
+
function deriveTargetMessagesPath(sourcePath, sourceLocale, targetLocale) {
|
|
397
|
+
const extension = path4.extname(sourcePath);
|
|
398
|
+
const basename = path4.basename(sourcePath, extension);
|
|
399
|
+
const replaced = replaceLocaleSegment(basename, sourceLocale, targetLocale);
|
|
400
|
+
assert(
|
|
401
|
+
replaced,
|
|
402
|
+
`Could not derive a target messages filename from "${sourcePath}". The basename must contain the source locale "${sourceLocale}".`
|
|
403
|
+
);
|
|
404
|
+
return path4.join(path4.dirname(sourcePath), `${replaced}${extension}`);
|
|
405
|
+
}
|
|
406
|
+
function formatTsPropertyKey(key) {
|
|
407
|
+
if (key === "__proto__") {
|
|
408
|
+
return JSON.stringify(key);
|
|
409
|
+
}
|
|
410
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(key) ? key : JSON.stringify(key);
|
|
411
|
+
}
|
|
412
|
+
function serializeTsObject(messages, indentLevel = 0) {
|
|
413
|
+
const entries = Object.entries(messages);
|
|
414
|
+
if (entries.length === 0) {
|
|
415
|
+
return "{}";
|
|
416
|
+
}
|
|
417
|
+
const indent = " ".repeat(indentLevel);
|
|
418
|
+
const childIndent = " ".repeat(indentLevel + 1);
|
|
419
|
+
const lines = entries.map(([key, value]) => {
|
|
420
|
+
const propertyKey = formatTsPropertyKey(key);
|
|
421
|
+
if (typeof value === "string") {
|
|
422
|
+
return `${childIndent}${propertyKey}: ${JSON.stringify(value)},`;
|
|
423
|
+
}
|
|
424
|
+
return `${childIndent}${propertyKey}: ${serializeTsObject(
|
|
425
|
+
value,
|
|
426
|
+
indentLevel + 1
|
|
427
|
+
)},`;
|
|
428
|
+
});
|
|
429
|
+
return `{
|
|
430
|
+
${lines.join("\n")}
|
|
431
|
+
${indent}}`;
|
|
432
|
+
}
|
|
433
|
+
function serializeMessages(messages, format, locale) {
|
|
434
|
+
if (format === "json") {
|
|
435
|
+
return `${JSON.stringify(messages, null, 2)}
|
|
436
|
+
`;
|
|
437
|
+
}
|
|
438
|
+
const identifier = toJavaScriptIdentifier(locale);
|
|
439
|
+
const objectLiteral = serializeTsObject(messages);
|
|
440
|
+
return `// generated by @better-translate/cli
|
|
441
|
+
export const ${identifier} = ${objectLiteral} as const;
|
|
442
|
+
|
|
443
|
+
export default ${identifier};
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/extract.ts
|
|
448
|
+
var DEFAULT_MAX_LENGTH = 40;
|
|
449
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
450
|
+
".cjs",
|
|
451
|
+
".cts",
|
|
452
|
+
".js",
|
|
453
|
+
".jsx",
|
|
454
|
+
".mjs",
|
|
455
|
+
".mts",
|
|
456
|
+
".ts",
|
|
457
|
+
".tsx"
|
|
458
|
+
]);
|
|
459
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
460
|
+
".git",
|
|
461
|
+
".next",
|
|
462
|
+
".turbo",
|
|
463
|
+
"build",
|
|
464
|
+
"coverage",
|
|
465
|
+
"dist",
|
|
466
|
+
"node_modules"
|
|
467
|
+
]);
|
|
468
|
+
var NAMESPACE_ROOTS = /* @__PURE__ */ new Set([
|
|
469
|
+
"app",
|
|
470
|
+
"components",
|
|
471
|
+
"lib",
|
|
472
|
+
"pages",
|
|
473
|
+
"routes",
|
|
474
|
+
"src"
|
|
475
|
+
]);
|
|
476
|
+
function createDefaultLogger() {
|
|
477
|
+
return {
|
|
478
|
+
error(message) {
|
|
479
|
+
console.error(message);
|
|
480
|
+
},
|
|
481
|
+
info(message) {
|
|
482
|
+
console.log(message);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
function cloneMessages(messages) {
|
|
487
|
+
return JSON.parse(JSON.stringify(messages));
|
|
488
|
+
}
|
|
489
|
+
function slugifySegment(value) {
|
|
490
|
+
const normalized = value.normalize("NFKD").replace(/\p{M}+/gu, "").trim();
|
|
491
|
+
const parts = normalized.split(/[^\p{L}\p{N}]+/u).map((part) => part.trim()).filter(Boolean);
|
|
492
|
+
if (parts.length === 0) {
|
|
493
|
+
return "message";
|
|
494
|
+
}
|
|
495
|
+
const [head, ...tail] = parts;
|
|
496
|
+
return `${head.toLowerCase()}${tail.map((part) => `${part[0].toUpperCase()}${part.slice(1).toLowerCase()}`).join("")}`;
|
|
497
|
+
}
|
|
498
|
+
function createLeafKey(value, maxLength) {
|
|
499
|
+
const slug = slugifySegment(value);
|
|
500
|
+
if (slug.length <= maxLength) {
|
|
501
|
+
return slug;
|
|
502
|
+
}
|
|
503
|
+
return slug.slice(0, maxLength);
|
|
504
|
+
}
|
|
505
|
+
function deriveNamespace(configDirectory, sourcePath) {
|
|
506
|
+
const relativePath = path5.relative(configDirectory, sourcePath);
|
|
507
|
+
const withoutExtension = relativePath.slice(
|
|
508
|
+
0,
|
|
509
|
+
relativePath.length - path5.extname(relativePath).length
|
|
510
|
+
);
|
|
511
|
+
const rawSegments = withoutExtension.split(path5.sep).map((segment) => segment.trim()).filter(Boolean);
|
|
512
|
+
const segments = [...rawSegments];
|
|
513
|
+
while (segments.length > 1 && NAMESPACE_ROOTS.has(segments[0].toLowerCase())) {
|
|
514
|
+
segments.shift();
|
|
515
|
+
}
|
|
516
|
+
return segments.map((segment) => slugifySegment(segment)).join(".");
|
|
517
|
+
}
|
|
518
|
+
function getMessageValue(messages, keyPath) {
|
|
519
|
+
let current = messages;
|
|
520
|
+
for (const segment of keyPath.split(".")) {
|
|
521
|
+
if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
522
|
+
return void 0;
|
|
523
|
+
}
|
|
524
|
+
current = current[segment];
|
|
525
|
+
}
|
|
526
|
+
return typeof current === "string" ? current : void 0;
|
|
527
|
+
}
|
|
528
|
+
function setMessageValue(messages, keyPath, value, logWarning) {
|
|
529
|
+
const segments = keyPath.split(".");
|
|
530
|
+
let current = messages;
|
|
531
|
+
for (const segment of segments.slice(0, -1)) {
|
|
532
|
+
const existing = Object.prototype.hasOwnProperty.call(current, segment) ? current[segment] : void 0;
|
|
533
|
+
if (existing !== void 0 && !isRecord(existing)) {
|
|
534
|
+
logWarning?.(
|
|
535
|
+
`key "${keyPath}" conflicts with existing leaf value at segment "${segment}"; skipping.`
|
|
536
|
+
);
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
if (!isRecord(existing)) {
|
|
540
|
+
current[segment] = {};
|
|
541
|
+
}
|
|
542
|
+
current = current[segment];
|
|
543
|
+
}
|
|
544
|
+
const finalSegment = segments[segments.length - 1];
|
|
545
|
+
const finalExisting = Object.prototype.hasOwnProperty.call(current, finalSegment) ? current[finalSegment] : void 0;
|
|
546
|
+
if (isRecord(finalExisting)) {
|
|
547
|
+
logWarning?.(
|
|
548
|
+
`key "${keyPath}" conflicts with existing nested object at "${finalSegment}"; skipping.`
|
|
549
|
+
);
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
current[finalSegment] = value;
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
function createScriptKind(sourcePath) {
|
|
556
|
+
const extension = path5.extname(sourcePath);
|
|
557
|
+
switch (extension) {
|
|
558
|
+
case ".js":
|
|
559
|
+
case ".cjs":
|
|
560
|
+
case ".mjs":
|
|
561
|
+
return ts2.ScriptKind.JS;
|
|
562
|
+
case ".jsx":
|
|
563
|
+
return ts2.ScriptKind.JSX;
|
|
564
|
+
case ".tsx":
|
|
565
|
+
return ts2.ScriptKind.TSX;
|
|
566
|
+
default:
|
|
567
|
+
return ts2.ScriptKind.TS;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function getLiteralArgument(node) {
|
|
571
|
+
if (ts2.isStringLiteral(node)) {
|
|
572
|
+
return node.text;
|
|
573
|
+
}
|
|
574
|
+
if (ts2.isNoSubstitutionTemplateLiteral(node)) {
|
|
575
|
+
return node.text;
|
|
576
|
+
}
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
function getObjectPropertyName(node) {
|
|
580
|
+
if (ts2.isIdentifier(node) || ts2.isStringLiteral(node)) {
|
|
581
|
+
return node.text;
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
function getBtMarkerState(node) {
|
|
586
|
+
if (!node) {
|
|
587
|
+
return "none";
|
|
588
|
+
}
|
|
589
|
+
if (!ts2.isObjectLiteralExpression(node)) {
|
|
590
|
+
return "none";
|
|
591
|
+
}
|
|
592
|
+
let sawBt = false;
|
|
593
|
+
for (const property of node.properties) {
|
|
594
|
+
if (!ts2.isPropertyAssignment(property)) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const name = getObjectPropertyName(property.name);
|
|
598
|
+
if (name !== "bt") {
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
sawBt = true;
|
|
602
|
+
if (property.initializer.kind !== ts2.SyntaxKind.TrueKeyword) {
|
|
603
|
+
return "invalid";
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return sawBt ? "valid" : "none";
|
|
607
|
+
}
|
|
608
|
+
function buildPreservedOptionsText(sourceFile, node) {
|
|
609
|
+
if (!node || !ts2.isObjectLiteralExpression(node)) {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
const remainingProperties = node.properties.filter((property) => {
|
|
613
|
+
if (!ts2.isPropertyAssignment(property)) {
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
const name = getObjectPropertyName(property.name);
|
|
617
|
+
return name !== "bt";
|
|
618
|
+
});
|
|
619
|
+
if (remainingProperties.length === 0) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const printer = ts2.createPrinter();
|
|
623
|
+
const objectLiteral = ts2.factory.updateObjectLiteralExpression(
|
|
624
|
+
node,
|
|
625
|
+
remainingProperties
|
|
626
|
+
);
|
|
627
|
+
return printer.printNode(
|
|
628
|
+
ts2.EmitHint.Unspecified,
|
|
629
|
+
objectLiteral,
|
|
630
|
+
sourceFile
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
function createWarning(sourceFile, node, message) {
|
|
634
|
+
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
635
|
+
return `${sourceFile.fileName}:${position.line + 1}: ${message}`;
|
|
636
|
+
}
|
|
637
|
+
function analyzeFile(options) {
|
|
638
|
+
const { configDirectory, maxLength, messages, sourcePath, sourceText } = options;
|
|
639
|
+
const sourceFile = ts2.createSourceFile(
|
|
640
|
+
sourcePath,
|
|
641
|
+
sourceText,
|
|
642
|
+
ts2.ScriptTarget.Latest,
|
|
643
|
+
true,
|
|
644
|
+
createScriptKind(sourcePath)
|
|
645
|
+
);
|
|
646
|
+
const namespace = deriveNamespace(configDirectory, sourcePath);
|
|
647
|
+
const literalCandidates = [];
|
|
648
|
+
const warnings = [];
|
|
649
|
+
const seenWarnings = /* @__PURE__ */ new Set();
|
|
650
|
+
const logWarning = (warning) => {
|
|
651
|
+
if (seenWarnings.has(warning)) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
seenWarnings.add(warning);
|
|
655
|
+
warnings.push(warning);
|
|
656
|
+
};
|
|
657
|
+
const visit = (node) => {
|
|
658
|
+
if (!ts2.isCallExpression(node)) {
|
|
659
|
+
ts2.forEachChild(node, visit);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const callee = node.expression;
|
|
663
|
+
const isIdentifierCall = ts2.isIdentifier(callee) && callee.text === "t";
|
|
664
|
+
const isPropertyCall = ts2.isPropertyAccessExpression(callee) && callee.name.text === "t";
|
|
665
|
+
if (!isIdentifierCall && !isPropertyCall) {
|
|
666
|
+
ts2.forEachChild(node, visit);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const markerState = getBtMarkerState(node.arguments[1]);
|
|
670
|
+
if (markerState === "none") {
|
|
671
|
+
ts2.forEachChild(node, visit);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (markerState === "invalid") {
|
|
675
|
+
logWarning(
|
|
676
|
+
createWarning(
|
|
677
|
+
sourceFile,
|
|
678
|
+
node,
|
|
679
|
+
"skipped marked t() call because the bt marker must be written as bt: true."
|
|
680
|
+
)
|
|
681
|
+
);
|
|
682
|
+
ts2.forEachChild(node, visit);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const firstArgument = node.arguments[0];
|
|
686
|
+
if (!firstArgument) {
|
|
687
|
+
logWarning(
|
|
688
|
+
createWarning(
|
|
689
|
+
sourceFile,
|
|
690
|
+
node,
|
|
691
|
+
"skipped marked t() call because the first argument is missing."
|
|
692
|
+
)
|
|
693
|
+
);
|
|
694
|
+
ts2.forEachChild(node, visit);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const literalValue = getLiteralArgument(firstArgument);
|
|
698
|
+
if (literalValue === null) {
|
|
699
|
+
logWarning(
|
|
700
|
+
createWarning(
|
|
701
|
+
sourceFile,
|
|
702
|
+
node,
|
|
703
|
+
"skipped marked t() call because the first argument is not a static string literal."
|
|
704
|
+
)
|
|
705
|
+
);
|
|
706
|
+
ts2.forEachChild(node, visit);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
const leafKey = createLeafKey(literalValue, maxLength);
|
|
710
|
+
const fullKey = namespace ? `${namespace}.${leafKey}` : leafKey;
|
|
711
|
+
const nodeStart = node.getStart(sourceFile);
|
|
712
|
+
const nodeEnd = node.getEnd();
|
|
713
|
+
const overlapping = literalCandidates.some((existing) => {
|
|
714
|
+
const existingStart = existing.callExpression.getStart(sourceFile);
|
|
715
|
+
const existingEnd = existing.callExpression.getEnd();
|
|
716
|
+
return nodeStart < existingEnd && nodeEnd > existingStart;
|
|
717
|
+
});
|
|
718
|
+
if (overlapping) {
|
|
719
|
+
logWarning(
|
|
720
|
+
createWarning(
|
|
721
|
+
sourceFile,
|
|
722
|
+
node,
|
|
723
|
+
"skipped marked t() call because it overlaps with another marked t() call."
|
|
724
|
+
)
|
|
725
|
+
);
|
|
726
|
+
ts2.forEachChild(node, visit);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
literalCandidates.push({
|
|
730
|
+
callExpression: node,
|
|
731
|
+
fullKey,
|
|
732
|
+
rewrittenOptionsText: buildPreservedOptionsText(sourceFile, node.arguments[1]),
|
|
733
|
+
sourceValue: literalValue
|
|
734
|
+
});
|
|
735
|
+
ts2.forEachChild(node, visit);
|
|
736
|
+
};
|
|
737
|
+
visit(sourceFile);
|
|
738
|
+
const duplicateStrings = /* @__PURE__ */ new Set();
|
|
739
|
+
const duplicateCounts = /* @__PURE__ */ new Map();
|
|
740
|
+
for (const candidate of literalCandidates) {
|
|
741
|
+
duplicateCounts.set(
|
|
742
|
+
candidate.sourceValue,
|
|
743
|
+
(duplicateCounts.get(candidate.sourceValue) ?? 0) + 1
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
for (const [sourceValue, count] of duplicateCounts) {
|
|
747
|
+
if (count > 1) {
|
|
748
|
+
duplicateStrings.add(sourceValue);
|
|
749
|
+
logWarning(
|
|
750
|
+
`${sourcePath}: skipped ${count} marked t() calls for "${sourceValue}" because the same string appears more than once in the file.`
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const collisionMap = /* @__PURE__ */ new Map();
|
|
755
|
+
for (const candidate of literalCandidates) {
|
|
756
|
+
const values = collisionMap.get(candidate.fullKey) ?? /* @__PURE__ */ new Set();
|
|
757
|
+
values.add(candidate.sourceValue);
|
|
758
|
+
collisionMap.set(candidate.fullKey, values);
|
|
759
|
+
}
|
|
760
|
+
const collidedKeys = /* @__PURE__ */ new Set();
|
|
761
|
+
for (const [key, values] of collisionMap) {
|
|
762
|
+
if (values.size > 1) {
|
|
763
|
+
collidedKeys.add(key);
|
|
764
|
+
logWarning(
|
|
765
|
+
`${sourcePath}: skipped marked t() calls for "${key}" because multiple strings would generate the same key.`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const successfulCandidates = [];
|
|
770
|
+
const addedKeys = [];
|
|
771
|
+
for (const candidate of literalCandidates) {
|
|
772
|
+
if (duplicateStrings.has(candidate.sourceValue)) {
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (collidedKeys.has(candidate.fullKey)) {
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const existingValue = getMessageValue(messages, candidate.fullKey);
|
|
779
|
+
if (existingValue === void 0) {
|
|
780
|
+
const written = setMessageValue(
|
|
781
|
+
messages,
|
|
782
|
+
candidate.fullKey,
|
|
783
|
+
candidate.sourceValue,
|
|
784
|
+
logWarning
|
|
785
|
+
);
|
|
786
|
+
if (written) {
|
|
787
|
+
addedKeys.push(candidate.fullKey);
|
|
788
|
+
successfulCandidates.push(candidate);
|
|
789
|
+
}
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
if (existingValue !== candidate.sourceValue) {
|
|
793
|
+
logWarning(
|
|
794
|
+
`${sourcePath}: skipped marked t() call for "${candidate.fullKey}" because the key already exists with a different value.`
|
|
795
|
+
);
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
successfulCandidates.push(candidate);
|
|
799
|
+
}
|
|
800
|
+
if (successfulCandidates.length === 0) {
|
|
801
|
+
return {
|
|
802
|
+
addedKeys,
|
|
803
|
+
updatedSource: null,
|
|
804
|
+
warnings
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const updatedSource = successfulCandidates.sort(
|
|
808
|
+
(left, right) => right.callExpression.getStart(sourceFile) - left.callExpression.getStart(sourceFile)
|
|
809
|
+
).reduce((currentText, candidate) => {
|
|
810
|
+
const start = candidate.callExpression.getStart(sourceFile);
|
|
811
|
+
const end = candidate.callExpression.getEnd();
|
|
812
|
+
const expressionText = candidate.callExpression.expression.getText(sourceFile);
|
|
813
|
+
const replacement = candidate.rewrittenOptionsText ? `${expressionText}(${JSON.stringify(candidate.fullKey)}, ${candidate.rewrittenOptionsText})` : `${expressionText}(${JSON.stringify(candidate.fullKey)})`;
|
|
814
|
+
return `${currentText.slice(0, start)}${replacement}${currentText.slice(end)}`;
|
|
815
|
+
}, sourceText);
|
|
816
|
+
return {
|
|
817
|
+
addedKeys,
|
|
818
|
+
updatedSource: updatedSource === sourceText ? null : updatedSource,
|
|
819
|
+
warnings
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
async function collectSourceFiles(directory, ignoredPaths) {
|
|
823
|
+
const entries = (await readdir(directory, {
|
|
824
|
+
withFileTypes: true
|
|
825
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
826
|
+
const files = [];
|
|
827
|
+
for (const entry of entries) {
|
|
828
|
+
const entryPath = path5.join(directory, entry.name);
|
|
829
|
+
if (ignoredPaths.has(entryPath)) {
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (entry.isDirectory()) {
|
|
833
|
+
if (IGNORED_DIRECTORIES.has(entry.name)) {
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
files.push(...await collectSourceFiles(entryPath, ignoredPaths));
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (SOURCE_EXTENSIONS.has(path5.extname(entry.name))) {
|
|
840
|
+
files.push(entryPath);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return files;
|
|
844
|
+
}
|
|
845
|
+
async function extractProject(options = {}) {
|
|
846
|
+
const logger = options.logger ?? createDefaultLogger();
|
|
847
|
+
const dryRun = options.dryRun ?? false;
|
|
848
|
+
const maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;
|
|
849
|
+
assert(
|
|
850
|
+
Number.isInteger(maxLength) && maxLength > 0,
|
|
851
|
+
"--max-length must be a positive integer."
|
|
852
|
+
);
|
|
853
|
+
logger.info("Loading Better Translate config...");
|
|
854
|
+
const loadedConfig = await loadCliConfig({
|
|
855
|
+
configPath: options.configPath,
|
|
856
|
+
cwd: options.cwd
|
|
857
|
+
});
|
|
858
|
+
logger.info(`Using config: ${loadedConfig.path}`);
|
|
859
|
+
logger.info(`Source locale: ${loadedConfig.config.sourceLocale}`);
|
|
860
|
+
const loadedSourceMessages = await loadSourceMessages(
|
|
861
|
+
loadedConfig.config.messages.entry,
|
|
862
|
+
loadedConfig.config.sourceLocale
|
|
863
|
+
);
|
|
864
|
+
const messages = cloneMessages(loadedSourceMessages.messages);
|
|
865
|
+
const ignoredPaths = /* @__PURE__ */ new Set([loadedConfig.config.messages.entry]);
|
|
866
|
+
const sourcePaths = await collectSourceFiles(
|
|
867
|
+
loadedConfig.directory,
|
|
868
|
+
ignoredPaths
|
|
869
|
+
);
|
|
870
|
+
const rewrittenPaths = [];
|
|
871
|
+
const warnings = [];
|
|
872
|
+
const updatedKeys = [];
|
|
873
|
+
for (const sourcePath of sourcePaths) {
|
|
874
|
+
const sourceText = await readFile3(sourcePath, "utf8");
|
|
875
|
+
const analysis = analyzeFile({
|
|
876
|
+
configDirectory: loadedConfig.directory,
|
|
877
|
+
maxLength,
|
|
878
|
+
messages,
|
|
879
|
+
sourcePath,
|
|
880
|
+
sourceText
|
|
881
|
+
});
|
|
882
|
+
warnings.push(...analysis.warnings);
|
|
883
|
+
updatedKeys.push(...analysis.addedKeys);
|
|
884
|
+
if (!analysis.updatedSource) {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
rewrittenPaths.push(sourcePath);
|
|
888
|
+
if (dryRun) {
|
|
889
|
+
logger.info(`[dry-run] rewrote ${sourcePath}`);
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
await writeFile2(sourcePath, analysis.updatedSource, "utf8");
|
|
893
|
+
logger.info(`rewrote ${sourcePath}`);
|
|
894
|
+
}
|
|
895
|
+
const hasMessageChanges = updatedKeys.length > 0 || JSON.stringify(messages) !== JSON.stringify(loadedSourceMessages.messages);
|
|
896
|
+
if (hasMessageChanges) {
|
|
897
|
+
const serialized = serializeMessages(
|
|
898
|
+
messages,
|
|
899
|
+
loadedSourceMessages.format,
|
|
900
|
+
loadedConfig.config.sourceLocale
|
|
901
|
+
);
|
|
902
|
+
if (loadedSourceMessages.format === "ts" && !loadedSourceMessages.sourceText.startsWith(
|
|
903
|
+
"// generated by @better-translate/cli"
|
|
904
|
+
)) {
|
|
905
|
+
logger.info(
|
|
906
|
+
`warn source messages file "${loadedConfig.config.messages.entry}" was not generated by the CLI; skipping update to avoid overwriting manual content. Use a .json source file or replace the file with a CLI-generated one.`
|
|
907
|
+
);
|
|
908
|
+
} else if (dryRun) {
|
|
909
|
+
logger.info(
|
|
910
|
+
`[dry-run] updated messages ${loadedConfig.config.messages.entry}`
|
|
911
|
+
);
|
|
912
|
+
} else {
|
|
913
|
+
await writeFile2(loadedConfig.config.messages.entry, serialized, "utf8");
|
|
914
|
+
logger.info(`updated messages ${loadedConfig.config.messages.entry}`);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
for (const warning of warnings) {
|
|
918
|
+
logger.error(`warn ${warning}`);
|
|
919
|
+
}
|
|
920
|
+
const fileLabel = rewrittenPaths.length === 1 ? "file" : "files";
|
|
921
|
+
const keyLabel = updatedKeys.length === 1 ? "key" : "keys";
|
|
922
|
+
logger.info(
|
|
923
|
+
`processed ${rewrittenPaths.length} ${fileLabel} and synced ${updatedKeys.length} ${keyLabel}.`
|
|
924
|
+
);
|
|
925
|
+
return {
|
|
926
|
+
dryRun,
|
|
927
|
+
filePaths: rewrittenPaths,
|
|
928
|
+
loadedConfig,
|
|
929
|
+
updatedMessages: updatedKeys,
|
|
930
|
+
warnings
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
325
934
|
// src/generate.ts
|
|
326
|
-
import { mkdir, writeFile as
|
|
327
|
-
import
|
|
935
|
+
import { mkdir, writeFile as writeFile3 } from "fs/promises";
|
|
936
|
+
import path7 from "path";
|
|
328
937
|
|
|
329
938
|
// src/markdown.ts
|
|
330
|
-
import { readdir, readFile as
|
|
331
|
-
import
|
|
939
|
+
import { readdir as readdir2, readFile as readFile4 } from "fs/promises";
|
|
940
|
+
import path6 from "path";
|
|
332
941
|
import matter from "gray-matter";
|
|
333
942
|
async function walkDirectory(directory) {
|
|
334
|
-
const entries = await
|
|
943
|
+
const entries = await readdir2(directory, {
|
|
335
944
|
withFileTypes: true
|
|
336
945
|
});
|
|
337
946
|
const files = await Promise.all(
|
|
338
947
|
entries.map(async (entry) => {
|
|
339
|
-
const entryPath =
|
|
948
|
+
const entryPath = path6.join(directory, entry.name);
|
|
340
949
|
if (entry.isDirectory()) {
|
|
341
950
|
return walkDirectory(entryPath);
|
|
342
951
|
}
|
|
@@ -422,11 +1031,11 @@ async function listMarkdownSourceFiles(rootDir, extensions) {
|
|
|
422
1031
|
).sort();
|
|
423
1032
|
}
|
|
424
1033
|
async function loadMarkdownDocument(rootDir, sourcePath) {
|
|
425
|
-
const sourceText = await
|
|
1034
|
+
const sourceText = await readFile4(sourcePath, "utf8");
|
|
426
1035
|
const parsed = matter(sourceText);
|
|
427
1036
|
const frontmatter = isRecord(parsed.data) ? parsed.data : {};
|
|
428
1037
|
const frontmatterStrings = extractFrontmatterStrings(frontmatter);
|
|
429
|
-
const relativePath =
|
|
1038
|
+
const relativePath = path6.relative(rootDir, sourcePath).split(path6.sep).join("/");
|
|
430
1039
|
return {
|
|
431
1040
|
body: parsed.content,
|
|
432
1041
|
frontmatter,
|
|
@@ -438,109 +1047,20 @@ async function loadMarkdownDocument(rootDir, sourcePath) {
|
|
|
438
1047
|
};
|
|
439
1048
|
}
|
|
440
1049
|
function deriveTargetMarkdownRoot(rootDir, sourceLocale, targetLocale) {
|
|
441
|
-
const basename =
|
|
1050
|
+
const basename = path6.basename(rootDir);
|
|
442
1051
|
assert(
|
|
443
1052
|
basename === sourceLocale,
|
|
444
1053
|
`markdown.rootDir must end with the source locale "${sourceLocale}" so the CLI can mirror sibling locale folders.`
|
|
445
1054
|
);
|
|
446
|
-
return
|
|
1055
|
+
return path6.join(path6.dirname(rootDir), targetLocale);
|
|
447
1056
|
}
|
|
448
1057
|
function deriveTargetMarkdownPath(rootDir, sourceLocale, targetLocale, relativePath) {
|
|
449
|
-
return
|
|
1058
|
+
return path6.join(
|
|
450
1059
|
deriveTargetMarkdownRoot(rootDir, sourceLocale, targetLocale),
|
|
451
1060
|
relativePath
|
|
452
1061
|
);
|
|
453
1062
|
}
|
|
454
1063
|
|
|
455
|
-
// src/messages.ts
|
|
456
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
457
|
-
import path5 from "path";
|
|
458
|
-
import {
|
|
459
|
-
createTranslationJsonSchema
|
|
460
|
-
} from "@better-translate/core";
|
|
461
|
-
function getExportedMessages(module, sourceLocale) {
|
|
462
|
-
const value = module.default ?? module[sourceLocale];
|
|
463
|
-
assert(
|
|
464
|
-
value !== void 0,
|
|
465
|
-
`The source translation module must export a default object or a named "${sourceLocale}" export.`
|
|
466
|
-
);
|
|
467
|
-
assertTranslationMessages(
|
|
468
|
-
value,
|
|
469
|
-
"The source translation file must export nested objects with string leaves only."
|
|
470
|
-
);
|
|
471
|
-
return value;
|
|
472
|
-
}
|
|
473
|
-
async function loadSourceMessages(sourcePath, sourceLocale) {
|
|
474
|
-
const extension = path5.extname(sourcePath);
|
|
475
|
-
const sourceText = await readFile3(sourcePath, "utf8");
|
|
476
|
-
if (extension === ".json") {
|
|
477
|
-
const parsed = JSON.parse(sourceText);
|
|
478
|
-
assertTranslationMessages(
|
|
479
|
-
parsed,
|
|
480
|
-
"The source JSON file must contain nested objects with string leaves only."
|
|
481
|
-
);
|
|
482
|
-
return {
|
|
483
|
-
format: "json",
|
|
484
|
-
keyPaths: flattenTranslationKeys(parsed),
|
|
485
|
-
messages: parsed,
|
|
486
|
-
schema: createTranslationJsonSchema(parsed),
|
|
487
|
-
sourceText,
|
|
488
|
-
sourcePath
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
assert(
|
|
492
|
-
extension === ".ts",
|
|
493
|
-
`Unsupported source translation extension "${extension}". Use .json or .ts.`
|
|
494
|
-
);
|
|
495
|
-
const module = await importModule(sourcePath);
|
|
496
|
-
const messages = getExportedMessages(module, sourceLocale);
|
|
497
|
-
return {
|
|
498
|
-
format: "ts",
|
|
499
|
-
keyPaths: flattenTranslationKeys(messages),
|
|
500
|
-
messages,
|
|
501
|
-
schema: createTranslationJsonSchema(messages),
|
|
502
|
-
sourceText,
|
|
503
|
-
sourcePath
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
function replaceLocaleSegment(basename, sourceLocale, targetLocale) {
|
|
507
|
-
if (basename === sourceLocale) {
|
|
508
|
-
return targetLocale;
|
|
509
|
-
}
|
|
510
|
-
const escapedSourceLocale = sourceLocale.replace(
|
|
511
|
-
/[.*+?^${}()|[\]\\]/g,
|
|
512
|
-
"\\$&"
|
|
513
|
-
);
|
|
514
|
-
const pattern = new RegExp(`(^|[._-])${escapedSourceLocale}(?=$|[._-])`);
|
|
515
|
-
const match = basename.match(pattern);
|
|
516
|
-
if (!match) {
|
|
517
|
-
return null;
|
|
518
|
-
}
|
|
519
|
-
return basename.replace(pattern, `${match[1]}${targetLocale}`);
|
|
520
|
-
}
|
|
521
|
-
function deriveTargetMessagesPath(sourcePath, sourceLocale, targetLocale) {
|
|
522
|
-
const extension = path5.extname(sourcePath);
|
|
523
|
-
const basename = path5.basename(sourcePath, extension);
|
|
524
|
-
const replaced = replaceLocaleSegment(basename, sourceLocale, targetLocale);
|
|
525
|
-
assert(
|
|
526
|
-
replaced,
|
|
527
|
-
`Could not derive a target messages filename from "${sourcePath}". The basename must contain the source locale "${sourceLocale}".`
|
|
528
|
-
);
|
|
529
|
-
return path5.join(path5.dirname(sourcePath), `${replaced}${extension}`);
|
|
530
|
-
}
|
|
531
|
-
function serializeMessages(messages, format, locale) {
|
|
532
|
-
if (format === "json") {
|
|
533
|
-
return `${JSON.stringify(messages, null, 2)}
|
|
534
|
-
`;
|
|
535
|
-
}
|
|
536
|
-
const identifier = toJavaScriptIdentifier(locale);
|
|
537
|
-
const objectLiteral = JSON.stringify(messages, null, 2);
|
|
538
|
-
return `export const ${identifier} = ${objectLiteral} as const;
|
|
539
|
-
|
|
540
|
-
export default ${identifier};
|
|
541
|
-
`;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
1064
|
// src/prompts.ts
|
|
545
1065
|
var MESSAGE_SYSTEM_INSTRUCTIONS = [
|
|
546
1066
|
"You are translating application locale files.",
|
|
@@ -638,10 +1158,10 @@ async function persistWrite(write, options) {
|
|
|
638
1158
|
);
|
|
639
1159
|
return;
|
|
640
1160
|
}
|
|
641
|
-
await mkdir(
|
|
1161
|
+
await mkdir(path7.dirname(write.targetPath), {
|
|
642
1162
|
recursive: true
|
|
643
1163
|
});
|
|
644
|
-
await
|
|
1164
|
+
await writeFile3(write.targetPath, write.content, "utf8");
|
|
645
1165
|
options.logger.info(
|
|
646
1166
|
`wrote ${write.kind}:${write.locale} ${write.targetPath}`
|
|
647
1167
|
);
|
|
@@ -850,5 +1370,6 @@ async function generateProject(options = {}) {
|
|
|
850
1370
|
|
|
851
1371
|
export {
|
|
852
1372
|
loadCliConfig,
|
|
1373
|
+
extractProject,
|
|
853
1374
|
generateProject
|
|
854
1375
|
};
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA2EA,wBAAsB,MAAM,CAC1B,IAAI,WAAwB,EAC5B,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,GACL,OAAO,CAAC,MAAM,CAAC,CAmDjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAkjBpB,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CA6G/B"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { loadCliConfig } from "./config-loader.js";
|
|
2
2
|
export { defineConfig } from "./define-config.js";
|
|
3
|
+
export { extractProject } from "./extract.js";
|
|
3
4
|
export { generateProject } from "./generate.js";
|
|
4
5
|
export { openai } from "./provider-models.js";
|
|
5
|
-
export type { BetterTranslateCliConfig, BetterTranslateCliGatewayConfig, BetterTranslateCliOpenAIConfig, CliLogger, CliWriteOperation, GenerateProjectOptions, GenerateProjectResult, LoadedBetterTranslateCliConfig, MarkdownExtension, OpenAIProviderModelSpec, ResolvedBetterTranslateCliConfig, ResolvedBetterTranslateCliGatewayConfig, ResolvedBetterTranslateCliOpenAIConfig, StructuredGenerationRequest, StructuredGenerator, } from "./types.js";
|
|
6
|
+
export type { BetterTranslateCliConfig, BetterTranslateCliGatewayConfig, BetterTranslateCliOpenAIConfig, CliLogger, ExtractProjectOptions, ExtractProjectResult, CliWriteOperation, GenerateProjectOptions, GenerateProjectResult, LoadedBetterTranslateCliConfig, MarkdownExtension, OpenAIProviderModelSpec, ResolvedBetterTranslateCliConfig, ResolvedBetterTranslateCliGatewayConfig, ResolvedBetterTranslateCliOpenAIConfig, StructuredGenerationRequest, StructuredGenerator, } from "./types.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,YAAY,EACV,wBAAwB,EACxB,+BAA+B,EAC/B,8BAA8B,EAC9B,SAAS,EACT,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,8BAA8B,EAC9B,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,uCAAuC,EACvC,sCAAsC,EACtC,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,YAAY,EACV,wBAAwB,EACxB,+BAA+B,EAC/B,8BAA8B,EAC9B,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,8BAA8B,EAC9B,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,uCAAuC,EACvC,sCAAsC,EACtC,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
+
extractProject,
|
|
2
3
|
generateProject,
|
|
3
4
|
loadCliConfig
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NRZIFUEW.js";
|
|
5
6
|
import {
|
|
6
7
|
defineConfig,
|
|
7
8
|
openai
|
|
8
9
|
} from "./chunk-WMIZO3GE.js";
|
|
9
10
|
export {
|
|
10
11
|
defineConfig,
|
|
12
|
+
extractProject,
|
|
11
13
|
generateProject,
|
|
12
14
|
loadCliConfig,
|
|
13
15
|
openai
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,mBAAmB,IAAI,SAAS,
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,mBAAmB,IAAI,SAAS,CAyK/C"}
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAUhC,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAoBD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAsC/B;AAyBD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,MAAM,CAWR;
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAUhC,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAoBD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAsC/B;AAyBD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,MAAM,CAWR;AAuCD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,CASR"}
|
package/dist/types.d.ts
CHANGED
|
@@ -86,5 +86,19 @@ export interface GenerateProjectResult {
|
|
|
86
86
|
loadedConfig: LoadedBetterTranslateCliConfig;
|
|
87
87
|
writes: CliWriteOperation[];
|
|
88
88
|
}
|
|
89
|
+
export interface ExtractProjectOptions {
|
|
90
|
+
configPath?: string;
|
|
91
|
+
cwd?: string;
|
|
92
|
+
dryRun?: boolean;
|
|
93
|
+
logger?: CliLogger;
|
|
94
|
+
maxLength?: number;
|
|
95
|
+
}
|
|
96
|
+
export interface ExtractProjectResult {
|
|
97
|
+
dryRun: boolean;
|
|
98
|
+
filePaths: string[];
|
|
99
|
+
loadedConfig: LoadedBetterTranslateCliConfig;
|
|
100
|
+
updatedMessages: string[];
|
|
101
|
+
warnings: string[];
|
|
102
|
+
}
|
|
89
103
|
export {};
|
|
90
104
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-translate/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "AI-powered translation generation CLI for Better Translate.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
},
|
|
22
22
|
"./package.json": "./package.json"
|
|
23
23
|
},
|
|
24
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
25
29
|
"scripts": {
|
|
26
30
|
"build": "tsup src/index.ts src/config.ts src/bin.ts --format esm --clean && tsc -p tsconfig.build.json",
|
|
27
31
|
"check-types": "tsc --noEmit",
|
|
@@ -43,7 +47,7 @@
|
|
|
43
47
|
"dependencies": {
|
|
44
48
|
"@ai-sdk/openai": "^3.0.41",
|
|
45
49
|
"ai": "^6.0.116",
|
|
46
|
-
"@better-translate/core": "
|
|
50
|
+
"@better-translate/core": "^2.0.0",
|
|
47
51
|
"dotenv": "^17.2.3",
|
|
48
52
|
"gray-matter": "^4.0.3",
|
|
49
53
|
"ora": "^8.2.0",
|
|
@@ -54,5 +58,12 @@
|
|
|
54
58
|
"@repo/typescript-config": "*",
|
|
55
59
|
"@types/node": "^24.5.2"
|
|
56
60
|
},
|
|
57
|
-
"keywords": [
|
|
61
|
+
"keywords": [
|
|
62
|
+
"ai",
|
|
63
|
+
"cli",
|
|
64
|
+
"i18n",
|
|
65
|
+
"l10n",
|
|
66
|
+
"translations",
|
|
67
|
+
"typescript"
|
|
68
|
+
]
|
|
58
69
|
}
|