transduck 0.6.7 → 0.6.8
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/dist/cli.js +110 -17
- package/package.json +1 -1
- package/src/cli.ts +99 -17
package/dist/cli.js
CHANGED
|
@@ -376,6 +376,19 @@ export async function runScan(opts) {
|
|
|
376
376
|
const store = new TranslationStore(cfg.storagePath);
|
|
377
377
|
await store.initialize();
|
|
378
378
|
const projectContextHash = hash(cfg.projectContext);
|
|
379
|
+
// Initialize shared Postgres store if configured
|
|
380
|
+
let shared = null;
|
|
381
|
+
if (cfg.sharedUrl) {
|
|
382
|
+
try {
|
|
383
|
+
shared = new SharedStore(cfg.sharedUrl);
|
|
384
|
+
await shared.initialize();
|
|
385
|
+
console.log(' Connected to shared Postgres store');
|
|
386
|
+
}
|
|
387
|
+
catch (err) {
|
|
388
|
+
console.warn(` Warning: could not connect to shared store: ${err.message}`);
|
|
389
|
+
shared = null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
379
392
|
let translated = 0;
|
|
380
393
|
let skipped = 0;
|
|
381
394
|
let failed = 0;
|
|
@@ -386,26 +399,68 @@ export async function runScan(opts) {
|
|
|
386
399
|
const sourceKey = entry.one + '\x00' + entry.other;
|
|
387
400
|
const stringContextHash = hash(entry.context ?? '');
|
|
388
401
|
const label = (entry.one ?? '').slice(0, 40);
|
|
402
|
+
const lookupParams = {
|
|
403
|
+
sourceText: sourceKey, sourceLang: cfg.sourceLang,
|
|
404
|
+
projectContextHash, stringContextHash,
|
|
405
|
+
};
|
|
389
406
|
for (const lang of targetLangs) {
|
|
390
407
|
done++;
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
projectContextHash, stringContextHash,
|
|
394
|
-
});
|
|
408
|
+
const langLookup = { ...lookupParams, targetLang: lang };
|
|
409
|
+
const cachedForms = await store.lookupPlural(langLookup);
|
|
395
410
|
if (Object.keys(cachedForms).length > 0) {
|
|
411
|
+
// Ensure shared store has it too
|
|
412
|
+
if (shared) {
|
|
413
|
+
try {
|
|
414
|
+
const sharedForms = await shared.lookupPlural(langLookup);
|
|
415
|
+
if (Object.keys(sharedForms).length === 0) {
|
|
416
|
+
for (const [cat, text] of Object.entries(cachedForms)) {
|
|
417
|
+
await shared.insertPlural({
|
|
418
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
419
|
+
pluralCategory: cat, translatedText: text,
|
|
420
|
+
model: cfg.backendModel, status: 'translated',
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
catch { /* ignore shared store errors */ }
|
|
426
|
+
}
|
|
396
427
|
skipped++;
|
|
397
428
|
console.log(` [${done}/${total}] ${lang} skipped (cached): ${label}`);
|
|
398
429
|
continue;
|
|
399
430
|
}
|
|
431
|
+
// Check shared store before calling backend
|
|
432
|
+
if (shared) {
|
|
433
|
+
try {
|
|
434
|
+
const sharedForms = await shared.lookupPlural(langLookup);
|
|
435
|
+
if (Object.keys(sharedForms).length > 0) {
|
|
436
|
+
for (const [cat, text] of Object.entries(sharedForms)) {
|
|
437
|
+
await store.insertPlural({
|
|
438
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
439
|
+
pluralCategory: cat, translatedText: text,
|
|
440
|
+
model: cfg.backendModel, status: 'translated',
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
skipped++;
|
|
444
|
+
console.log(` [${done}/${total}] ${lang} skipped (shared cache): ${label}`);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch { /* ignore */ }
|
|
449
|
+
}
|
|
400
450
|
try {
|
|
401
451
|
const forms = await backendTranslatePlural(entry.one, entry.other, cfg.sourceLang, lang, cfg.projectContext, entry.context ?? null, cfg);
|
|
402
452
|
for (const [cat, translatedText] of Object.entries(forms)) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
projectContextHash, stringContextHash, stringContext: entry.context ?? '',
|
|
453
|
+
const insertParams = {
|
|
454
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
406
455
|
pluralCategory: cat, translatedText: translatedText,
|
|
407
456
|
model: cfg.backendModel, status: 'translated',
|
|
408
|
-
}
|
|
457
|
+
};
|
|
458
|
+
await store.insertPlural(insertParams);
|
|
459
|
+
if (shared)
|
|
460
|
+
try {
|
|
461
|
+
await shared.insertPlural(insertParams);
|
|
462
|
+
}
|
|
463
|
+
catch { /* ignore */ }
|
|
409
464
|
}
|
|
410
465
|
translated++;
|
|
411
466
|
console.log(` [${done}/${total}] ${lang} translated: ${label}`);
|
|
@@ -419,25 +474,61 @@ export async function runScan(opts) {
|
|
|
419
474
|
else {
|
|
420
475
|
const stringContextHash = hash(entry.context ?? '');
|
|
421
476
|
const label = (entry.text ?? '').slice(0, 40);
|
|
477
|
+
const lookupParams = {
|
|
478
|
+
sourceText: entry.text, sourceLang: cfg.sourceLang,
|
|
479
|
+
projectContextHash, stringContextHash,
|
|
480
|
+
};
|
|
422
481
|
for (const lang of targetLangs) {
|
|
423
482
|
done++;
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
projectContextHash, stringContextHash,
|
|
427
|
-
});
|
|
483
|
+
const langLookup = { ...lookupParams, targetLang: lang };
|
|
484
|
+
const cached = await store.lookup(langLookup);
|
|
428
485
|
if (cached !== null) {
|
|
486
|
+
// Ensure shared store has it too
|
|
487
|
+
if (shared) {
|
|
488
|
+
try {
|
|
489
|
+
const sharedCached = await shared.lookup(langLookup);
|
|
490
|
+
if (sharedCached === null) {
|
|
491
|
+
await shared.insert({
|
|
492
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
493
|
+
translatedText: cached, model: cfg.backendModel, status: 'translated',
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
catch { /* ignore */ }
|
|
498
|
+
}
|
|
429
499
|
skipped++;
|
|
430
500
|
console.log(` [${done}/${total}] ${lang} skipped (cached): ${label}`);
|
|
431
501
|
continue;
|
|
432
502
|
}
|
|
503
|
+
// Check shared store before calling backend
|
|
504
|
+
if (shared) {
|
|
505
|
+
try {
|
|
506
|
+
const sharedCached = await shared.lookup(langLookup);
|
|
507
|
+
if (sharedCached !== null) {
|
|
508
|
+
await store.insert({
|
|
509
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
510
|
+
translatedText: sharedCached, model: cfg.backendModel, status: 'translated',
|
|
511
|
+
});
|
|
512
|
+
skipped++;
|
|
513
|
+
console.log(` [${done}/${total}] ${lang} skipped (shared cache): ${label}`);
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch { /* ignore */ }
|
|
518
|
+
}
|
|
433
519
|
try {
|
|
434
520
|
const result = await backendTranslate(entry.text, cfg.sourceLang, lang, cfg.projectContext, entry.context ?? null, cfg);
|
|
435
521
|
if (validateTranslation(entry.text, result)) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
projectContextHash, stringContextHash, stringContext: entry.context ?? '',
|
|
522
|
+
const insertParams = {
|
|
523
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
439
524
|
translatedText: result, model: cfg.backendModel, status: 'translated',
|
|
440
|
-
}
|
|
525
|
+
};
|
|
526
|
+
await store.insert(insertParams);
|
|
527
|
+
if (shared)
|
|
528
|
+
try {
|
|
529
|
+
await shared.insert(insertParams);
|
|
530
|
+
}
|
|
531
|
+
catch { /* ignore */ }
|
|
441
532
|
translated++;
|
|
442
533
|
console.log(` [${done}/${total}] ${lang} translated: ${label}`);
|
|
443
534
|
}
|
|
@@ -454,6 +545,8 @@ export async function runScan(opts) {
|
|
|
454
545
|
}
|
|
455
546
|
}
|
|
456
547
|
store.close();
|
|
548
|
+
if (shared)
|
|
549
|
+
await shared.close();
|
|
457
550
|
lines.push(`\nTranslated: ${translated} | Skipped: ${skipped} | Failed: ${failed}`);
|
|
458
551
|
} // end if entries.length > 0
|
|
459
552
|
}
|
|
@@ -544,7 +637,7 @@ export async function runStats(opts) {
|
|
|
544
637
|
}
|
|
545
638
|
// CLI entry point
|
|
546
639
|
const program = new Command();
|
|
547
|
-
program.name('transduck').description('AI-native translation tool').version('0.6.
|
|
640
|
+
program.name('transduck').description('AI-native translation tool').version('0.6.8');
|
|
548
641
|
program.command('init')
|
|
549
642
|
.description('Initialize a new transduck project')
|
|
550
643
|
.action(async () => {
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -459,6 +459,19 @@ export async function runScan(opts: ScanOptions): Promise<string> {
|
|
|
459
459
|
await store.initialize();
|
|
460
460
|
const projectContextHash = hash(cfg.projectContext);
|
|
461
461
|
|
|
462
|
+
// Initialize shared Postgres store if configured
|
|
463
|
+
let shared: SharedStore | null = null;
|
|
464
|
+
if (cfg.sharedUrl) {
|
|
465
|
+
try {
|
|
466
|
+
shared = new SharedStore(cfg.sharedUrl);
|
|
467
|
+
await shared.initialize();
|
|
468
|
+
console.log(' Connected to shared Postgres store');
|
|
469
|
+
} catch (err) {
|
|
470
|
+
console.warn(` Warning: could not connect to shared store: ${(err as Error).message}`);
|
|
471
|
+
shared = null;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
462
475
|
let translated = 0;
|
|
463
476
|
let skipped = 0;
|
|
464
477
|
let failed = 0;
|
|
@@ -470,20 +483,56 @@ export async function runScan(opts: ScanOptions): Promise<string> {
|
|
|
470
483
|
const sourceKey = entry.one + '\x00' + entry.other;
|
|
471
484
|
const stringContextHash = hash(entry.context ?? '');
|
|
472
485
|
const label = (entry.one ?? '').slice(0, 40);
|
|
486
|
+
const lookupParams = {
|
|
487
|
+
sourceText: sourceKey, sourceLang: cfg.sourceLang,
|
|
488
|
+
projectContextHash, stringContextHash,
|
|
489
|
+
};
|
|
473
490
|
|
|
474
491
|
for (const lang of targetLangs) {
|
|
475
492
|
done++;
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
projectContextHash, stringContextHash,
|
|
479
|
-
});
|
|
493
|
+
const langLookup = { ...lookupParams, targetLang: lang };
|
|
494
|
+
const cachedForms = await store.lookupPlural(langLookup);
|
|
480
495
|
|
|
481
496
|
if (Object.keys(cachedForms).length > 0) {
|
|
497
|
+
// Ensure shared store has it too
|
|
498
|
+
if (shared) {
|
|
499
|
+
try {
|
|
500
|
+
const sharedForms = await shared.lookupPlural(langLookup);
|
|
501
|
+
if (Object.keys(sharedForms).length === 0) {
|
|
502
|
+
for (const [cat, text] of Object.entries(cachedForms)) {
|
|
503
|
+
await shared.insertPlural({
|
|
504
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
505
|
+
pluralCategory: cat, translatedText: text,
|
|
506
|
+
model: cfg.backendModel, status: 'translated',
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} catch { /* ignore shared store errors */ }
|
|
511
|
+
}
|
|
482
512
|
skipped++;
|
|
483
513
|
console.log(` [${done}/${total}] ${lang} skipped (cached): ${label}`);
|
|
484
514
|
continue;
|
|
485
515
|
}
|
|
486
516
|
|
|
517
|
+
// Check shared store before calling backend
|
|
518
|
+
if (shared) {
|
|
519
|
+
try {
|
|
520
|
+
const sharedForms = await shared.lookupPlural(langLookup);
|
|
521
|
+
if (Object.keys(sharedForms).length > 0) {
|
|
522
|
+
for (const [cat, text] of Object.entries(sharedForms)) {
|
|
523
|
+
await store.insertPlural({
|
|
524
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
525
|
+
pluralCategory: cat, translatedText: text,
|
|
526
|
+
model: cfg.backendModel, status: 'translated',
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
skipped++;
|
|
530
|
+
console.log(` [${done}/${total}] ${lang} skipped (shared cache): ${label}`);
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
} catch { /* ignore */ }
|
|
534
|
+
}
|
|
535
|
+
|
|
487
536
|
try {
|
|
488
537
|
const forms = await backendTranslatePlural(
|
|
489
538
|
entry.one!, entry.other!,
|
|
@@ -492,12 +541,13 @@ export async function runScan(opts: ScanOptions): Promise<string> {
|
|
|
492
541
|
);
|
|
493
542
|
|
|
494
543
|
for (const [cat, translatedText] of Object.entries(forms)) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
projectContextHash, stringContextHash, stringContext: entry.context ?? '',
|
|
544
|
+
const insertParams = {
|
|
545
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
498
546
|
pluralCategory: cat, translatedText: translatedText as string,
|
|
499
547
|
model: cfg.backendModel, status: 'translated',
|
|
500
|
-
}
|
|
548
|
+
};
|
|
549
|
+
await store.insertPlural(insertParams);
|
|
550
|
+
if (shared) try { await shared.insertPlural(insertParams); } catch { /* ignore */ }
|
|
501
551
|
}
|
|
502
552
|
translated++;
|
|
503
553
|
console.log(` [${done}/${total}] ${lang} translated: ${label}`);
|
|
@@ -509,20 +559,50 @@ export async function runScan(opts: ScanOptions): Promise<string> {
|
|
|
509
559
|
} else {
|
|
510
560
|
const stringContextHash = hash(entry.context ?? '');
|
|
511
561
|
const label = (entry.text ?? '').slice(0, 40);
|
|
562
|
+
const lookupParams = {
|
|
563
|
+
sourceText: entry.text!, sourceLang: cfg.sourceLang,
|
|
564
|
+
projectContextHash, stringContextHash,
|
|
565
|
+
};
|
|
512
566
|
|
|
513
567
|
for (const lang of targetLangs) {
|
|
514
568
|
done++;
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
projectContextHash, stringContextHash,
|
|
518
|
-
});
|
|
569
|
+
const langLookup = { ...lookupParams, targetLang: lang };
|
|
570
|
+
const cached = await store.lookup(langLookup);
|
|
519
571
|
|
|
520
572
|
if (cached !== null) {
|
|
573
|
+
// Ensure shared store has it too
|
|
574
|
+
if (shared) {
|
|
575
|
+
try {
|
|
576
|
+
const sharedCached = await shared.lookup(langLookup);
|
|
577
|
+
if (sharedCached === null) {
|
|
578
|
+
await shared.insert({
|
|
579
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
580
|
+
translatedText: cached, model: cfg.backendModel, status: 'translated',
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
} catch { /* ignore */ }
|
|
584
|
+
}
|
|
521
585
|
skipped++;
|
|
522
586
|
console.log(` [${done}/${total}] ${lang} skipped (cached): ${label}`);
|
|
523
587
|
continue;
|
|
524
588
|
}
|
|
525
589
|
|
|
590
|
+
// Check shared store before calling backend
|
|
591
|
+
if (shared) {
|
|
592
|
+
try {
|
|
593
|
+
const sharedCached = await shared.lookup(langLookup);
|
|
594
|
+
if (sharedCached !== null) {
|
|
595
|
+
await store.insert({
|
|
596
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
597
|
+
translatedText: sharedCached, model: cfg.backendModel, status: 'translated',
|
|
598
|
+
});
|
|
599
|
+
skipped++;
|
|
600
|
+
console.log(` [${done}/${total}] ${lang} skipped (shared cache): ${label}`);
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
} catch { /* ignore */ }
|
|
604
|
+
}
|
|
605
|
+
|
|
526
606
|
try {
|
|
527
607
|
const result = await backendTranslate(
|
|
528
608
|
entry.text!, cfg.sourceLang, lang,
|
|
@@ -530,11 +610,12 @@ export async function runScan(opts: ScanOptions): Promise<string> {
|
|
|
530
610
|
);
|
|
531
611
|
|
|
532
612
|
if (validateTranslation(entry.text!, result)) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
projectContextHash, stringContextHash, stringContext: entry.context ?? '',
|
|
613
|
+
const insertParams = {
|
|
614
|
+
...langLookup, stringContext: entry.context ?? '',
|
|
536
615
|
translatedText: result, model: cfg.backendModel, status: 'translated',
|
|
537
|
-
}
|
|
616
|
+
};
|
|
617
|
+
await store.insert(insertParams);
|
|
618
|
+
if (shared) try { await shared.insert(insertParams); } catch { /* ignore */ }
|
|
538
619
|
translated++;
|
|
539
620
|
console.log(` [${done}/${total}] ${lang} translated: ${label}`);
|
|
540
621
|
} else {
|
|
@@ -550,6 +631,7 @@ export async function runScan(opts: ScanOptions): Promise<string> {
|
|
|
550
631
|
}
|
|
551
632
|
|
|
552
633
|
store.close();
|
|
634
|
+
if (shared) await shared.close();
|
|
553
635
|
lines.push(`\nTranslated: ${translated} | Skipped: ${skipped} | Failed: ${failed}`);
|
|
554
636
|
} // end if entries.length > 0
|
|
555
637
|
}
|
|
@@ -661,7 +743,7 @@ export async function runStats(opts: StatsOptions): Promise<string> {
|
|
|
661
743
|
// CLI entry point
|
|
662
744
|
const program = new Command();
|
|
663
745
|
|
|
664
|
-
program.name('transduck').description('AI-native translation tool').version('0.6.
|
|
746
|
+
program.name('transduck').description('AI-native translation tool').version('0.6.8');
|
|
665
747
|
|
|
666
748
|
program.command('init')
|
|
667
749
|
.description('Initialize a new transduck project')
|