dsh-cait 1.0.4 → 1.1.3
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/CHANGELOG.md +202 -0
- package/README.ja.md +147 -25
- package/README.md +176 -27
- package/README.zh-CN.md +137 -26
- package/client/components.js +2047 -340
- package/client/entry.js +13 -0
- package/client/helpers.js +110 -7
- package/client/locales.js +435 -6
- package/client/rpc.js +1 -1
- package/client/styles.js +137 -3
- package/client.js +2742 -356
- package/index.js +3 -0
- package/lib/aligner.js +411 -0
- package/lib/api.js +31 -0
- package/lib/context.js +71 -0
- package/lib/docx.js +12 -9
- package/lib/epub.js +355 -0
- package/lib/formats.js +88 -33
- package/lib/glossary.js +4 -4
- package/lib/html.js +271 -0
- package/lib/import-export.js +208 -95
- package/lib/libraries.js +55 -0
- package/lib/library-export.js +75 -0
- package/lib/library-view.js +215 -0
- package/lib/mt.js +17 -7
- package/lib/persistence.js +92 -7
- package/lib/pre-translate.js +263 -0
- package/lib/preprocess.js +65 -0
- package/lib/propagate.js +133 -0
- package/lib/qa.js +167 -0
- package/lib/store-utils.js +48 -6
- package/lib/store.js +143 -12
- package/lib/term-extract.js +190 -0
- package/lib/terms.js +102 -0
- package/lib/tm.js +123 -11
- package/lib/tmx.js +47 -2
- package/lib/tools.js +472 -0
- package/package.json +2 -2
package/lib/tools.js
CHANGED
|
@@ -21,6 +21,11 @@ function matchLabel(match) {
|
|
|
21
21
|
return MATCH_LABELS[match?.matchType] || (match?.exact ? 'exact' : 'fuzzy')
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function shorten(value, max = 70) {
|
|
25
|
+
const textValue = String(value ?? '').replace(/\s+/g, ' ').trim()
|
|
26
|
+
return textValue.length > max ? `${textValue.slice(0, max - 1)}…` : textValue
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
function summarizeMatches(matches) {
|
|
25
30
|
if (!matches?.length) return 'TM matches: none'
|
|
26
31
|
const lines = ['TM matches:']
|
|
@@ -464,6 +469,473 @@ export function registerCatTools(ctx, store) {
|
|
|
464
469
|
}
|
|
465
470
|
},
|
|
466
471
|
}),
|
|
472
|
+
|
|
473
|
+
ctx.tools.register({
|
|
474
|
+
name: 'cat_pre_translate',
|
|
475
|
+
description:
|
|
476
|
+
'Pre-translate one block of segments in a CAIT document: exact translation-memory matches first, then machine translation through the route in CAIT settings. Segments that are already confirmed or already carry a target are skipped, so repeated calls never overwrite human work. Each call handles at most 32 segments (default 8); the response carries next, the offset of the following block, or null at the end of the document. Call again with from: next to continue.',
|
|
477
|
+
parameters: {
|
|
478
|
+
type: 'object',
|
|
479
|
+
properties: {
|
|
480
|
+
documentId: {
|
|
481
|
+
type: 'string',
|
|
482
|
+
description: 'Optional CAIT document id. Defaults to the active document.',
|
|
483
|
+
},
|
|
484
|
+
from: {
|
|
485
|
+
type: 'integer',
|
|
486
|
+
minimum: 0,
|
|
487
|
+
description: 'Zero-based offset of the first segment in this block. Defaults to 0.',
|
|
488
|
+
},
|
|
489
|
+
count: {
|
|
490
|
+
type: 'integer',
|
|
491
|
+
minimum: 1,
|
|
492
|
+
maximum: 32,
|
|
493
|
+
description: 'Segments per block. Defaults to 8.',
|
|
494
|
+
},
|
|
495
|
+
mode: {
|
|
496
|
+
type: 'string',
|
|
497
|
+
enum: ['mt', 'tm'],
|
|
498
|
+
description:
|
|
499
|
+
'mt (default) fills from translation memory when possible and machine translation otherwise; tm only reuses exact translation-memory matches and reports the rest as noMatch.',
|
|
500
|
+
},
|
|
501
|
+
overwrite: {
|
|
502
|
+
type: 'boolean',
|
|
503
|
+
description: 'Also replace segments that already have a target. Defaults to false.',
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
additionalProperties: false,
|
|
507
|
+
},
|
|
508
|
+
output: {
|
|
509
|
+
schema: { type: 'object', additionalProperties: true },
|
|
510
|
+
render(_args, value) {
|
|
511
|
+
if (!value || value.ok === false) {
|
|
512
|
+
return text(`CAIT: ${value?.message || 'Pre-translation failed.'}`)
|
|
513
|
+
}
|
|
514
|
+
const ice = value.segments.filter((item) => item.ice).length
|
|
515
|
+
const lines = [
|
|
516
|
+
`CAIT pre-translate (${value.mode}) — segments ${value.from + 1}–${value.to} of ${value.progress.total}`,
|
|
517
|
+
`Filled ${value.filled}${ice ? ` (${ice} context match${ice === 1 ? '' : 'es'})` : ''} · skipped ${value.skipped.length} · no TM match ${value.noMatch.length} · failed ${value.failed.length}`,
|
|
518
|
+
]
|
|
519
|
+
if (value.failed.length) {
|
|
520
|
+
for (const item of value.failed.slice(0, 5)) {
|
|
521
|
+
lines.push(`- segment ${item.index + 1}: ${item.code} — ${item.message}`)
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
lines.push(
|
|
525
|
+
'',
|
|
526
|
+
`Progress: ${value.progress.confirmed}/${value.progress.total} confirmed · ${value.progress.translated} awaiting review · ${value.progress.pending} pending.`,
|
|
527
|
+
)
|
|
528
|
+
lines.push(value.next === null ? 'End of document reached.' : `Next block starts at segment ${value.next + 1}.`)
|
|
529
|
+
return text(lines.join('\n'))
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
async execute(args = {}) {
|
|
533
|
+
try {
|
|
534
|
+
return { ok: true, ...(await store.preTranslate(args)) }
|
|
535
|
+
} catch (error) {
|
|
536
|
+
return failure(error)
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
}),
|
|
540
|
+
|
|
541
|
+
ctx.tools.register({
|
|
542
|
+
name: 'cat_get_segments',
|
|
543
|
+
description:
|
|
544
|
+
'List a bounded window of segments from a CAIT document with their source, target, status (pending, translated or confirmed), origin (tm, mt, agent or manual) and any pending suggestion. Use status to pull only the segments that still need work. The response carries next, the offset of the following window, or null at the end.',
|
|
545
|
+
parameters: {
|
|
546
|
+
type: 'object',
|
|
547
|
+
properties: {
|
|
548
|
+
documentId: {
|
|
549
|
+
type: 'string',
|
|
550
|
+
description: 'Optional CAIT document id. Defaults to the active document.',
|
|
551
|
+
},
|
|
552
|
+
from: {
|
|
553
|
+
type: 'integer',
|
|
554
|
+
minimum: 0,
|
|
555
|
+
description: 'Zero-based offset of the first segment. Defaults to 0.',
|
|
556
|
+
},
|
|
557
|
+
count: {
|
|
558
|
+
type: 'integer',
|
|
559
|
+
minimum: 1,
|
|
560
|
+
maximum: 50,
|
|
561
|
+
description: 'Segments per window. Defaults to 20.',
|
|
562
|
+
},
|
|
563
|
+
status: {
|
|
564
|
+
type: 'string',
|
|
565
|
+
enum: ['pending', 'translated', 'confirmed'],
|
|
566
|
+
description: 'Optional status filter.',
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
additionalProperties: false,
|
|
570
|
+
},
|
|
571
|
+
output: {
|
|
572
|
+
schema: { type: 'object', additionalProperties: true },
|
|
573
|
+
render(_args, value) {
|
|
574
|
+
if (!value || value.ok === false) {
|
|
575
|
+
return text(`CAIT: ${value?.message || 'Could not list segments.'}`)
|
|
576
|
+
}
|
|
577
|
+
const doc = value.document
|
|
578
|
+
const lines = [
|
|
579
|
+
`CAIT — ${doc.name} (${doc.sourceLang} → ${doc.targetLang}) · ${value.progress.confirmed}/${doc.total} confirmed · ${value.progress.translated} awaiting review · ${value.progress.pending} pending`,
|
|
580
|
+
'',
|
|
581
|
+
]
|
|
582
|
+
if (!value.segments.length) {
|
|
583
|
+
lines.push('No segments match.')
|
|
584
|
+
return text(lines.join('\n'))
|
|
585
|
+
}
|
|
586
|
+
for (const segment of value.segments) {
|
|
587
|
+
lines.push(
|
|
588
|
+
`[${segment.index + 1}] ${segment.status}${segment.origin ? ` · ${segment.origin}` : ''}${segment.suggestion ? ' · has suggestion' : ''}`,
|
|
589
|
+
` ${segment.source}`,
|
|
590
|
+
` → ${segment.target || '(empty)'}`,
|
|
591
|
+
)
|
|
592
|
+
}
|
|
593
|
+
lines.push('', value.next === null ? 'End of document reached.' : `Next window starts at ${value.next + 1}.`)
|
|
594
|
+
return text(lines.join('\n'))
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
async execute(args = {}) {
|
|
598
|
+
try {
|
|
599
|
+
return { ok: true, ...store.segmentsForTool(args) }
|
|
600
|
+
} catch (error) {
|
|
601
|
+
return failure(error)
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
}),
|
|
605
|
+
|
|
606
|
+
ctx.tools.register({
|
|
607
|
+
name: 'cat_confirm_segments',
|
|
608
|
+
description:
|
|
609
|
+
'Confirm a batch of CAIT segments that already have a target, writing each source–target pair into translation memory. Pass indices, or a from/count window. Segments without a target and segments that are already confirmed are reported as skipped instead of guessed. Use this after reviewing pre-translated text in bulk.',
|
|
610
|
+
parameters: {
|
|
611
|
+
type: 'object',
|
|
612
|
+
properties: {
|
|
613
|
+
documentId: {
|
|
614
|
+
type: 'string',
|
|
615
|
+
description: 'Optional CAIT document id. Defaults to the active document.',
|
|
616
|
+
},
|
|
617
|
+
indices: {
|
|
618
|
+
type: 'array',
|
|
619
|
+
items: { type: 'integer', minimum: 0 },
|
|
620
|
+
description: 'Zero-based segment indices to confirm. Takes precedence over from/count.',
|
|
621
|
+
},
|
|
622
|
+
from: {
|
|
623
|
+
type: 'integer',
|
|
624
|
+
minimum: 0,
|
|
625
|
+
description: 'Zero-based offset of the first segment of the window. Defaults to 0.',
|
|
626
|
+
},
|
|
627
|
+
count: {
|
|
628
|
+
type: 'integer',
|
|
629
|
+
minimum: 1,
|
|
630
|
+
maximum: 50,
|
|
631
|
+
description: 'Segments in the window. Defaults to 20.',
|
|
632
|
+
},
|
|
633
|
+
},
|
|
634
|
+
additionalProperties: false,
|
|
635
|
+
},
|
|
636
|
+
output: {
|
|
637
|
+
schema: { type: 'object', additionalProperties: true },
|
|
638
|
+
render(_args, value) {
|
|
639
|
+
if (!value || value.ok === false) {
|
|
640
|
+
return text(`CAIT: ${value?.message || 'Could not confirm segments.'}`)
|
|
641
|
+
}
|
|
642
|
+
const lines = [`Confirmed ${value.confirmed.length} segment(s).`]
|
|
643
|
+
if (value.confirmed.length) lines.push(`Indices: ${value.confirmed.map((index) => index + 1).join(', ')}`)
|
|
644
|
+
if (value.skipped.length) {
|
|
645
|
+
const summary = {}
|
|
646
|
+
for (const item of value.skipped) summary[item.reason] = (summary[item.reason] || 0) + 1
|
|
647
|
+
lines.push(
|
|
648
|
+
`Skipped ${value.skipped.length}: ${Object.entries(summary)
|
|
649
|
+
.map(([reason, amount]) => `${reason} ${amount}`)
|
|
650
|
+
.join(', ')}`,
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
lines.push(
|
|
654
|
+
'',
|
|
655
|
+
`Progress: ${value.progress.confirmed}/${value.progress.total} confirmed · ${value.progress.translated} awaiting review · ${value.progress.pending} pending.`,
|
|
656
|
+
)
|
|
657
|
+
if (value.done) lines.push('Every segment is confirmed.')
|
|
658
|
+
return text(lines.join('\n'))
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
async execute(args = {}) {
|
|
662
|
+
try {
|
|
663
|
+
return { ok: true, ...store.confirmSegmentsForTool(args) }
|
|
664
|
+
} catch (error) {
|
|
665
|
+
return failure(error)
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
}),
|
|
669
|
+
ctx.tools.register({
|
|
670
|
+
name: 'cat_propose_translation',
|
|
671
|
+
description:
|
|
672
|
+
'Propose a translation for one segment without touching its current target. The proposal is stored as a suggestion the human can adopt in the review pane; confirmed segments are refused so a decision that was already made is never silently overwritten. Use this for terminology fixes, consistency repairs and style rewrites.',
|
|
673
|
+
parameters: {
|
|
674
|
+
type: 'object',
|
|
675
|
+
properties: {
|
|
676
|
+
documentId: {
|
|
677
|
+
type: 'string',
|
|
678
|
+
description: 'Optional CAIT document id. Defaults to the active document.',
|
|
679
|
+
},
|
|
680
|
+
index: {
|
|
681
|
+
type: 'integer',
|
|
682
|
+
minimum: 0,
|
|
683
|
+
description: 'Zero-based segment index. Defaults to the document cursor.',
|
|
684
|
+
},
|
|
685
|
+
target: {
|
|
686
|
+
type: 'string',
|
|
687
|
+
description: 'The proposed translation text.',
|
|
688
|
+
},
|
|
689
|
+
reason: {
|
|
690
|
+
type: 'string',
|
|
691
|
+
description: 'Optional short note shown next to the proposal, e.g. "术语统一:mode → 模式".',
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
required: ['target'],
|
|
695
|
+
additionalProperties: false,
|
|
696
|
+
},
|
|
697
|
+
output: {
|
|
698
|
+
schema: { type: 'object', additionalProperties: true },
|
|
699
|
+
render(_args, value) {
|
|
700
|
+
if (!value || value.ok === false) {
|
|
701
|
+
return text(`CAIT: ${value?.message || 'Could not propose a translation.'}`)
|
|
702
|
+
}
|
|
703
|
+
const lines = [
|
|
704
|
+
`Proposed a translation for segment ${value.segment.index + 1} (${value.segment.status}).`,
|
|
705
|
+
value.segment.target ? `Current target: ${value.segment.target}` : 'Current target: (empty)',
|
|
706
|
+
`Proposal: ${value.segment.suggestion}`,
|
|
707
|
+
]
|
|
708
|
+
if (value.segment.suggestionReason) lines.push(`Reason: ${value.segment.suggestionReason}`)
|
|
709
|
+
lines.push('', 'The human adopts it in the review pane; the target is unchanged until then.')
|
|
710
|
+
return text(lines.join('\n'))
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
async execute(args = {}) {
|
|
714
|
+
try {
|
|
715
|
+
return { ok: true, ...store.proposeTranslation(args) }
|
|
716
|
+
} catch (error) {
|
|
717
|
+
return failure(error)
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
}),
|
|
721
|
+
ctx.tools.register({
|
|
722
|
+
name: 'cat_align',
|
|
723
|
+
description:
|
|
724
|
+
'Align a source text with an existing translation of it and report how confident the pairing is. Pass the two texts directly (read the files first if they are on disk). Nothing is written: the result only says how many pairs are trustworthy and which rows a human should check. Writing happens in the align pane after review.',
|
|
725
|
+
parameters: {
|
|
726
|
+
type: 'object',
|
|
727
|
+
properties: {
|
|
728
|
+
sourceText: { type: 'string', description: 'Full text of the source document.' },
|
|
729
|
+
targetText: { type: 'string', description: 'Full text of the existing translation.' },
|
|
730
|
+
sourceName: { type: 'string', description: 'Optional file name for display.' },
|
|
731
|
+
targetName: { type: 'string', description: 'Optional file name for display.' },
|
|
732
|
+
minScore: {
|
|
733
|
+
type: 'number',
|
|
734
|
+
description: 'Only list rows below this score as needing review. Defaults to 0.6.',
|
|
735
|
+
},
|
|
736
|
+
limit: {
|
|
737
|
+
type: 'integer',
|
|
738
|
+
minimum: 1,
|
|
739
|
+
maximum: 50,
|
|
740
|
+
description: 'How many doubtful rows to show. Defaults to 10.',
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
required: ['sourceText', 'targetText'],
|
|
744
|
+
additionalProperties: false,
|
|
745
|
+
},
|
|
746
|
+
output: {
|
|
747
|
+
schema: { type: 'object', additionalProperties: true },
|
|
748
|
+
render(args, value) {
|
|
749
|
+
if (!value || value.ok === false) {
|
|
750
|
+
return text(`CAIT: ${value?.message || 'Could not align the two texts.'}`)
|
|
751
|
+
}
|
|
752
|
+
const threshold = Number.isFinite(args?.minScore) ? args.minScore : 0.6
|
|
753
|
+
const doubtful = value.pairs.filter(
|
|
754
|
+
(row) => row.score < threshold || row.confidence !== 'high',
|
|
755
|
+
)
|
|
756
|
+
const limit = Number.isFinite(args?.limit) ? args.limit : 10
|
|
757
|
+
const lines = [
|
|
758
|
+
`CAIT align: ${value.pairs.length} pair(s) from ${value.sourceSegments} source / ${value.targetSegments} target segment(s).`,
|
|
759
|
+
`High confidence ${value.high} · medium ${value.medium} · low ${value.low} · unpaired source ${value.unpairedSource} · unpaired target ${value.unpairedTarget}.`,
|
|
760
|
+
]
|
|
761
|
+
if (value.blocked) lines.push('Large document: aligned in blocks, check the middle of the file.')
|
|
762
|
+
if (doubtful.length) {
|
|
763
|
+
lines.push('', `Rows worth a human look (first ${Math.min(limit, doubtful.length)}):`)
|
|
764
|
+
for (const row of doubtful.slice(0, limit)) {
|
|
765
|
+
lines.push(
|
|
766
|
+
`[${row.sourceIndex + 1}↔${row.targetIndex + 1}] ${row.score} · ${shorten(row.source)} → ${shorten(row.target)}`,
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
lines.push('', 'Write the reviewed pairs in the align pane; this tool never writes.')
|
|
771
|
+
return text(lines.join('\n'))
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
async execute(args = {}) {
|
|
775
|
+
try {
|
|
776
|
+
const result = store.alignUploads({
|
|
777
|
+
sourceFile: { name: args.sourceName || 'source.txt', text: args.sourceText },
|
|
778
|
+
targetFile: { name: args.targetName || 'target.txt', text: args.targetText },
|
|
779
|
+
})
|
|
780
|
+
return { ok: true, ...result, rows: undefined }
|
|
781
|
+
} catch (error) {
|
|
782
|
+
return failure(error)
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
}),
|
|
786
|
+
ctx.tools.register({
|
|
787
|
+
name: 'cat_consistency_report',
|
|
788
|
+
description:
|
|
789
|
+
'List groups of segments that share the same source text but carry different translations. Use it to find inconsistencies an import or propagation left behind; then decide the right wording and use cat_propose_translation.',
|
|
790
|
+
parameters: {
|
|
791
|
+
type: 'object',
|
|
792
|
+
properties: {
|
|
793
|
+
documentId: {
|
|
794
|
+
type: 'string',
|
|
795
|
+
description: 'Optional CAIT document id. Defaults to the active document.',
|
|
796
|
+
},
|
|
797
|
+
limit: {
|
|
798
|
+
type: 'integer',
|
|
799
|
+
minimum: 1,
|
|
800
|
+
maximum: 200,
|
|
801
|
+
description: 'Maximum groups to return. Defaults to 50.',
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
additionalProperties: false,
|
|
805
|
+
},
|
|
806
|
+
output: {
|
|
807
|
+
schema: { type: 'object', additionalProperties: true },
|
|
808
|
+
render(_args, value) {
|
|
809
|
+
if (!value || value.ok === false) {
|
|
810
|
+
return text(`CAIT: ${value?.message || 'Could not build the consistency report.'}`)
|
|
811
|
+
}
|
|
812
|
+
if (!value.conflicts.length) {
|
|
813
|
+
return text('CAIT consistency: every repeated source uses the same translation.')
|
|
814
|
+
}
|
|
815
|
+
const lines = [`CAIT consistency: ${value.total} inconsistent group(s).`]
|
|
816
|
+
for (const conflict of value.conflicts) {
|
|
817
|
+
lines.push('', `Source: ${shorten(conflict.source, 90)}`)
|
|
818
|
+
for (const variant of conflict.variants) {
|
|
819
|
+
lines.push(
|
|
820
|
+
` ${shorten(variant.target, 80)} — segment(s) ${variant.indices.map((index) => index + 1).join(', ')}`,
|
|
821
|
+
)
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return text(lines.join('\n'))
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
async execute(args = {}) {
|
|
828
|
+
try {
|
|
829
|
+
return { ok: true, ...store.consistencyReport(args) }
|
|
830
|
+
} catch (error) {
|
|
831
|
+
return failure(error)
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
}),
|
|
835
|
+
ctx.tools.register({
|
|
836
|
+
name: 'cat_term_candidates',
|
|
837
|
+
description:
|
|
838
|
+
'List terminology candidates mined from a document by counting: repeated multi-word names, capitalised compounds and repeated CJK substrings, ranked by frequency and length. Each candidate carries its occurrence count, sample sentences and any translation-memory wording that already exists. This is the short list to judge; do not re-derive terms yourself. Pick the real domain terms, translate them, then store them with cat_add_terms.',
|
|
839
|
+
parameters: {
|
|
840
|
+
type: 'object',
|
|
841
|
+
properties: {
|
|
842
|
+
documentId: {
|
|
843
|
+
type: 'string',
|
|
844
|
+
description: 'Optional CAIT document id. Defaults to the active document.',
|
|
845
|
+
},
|
|
846
|
+
limit: {
|
|
847
|
+
type: 'integer',
|
|
848
|
+
minimum: 1,
|
|
849
|
+
maximum: 200,
|
|
850
|
+
description: 'Maximum candidates to return. Defaults to 60.',
|
|
851
|
+
},
|
|
852
|
+
minCount: {
|
|
853
|
+
type: 'integer',
|
|
854
|
+
minimum: 1,
|
|
855
|
+
maximum: 20,
|
|
856
|
+
description: 'Minimum number of occurrences. Defaults to 2.',
|
|
857
|
+
},
|
|
858
|
+
},
|
|
859
|
+
additionalProperties: false,
|
|
860
|
+
},
|
|
861
|
+
output: {
|
|
862
|
+
schema: { type: 'object', additionalProperties: true },
|
|
863
|
+
render(_args, value) {
|
|
864
|
+
if (!value || value.ok === false) {
|
|
865
|
+
return text(`CAIT: ${value?.message || 'Could not mine terminology candidates.'}`)
|
|
866
|
+
}
|
|
867
|
+
if (!value.candidates.length) {
|
|
868
|
+
return text('CAIT terms: no repeated candidate found in this document.')
|
|
869
|
+
}
|
|
870
|
+
const lines = [`CAIT terms: ${value.total} candidate(s).`]
|
|
871
|
+
for (const candidate of value.candidates) {
|
|
872
|
+
const flags = [
|
|
873
|
+
`×${candidate.count}`,
|
|
874
|
+
candidate.inLibrary ? 'in glossary' : '',
|
|
875
|
+
candidate.evidence.length ? `memory: ${candidate.evidence.map((item) => shorten(item, 40)).join(' | ')}` : '',
|
|
876
|
+
].filter(Boolean)
|
|
877
|
+
lines.push('', `${candidate.term} — ${flags.join(' · ')}`)
|
|
878
|
+
if (candidate.examples.length) lines.push(` e.g. ${shorten(candidate.examples[0], 100)}`)
|
|
879
|
+
}
|
|
880
|
+
return text(lines.join('\n'))
|
|
881
|
+
},
|
|
882
|
+
},
|
|
883
|
+
async execute(args = {}) {
|
|
884
|
+
try {
|
|
885
|
+
return { ok: true, ...store.termCandidates(args) }
|
|
886
|
+
} catch (error) {
|
|
887
|
+
return failure(error)
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
}),
|
|
891
|
+
ctx.tools.register({
|
|
892
|
+
name: 'cat_add_terms',
|
|
893
|
+
description:
|
|
894
|
+
'Add accepted terminology to a glossary library in one batch. Entries already present (same source and language) are skipped rather than overwritten, so this never destroys wording the user chose. Create or pick a library with the user first when they keep separate projects.',
|
|
895
|
+
parameters: {
|
|
896
|
+
type: 'object',
|
|
897
|
+
properties: {
|
|
898
|
+
glossaryId: {
|
|
899
|
+
type: 'string',
|
|
900
|
+
description: 'Optional glossary library id. Defaults to the mounted library.',
|
|
901
|
+
},
|
|
902
|
+
entries: {
|
|
903
|
+
type: 'array',
|
|
904
|
+
description: 'Terms to add.',
|
|
905
|
+
items: {
|
|
906
|
+
type: 'object',
|
|
907
|
+
properties: {
|
|
908
|
+
source: { type: 'string', description: 'Source term.' },
|
|
909
|
+
target: { type: 'string', description: 'Translated term.' },
|
|
910
|
+
note: { type: 'string', description: 'Optional note.' },
|
|
911
|
+
sourceLang: { type: 'string', description: 'Optional source language tag.' },
|
|
912
|
+
caseSensitive: { type: 'boolean', description: 'Match this term case-sensitively.' },
|
|
913
|
+
},
|
|
914
|
+
required: ['source', 'target'],
|
|
915
|
+
additionalProperties: false,
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
required: ['entries'],
|
|
920
|
+
additionalProperties: false,
|
|
921
|
+
},
|
|
922
|
+
output: {
|
|
923
|
+
schema: { type: 'object', additionalProperties: true },
|
|
924
|
+
render(_args, value) {
|
|
925
|
+
if (!value || value.ok === false) {
|
|
926
|
+
return text(`CAIT: ${value?.message || 'Could not add terms.'}`)
|
|
927
|
+
}
|
|
928
|
+
return text(`CAIT terms: added ${value.added}, skipped ${value.skipped} (of ${value.total}).`)
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
async execute(args = {}) {
|
|
932
|
+
try {
|
|
933
|
+
return { ok: true, ...store.addGlossaryEntries(args) }
|
|
934
|
+
} catch (error) {
|
|
935
|
+
return failure(error)
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
}),
|
|
467
939
|
]
|
|
468
940
|
|
|
469
941
|
return () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-cait",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jovi Lecca",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"bugs": {
|
|
18
18
|
"url": "https://github.com/JoviLecca/dsh-CAIT/issues"
|
|
19
19
|
},
|
|
20
|
-
"description": "CAIT — a CAT workbench for DeepSeek Harness with translation memory, glossary, document tabs, MT routes, and Office/subtitle/localization formats.",
|
|
20
|
+
"description": "CAIT — a CAT workbench for DeepSeek Harness with translation memory, glossary, document tabs, MT routes, and Office/EPUB/subtitle/localization formats.",
|
|
21
21
|
"type": "module",
|
|
22
22
|
"keywords": [
|
|
23
23
|
"dsh-plugin",
|