@better-translate/cli 1.0.1 → 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 CHANGED
@@ -1,5 +1,5 @@
1
1
  # @better-translate/cli
2
2
 
3
- `@better-translate/cli` generates translated message files and localized markdown from one source locale. Use it when you want Better Translate to create or update locale files for you.
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-JFSWNLL6.js";
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 parseArgs(argv) {
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 = parseArgs(args);
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
- await generateProject({
156
- configPath: parsed.configPath,
157
- cwd: options.cwd,
158
- dryRun: parsed.dryRun,
159
- logger: createSpinnerLogger()
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 generation failed: ${error instanceof Error ? error.message : String(error)}`
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 writeFile2 } from "fs/promises";
327
- import path6 from "path";
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 readFile2 } from "fs/promises";
331
- import path4 from "path";
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 readdir(directory, {
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 = path4.join(directory, entry.name);
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 readFile2(sourcePath, "utf8");
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 = path4.relative(rootDir, sourcePath).split(path4.sep).join("/");
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 = path4.basename(rootDir);
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 path4.join(path4.dirname(rootDir), targetLocale);
1055
+ return path6.join(path6.dirname(rootDir), targetLocale);
447
1056
  }
448
1057
  function deriveTargetMarkdownPath(rootDir, sourceLocale, targetLocale, relativePath) {
449
- return path4.join(
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(path6.dirname(write.targetPath), {
1161
+ await mkdir(path7.dirname(write.targetPath), {
642
1162
  recursive: true
643
1163
  });
644
- await writeFile2(write.targetPath, write.content, "utf8");
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":"AAgDA,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,CAoCjB"}
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,3 @@
1
+ import type { ExtractProjectOptions, ExtractProjectResult } from "./types.js";
2
+ export declare function extractProject(options?: ExtractProjectOptions): Promise<ExtractProjectResult>;
3
+ //# sourceMappingURL=extract.d.ts.map
@@ -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
@@ -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-JFSWNLL6.js";
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
@@ -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,CAuI/C"}
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"}
@@ -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;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,CASR"}
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
@@ -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": "1.0.1",
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": ["dist", "LICENSE", "README.md"],
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": "^1.0.0",
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": ["ai", "cli", "i18n", "l10n", "translations", "typescript"]
61
+ "keywords": [
62
+ "ai",
63
+ "cli",
64
+ "i18n",
65
+ "l10n",
66
+ "translations",
67
+ "typescript"
68
+ ]
58
69
  }