@willwade/aac-processors 0.0.13 → 0.0.14
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 +4 -5
- package/dist/core/baseProcessor.d.ts +41 -0
- package/dist/core/baseProcessor.js +41 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/processors/gridset/symbolAlignment.d.ts +125 -0
- package/dist/processors/gridset/symbolAlignment.js +283 -0
- package/dist/processors/gridsetProcessor.d.ts +26 -0
- package/dist/processors/gridsetProcessor.js +121 -5
- package/dist/processors/obfProcessor.d.ts +26 -0
- package/dist/processors/obfProcessor.js +82 -1
- package/dist/processors/snapProcessor.js +1 -1
- package/dist/processors/touchchatProcessor.d.ts +26 -0
- package/dist/processors/touchchatProcessor.js +82 -1
- package/dist/utilities/translation/translationProcessor.d.ts +119 -0
- package/dist/utilities/translation/translationProcessor.js +204 -0
- package/package.json +1 -1
- /package/dist/{optional → utilities}/analytics/history.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/history.js +0 -0
- /package/dist/{optional → utilities}/analytics/index.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/index.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/comparison.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/comparison.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/core.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/core.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/effort.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/effort.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/index.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/index.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/obl-types.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/obl-types.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/obl.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/obl.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/sentence.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/sentence.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/types.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/types.js +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/vocabulary.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/metrics/vocabulary.js +0 -0
- /package/dist/{optional → utilities}/analytics/reference/index.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/reference/index.js +0 -0
- /package/dist/{optional → utilities}/analytics/utils/idGenerator.d.ts +0 -0
- /package/dist/{optional → utilities}/analytics/utils/idGenerator.js +0 -0
- /package/dist/{optional → utilities}/symbolTools.d.ts +0 -0
- /package/dist/{optional → utilities}/symbolTools.js +0 -0
|
@@ -10,6 +10,7 @@ const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
12
12
|
const resolver_1 = require("./gridset/resolver");
|
|
13
|
+
const translationProcessor_1 = require("../utilities/translation/translationProcessor");
|
|
13
14
|
const password_1 = require("./gridset/password");
|
|
14
15
|
const crypto_1 = __importDefault(require("crypto"));
|
|
15
16
|
const zlib_1 = __importDefault(require("zlib"));
|
|
@@ -19,7 +20,8 @@ const pluginTypes_1 = require("./gridset/pluginTypes");
|
|
|
19
20
|
const commands_1 = require("./gridset/commands");
|
|
20
21
|
const symbols_1 = require("./gridset/symbols");
|
|
21
22
|
const resolver_2 = require("./gridset/resolver");
|
|
22
|
-
const idGenerator_1 = require("../
|
|
23
|
+
const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
|
|
24
|
+
const symbolAlignment_1 = require("./gridset/symbolAlignment");
|
|
23
25
|
class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
24
26
|
constructor(options) {
|
|
25
27
|
super(options);
|
|
@@ -1084,17 +1086,51 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1084
1086
|
if (tPage)
|
|
1085
1087
|
page.name = tPage;
|
|
1086
1088
|
}
|
|
1087
|
-
// Translate button labels and messages
|
|
1089
|
+
// Translate button labels and messages, preserving symbol positions
|
|
1088
1090
|
page.buttons.forEach((button) => {
|
|
1091
|
+
// Translate label
|
|
1089
1092
|
if (button.label && translations.has(button.label)) {
|
|
1090
1093
|
const tLabel = translations.get(button.label);
|
|
1091
1094
|
if (tLabel)
|
|
1092
1095
|
button.label = tLabel;
|
|
1093
1096
|
}
|
|
1097
|
+
// Translate message with symbol preservation
|
|
1094
1098
|
if (button.message && translations.has(button.message)) {
|
|
1095
|
-
const
|
|
1096
|
-
|
|
1097
|
-
|
|
1099
|
+
const originalMessage = button.message;
|
|
1100
|
+
const translatedText = translations.get(originalMessage);
|
|
1101
|
+
if (translatedText) {
|
|
1102
|
+
// Extract symbols from the button (from richText or image fields)
|
|
1103
|
+
const symbols = (0, symbolAlignment_1.extractSymbolsFromButton)(button);
|
|
1104
|
+
if (symbols && symbols.length > 0) {
|
|
1105
|
+
// Use symbol-aware translation to preserve symbol positions
|
|
1106
|
+
const result = (0, symbolAlignment_1.translateWithSymbols)(originalMessage, translatedText, symbols);
|
|
1107
|
+
// Update the message
|
|
1108
|
+
button.message = result.text;
|
|
1109
|
+
// Update the rich text structure if it exists
|
|
1110
|
+
if (button.semanticAction?.richText) {
|
|
1111
|
+
button.semanticAction.richText.text = result.text;
|
|
1112
|
+
button.semanticAction.richText.symbols = result.richTextSymbols;
|
|
1113
|
+
}
|
|
1114
|
+
else if (result.richTextSymbols.length > 0) {
|
|
1115
|
+
// Create rich text structure if it doesn't exist but we have symbols
|
|
1116
|
+
if (!button.semanticAction) {
|
|
1117
|
+
button.semanticAction = {
|
|
1118
|
+
category: treeStructure_1.AACSemanticCategory.COMMUNICATION,
|
|
1119
|
+
intent: treeStructure_1.AACSemanticIntent.SPEAK_TEXT,
|
|
1120
|
+
text: result.text,
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
button.semanticAction.richText = {
|
|
1124
|
+
text: result.text,
|
|
1125
|
+
symbols: result.richTextSymbols,
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1130
|
+
// No symbols to preserve, simple translation
|
|
1131
|
+
button.message = translatedText;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1098
1134
|
}
|
|
1099
1135
|
});
|
|
1100
1136
|
});
|
|
@@ -1102,6 +1138,86 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1102
1138
|
this.saveFromTree(tree, outputPath);
|
|
1103
1139
|
return fs_1.default.readFileSync(outputPath);
|
|
1104
1140
|
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Extract symbol information from a gridset for LLM-based translation.
|
|
1143
|
+
* Returns a structured format showing which buttons have symbols and their context.
|
|
1144
|
+
*
|
|
1145
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
1146
|
+
*
|
|
1147
|
+
* @param filePathOrBuffer - Path to gridset file or buffer
|
|
1148
|
+
* @returns Array of symbol information for LLM processing
|
|
1149
|
+
*/
|
|
1150
|
+
extractSymbolsForLLM(filePathOrBuffer) {
|
|
1151
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
1152
|
+
// Collect all buttons from all pages
|
|
1153
|
+
const allButtons = [];
|
|
1154
|
+
Object.values(tree.pages).forEach((page) => {
|
|
1155
|
+
page.buttons.forEach((button) => {
|
|
1156
|
+
// Add page context to each button
|
|
1157
|
+
button.pageId = page.id;
|
|
1158
|
+
button.pageName = page.name || page.id;
|
|
1159
|
+
allButtons.push(button);
|
|
1160
|
+
});
|
|
1161
|
+
});
|
|
1162
|
+
// Use shared utility to extract buttons with translation context
|
|
1163
|
+
return (0, translationProcessor_1.extractAllButtonsForTranslation)(allButtons, (button) => ({
|
|
1164
|
+
pageId: button.pageId,
|
|
1165
|
+
pageName: button.pageName,
|
|
1166
|
+
}));
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Apply LLM translations with symbol information.
|
|
1170
|
+
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
1171
|
+
*
|
|
1172
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
1173
|
+
*
|
|
1174
|
+
* @param filePathOrBuffer - Path to gridset file or buffer
|
|
1175
|
+
* @param llmTranslations - Array of LLM translations with symbol info
|
|
1176
|
+
* @param outputPath - Where to save the translated gridset
|
|
1177
|
+
* @param options - Translation options (e.g., allowPartial for testing)
|
|
1178
|
+
* @returns Buffer of the translated gridset
|
|
1179
|
+
*/
|
|
1180
|
+
processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
1181
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
1182
|
+
// Validate translations using shared utility
|
|
1183
|
+
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
1184
|
+
(0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
|
|
1185
|
+
// Create a map for quick lookup
|
|
1186
|
+
const translationMap = new Map(llmTranslations.map((t) => [t.buttonId, t]));
|
|
1187
|
+
// Apply translations
|
|
1188
|
+
Object.values(tree.pages).forEach((page) => {
|
|
1189
|
+
page.buttons.forEach((button) => {
|
|
1190
|
+
const translation = translationMap.get(button.id);
|
|
1191
|
+
if (!translation)
|
|
1192
|
+
return;
|
|
1193
|
+
// Apply label translation
|
|
1194
|
+
if (translation.translatedLabel) {
|
|
1195
|
+
button.label = translation.translatedLabel;
|
|
1196
|
+
}
|
|
1197
|
+
// Apply message translation
|
|
1198
|
+
if (translation.translatedMessage) {
|
|
1199
|
+
button.message = translation.translatedMessage;
|
|
1200
|
+
// Update rich text if symbols provided
|
|
1201
|
+
if (translation.symbols && translation.symbols.length > 0) {
|
|
1202
|
+
if (!button.semanticAction) {
|
|
1203
|
+
button.semanticAction = {
|
|
1204
|
+
category: treeStructure_1.AACSemanticCategory.COMMUNICATION,
|
|
1205
|
+
intent: treeStructure_1.AACSemanticIntent.SPEAK_TEXT,
|
|
1206
|
+
text: translation.translatedMessage,
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
button.semanticAction.richText = {
|
|
1210
|
+
text: translation.translatedMessage,
|
|
1211
|
+
symbols: translation.symbols,
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
});
|
|
1217
|
+
// Save and return
|
|
1218
|
+
this.saveFromTree(tree, outputPath);
|
|
1219
|
+
return fs_1.default.readFileSync(outputPath);
|
|
1220
|
+
}
|
|
1105
1221
|
saveFromTree(tree, outputPath) {
|
|
1106
1222
|
const zip = new adm_zip_1.default();
|
|
1107
1223
|
if (Object.keys(tree.pages).length === 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
|
|
2
2
|
import { AACTree } from '../core/treeStructure';
|
|
3
3
|
import { ValidationResult } from '../validation/validationTypes';
|
|
4
|
+
import { type ButtonForTranslation, type LLMLTranslationResult } from '../utilities/translation/translationProcessor';
|
|
4
5
|
declare class ObfProcessor extends BaseProcessor {
|
|
5
6
|
constructor(options?: ProcessorOptions);
|
|
6
7
|
private processBoard;
|
|
@@ -26,5 +27,30 @@ declare class ObfProcessor extends BaseProcessor {
|
|
|
26
27
|
* @returns Promise with validation result
|
|
27
28
|
*/
|
|
28
29
|
validate(filePath: string): Promise<ValidationResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Extract symbol information from an OBF/OBZ file for LLM-based translation.
|
|
32
|
+
* Returns a structured format showing which buttons have symbols and their context.
|
|
33
|
+
*
|
|
34
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
35
|
+
*
|
|
36
|
+
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
37
|
+
* @returns Array of symbol information for LLM processing
|
|
38
|
+
*/
|
|
39
|
+
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): ButtonForTranslation[];
|
|
40
|
+
/**
|
|
41
|
+
* Apply LLM translations with symbol information.
|
|
42
|
+
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
43
|
+
*
|
|
44
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
45
|
+
*
|
|
46
|
+
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
47
|
+
* @param llmTranslations - Array of LLM translations with symbol info
|
|
48
|
+
* @param outputPath - Where to save the translated OBF/OBZ file
|
|
49
|
+
* @param options - Translation options (e.g., allowPartial for testing)
|
|
50
|
+
* @returns Buffer of the translated OBF/OBZ file
|
|
51
|
+
*/
|
|
52
|
+
processLLMTranslations(filePathOrBuffer: string | Buffer, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
|
|
53
|
+
allowPartial?: boolean;
|
|
54
|
+
}): Buffer;
|
|
29
55
|
}
|
|
30
56
|
export { ObfProcessor };
|
|
@@ -6,10 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ObfProcessor = void 0;
|
|
7
7
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
|
-
const idGenerator_1 = require("../
|
|
9
|
+
const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
|
|
10
10
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const obfValidator_1 = require("../validation/obfValidator");
|
|
13
|
+
const translationProcessor_1 = require("../utilities/translation/translationProcessor");
|
|
13
14
|
const OBF_FORMAT_VERSION = 'open-board-0.1';
|
|
14
15
|
/**
|
|
15
16
|
* Map OBF hidden value to AAC standard visibility
|
|
@@ -403,5 +404,85 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
403
404
|
async validate(filePath) {
|
|
404
405
|
return obfValidator_1.ObfValidator.validateFile(filePath);
|
|
405
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Extract symbol information from an OBF/OBZ file for LLM-based translation.
|
|
409
|
+
* Returns a structured format showing which buttons have symbols and their context.
|
|
410
|
+
*
|
|
411
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
412
|
+
*
|
|
413
|
+
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
414
|
+
* @returns Array of symbol information for LLM processing
|
|
415
|
+
*/
|
|
416
|
+
extractSymbolsForLLM(filePathOrBuffer) {
|
|
417
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
418
|
+
// Collect all buttons from all pages
|
|
419
|
+
const allButtons = [];
|
|
420
|
+
Object.values(tree.pages).forEach((page) => {
|
|
421
|
+
page.buttons.forEach((button) => {
|
|
422
|
+
// Add page context to each button
|
|
423
|
+
button.pageId = page.id;
|
|
424
|
+
button.pageName = page.name || page.id;
|
|
425
|
+
allButtons.push(button);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
// Use shared utility to extract buttons with translation context
|
|
429
|
+
return (0, translationProcessor_1.extractAllButtonsForTranslation)(allButtons, (button) => ({
|
|
430
|
+
pageId: button.pageId,
|
|
431
|
+
pageName: button.pageName,
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Apply LLM translations with symbol information.
|
|
436
|
+
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
437
|
+
*
|
|
438
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
439
|
+
*
|
|
440
|
+
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
441
|
+
* @param llmTranslations - Array of LLM translations with symbol info
|
|
442
|
+
* @param outputPath - Where to save the translated OBF/OBZ file
|
|
443
|
+
* @param options - Translation options (e.g., allowPartial for testing)
|
|
444
|
+
* @returns Buffer of the translated OBF/OBZ file
|
|
445
|
+
*/
|
|
446
|
+
processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
447
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
448
|
+
// Validate translations using shared utility
|
|
449
|
+
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
450
|
+
(0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
|
|
451
|
+
// Create a map for quick lookup
|
|
452
|
+
const translationMap = new Map(llmTranslations.map((t) => [t.buttonId, t]));
|
|
453
|
+
// Apply translations
|
|
454
|
+
Object.values(tree.pages).forEach((page) => {
|
|
455
|
+
page.buttons.forEach((button) => {
|
|
456
|
+
const translation = translationMap.get(button.id);
|
|
457
|
+
if (!translation)
|
|
458
|
+
return;
|
|
459
|
+
// Apply label translation
|
|
460
|
+
if (translation.translatedLabel) {
|
|
461
|
+
button.label = translation.translatedLabel;
|
|
462
|
+
}
|
|
463
|
+
// Apply message translation (vocalization in OBF)
|
|
464
|
+
if (translation.translatedMessage) {
|
|
465
|
+
button.message = translation.translatedMessage;
|
|
466
|
+
// Update semantic action if symbols provided
|
|
467
|
+
if (translation.symbols && translation.symbols.length > 0) {
|
|
468
|
+
if (!button.semanticAction) {
|
|
469
|
+
button.semanticAction = {
|
|
470
|
+
category: treeStructure_1.AACSemanticCategory.COMMUNICATION,
|
|
471
|
+
intent: treeStructure_1.AACSemanticIntent.SPEAK_TEXT,
|
|
472
|
+
text: translation.translatedMessage,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
button.semanticAction.richText = {
|
|
476
|
+
text: translation.translatedMessage,
|
|
477
|
+
symbols: translation.symbols,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
// Save and return
|
|
484
|
+
this.saveFromTree(tree, outputPath);
|
|
485
|
+
return fs_1.default.readFileSync(outputPath);
|
|
486
|
+
}
|
|
406
487
|
}
|
|
407
488
|
exports.ObfProcessor = ObfProcessor;
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.SnapProcessor = void 0;
|
|
7
7
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
|
-
const idGenerator_1 = require("../
|
|
9
|
+
const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
|
|
10
10
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
|
|
2
2
|
import { AACTree } from '../core/treeStructure';
|
|
3
3
|
import { ValidationResult } from '../validation/validationTypes';
|
|
4
|
+
import { type ButtonForTranslation, type LLMLTranslationResult } from '../utilities/translation/translationProcessor';
|
|
4
5
|
declare class TouchChatProcessor extends BaseProcessor {
|
|
5
6
|
private tree;
|
|
6
7
|
private sourceFile;
|
|
@@ -30,5 +31,30 @@ declare class TouchChatProcessor extends BaseProcessor {
|
|
|
30
31
|
* @returns Promise with validation result
|
|
31
32
|
*/
|
|
32
33
|
validate(filePath: string): Promise<ValidationResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Extract symbol information from a TouchChat file for LLM-based translation.
|
|
36
|
+
* Returns a structured format showing which buttons have symbols and their context.
|
|
37
|
+
*
|
|
38
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
39
|
+
*
|
|
40
|
+
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
41
|
+
* @returns Array of symbol information for LLM processing
|
|
42
|
+
*/
|
|
43
|
+
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): ButtonForTranslation[];
|
|
44
|
+
/**
|
|
45
|
+
* Apply LLM translations with symbol information.
|
|
46
|
+
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
47
|
+
*
|
|
48
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
49
|
+
*
|
|
50
|
+
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
51
|
+
* @param llmTranslations - Array of LLM translations with symbol info
|
|
52
|
+
* @param outputPath - Where to save the translated TouchChat file
|
|
53
|
+
* @param options - Translation options (e.g., allowPartial for testing)
|
|
54
|
+
* @returns Buffer of the translated TouchChat file
|
|
55
|
+
*/
|
|
56
|
+
processLLMTranslations(filePathOrBuffer: string | Buffer, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
|
|
57
|
+
allowPartial?: boolean;
|
|
58
|
+
}): Buffer;
|
|
33
59
|
}
|
|
34
60
|
export { TouchChatProcessor };
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.TouchChatProcessor = void 0;
|
|
7
7
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
|
-
const idGenerator_1 = require("../
|
|
9
|
+
const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
|
|
10
10
|
const stringCasing_1 = require("../core/stringCasing");
|
|
11
11
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
12
12
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
@@ -14,6 +14,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
14
14
|
const fs_1 = __importDefault(require("fs"));
|
|
15
15
|
const os_1 = __importDefault(require("os"));
|
|
16
16
|
const touchChatValidator_1 = require("../validation/touchChatValidator");
|
|
17
|
+
const translationProcessor_1 = require("../utilities/translation/translationProcessor");
|
|
17
18
|
const toNumberOrUndefined = (value) => typeof value === 'number' ? value : undefined;
|
|
18
19
|
const toStringOrUndefined = (value) => typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
19
20
|
const toBooleanOrUndefined = (value) => typeof value === 'number' ? value !== 0 : undefined;
|
|
@@ -842,5 +843,85 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
842
843
|
async validate(filePath) {
|
|
843
844
|
return touchChatValidator_1.TouchChatValidator.validateFile(filePath);
|
|
844
845
|
}
|
|
846
|
+
/**
|
|
847
|
+
* Extract symbol information from a TouchChat file for LLM-based translation.
|
|
848
|
+
* Returns a structured format showing which buttons have symbols and their context.
|
|
849
|
+
*
|
|
850
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
851
|
+
*
|
|
852
|
+
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
853
|
+
* @returns Array of symbol information for LLM processing
|
|
854
|
+
*/
|
|
855
|
+
extractSymbolsForLLM(filePathOrBuffer) {
|
|
856
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
857
|
+
// Collect all buttons from all pages
|
|
858
|
+
const allButtons = [];
|
|
859
|
+
Object.values(tree.pages).forEach((page) => {
|
|
860
|
+
page.buttons.forEach((button) => {
|
|
861
|
+
// Add page context to each button
|
|
862
|
+
button.pageId = page.id;
|
|
863
|
+
button.pageName = page.name || page.id;
|
|
864
|
+
allButtons.push(button);
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
// Use shared utility to extract buttons with translation context
|
|
868
|
+
return (0, translationProcessor_1.extractAllButtonsForTranslation)(allButtons, (button) => ({
|
|
869
|
+
pageId: button.pageId,
|
|
870
|
+
pageName: button.pageName,
|
|
871
|
+
}));
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Apply LLM translations with symbol information.
|
|
875
|
+
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
876
|
+
*
|
|
877
|
+
* This method uses shared translation utilities that work across all AAC formats.
|
|
878
|
+
*
|
|
879
|
+
* @param filePathOrBuffer - Path to TouchChat .ce file or buffer
|
|
880
|
+
* @param llmTranslations - Array of LLM translations with symbol info
|
|
881
|
+
* @param outputPath - Where to save the translated TouchChat file
|
|
882
|
+
* @param options - Translation options (e.g., allowPartial for testing)
|
|
883
|
+
* @returns Buffer of the translated TouchChat file
|
|
884
|
+
*/
|
|
885
|
+
processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
886
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
887
|
+
// Validate translations using shared utility
|
|
888
|
+
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
889
|
+
(0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
|
|
890
|
+
// Create a map for quick lookup
|
|
891
|
+
const translationMap = new Map(llmTranslations.map((t) => [t.buttonId, t]));
|
|
892
|
+
// Apply translations
|
|
893
|
+
Object.values(tree.pages).forEach((page) => {
|
|
894
|
+
page.buttons.forEach((button) => {
|
|
895
|
+
const translation = translationMap.get(button.id);
|
|
896
|
+
if (!translation)
|
|
897
|
+
return;
|
|
898
|
+
// Apply label translation
|
|
899
|
+
if (translation.translatedLabel) {
|
|
900
|
+
button.label = translation.translatedLabel;
|
|
901
|
+
}
|
|
902
|
+
// Apply message translation
|
|
903
|
+
if (translation.translatedMessage) {
|
|
904
|
+
button.message = translation.translatedMessage;
|
|
905
|
+
// Update semantic action if symbols provided
|
|
906
|
+
if (translation.symbols && translation.symbols.length > 0) {
|
|
907
|
+
if (!button.semanticAction) {
|
|
908
|
+
button.semanticAction = {
|
|
909
|
+
category: treeStructure_1.AACSemanticCategory.COMMUNICATION,
|
|
910
|
+
intent: treeStructure_1.AACSemanticIntent.SPEAK_TEXT,
|
|
911
|
+
text: translation.translatedMessage,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
button.semanticAction.richText = {
|
|
915
|
+
text: translation.translatedMessage,
|
|
916
|
+
symbols: translation.symbols,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
// Save and return
|
|
923
|
+
this.saveFromTree(tree, outputPath);
|
|
924
|
+
return fs_1.default.readFileSync(outputPath);
|
|
925
|
+
}
|
|
845
926
|
}
|
|
846
927
|
exports.TouchChatProcessor = TouchChatProcessor;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-Based Translation with Symbol Preservation
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for translating AAC files while preserving
|
|
5
|
+
* symbol-to-word associations across different formats (gridset, OBF, Snap, etc.).
|
|
6
|
+
*
|
|
7
|
+
* The key insight: Different AAC formats have different internal structures,
|
|
8
|
+
* but they all share common concepts:
|
|
9
|
+
* - Buttons with labels and messages
|
|
10
|
+
* - Symbols attached to specific words
|
|
11
|
+
* - Need to preserve symbol positions during translation
|
|
12
|
+
*
|
|
13
|
+
* This module provides a format-agnostic way to:
|
|
14
|
+
* 1. Extract symbol information for LLM processing
|
|
15
|
+
* 2. Apply LLM translations with preserved symbols
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* 1. Processor extracts buttons and calls extractSymbolsForLLM()
|
|
19
|
+
* 2. LLM translates and returns aligned symbols
|
|
20
|
+
* 3. Processor calls processLLMTranslations() to apply results
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Represents a symbol attached to text in a format-agnostic way
|
|
24
|
+
*/
|
|
25
|
+
export interface SymbolInfo {
|
|
26
|
+
text: string;
|
|
27
|
+
image?: string;
|
|
28
|
+
symbolLibrary?: string;
|
|
29
|
+
symbolPath?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Button data extracted for translation (format-agnostic)
|
|
33
|
+
*/
|
|
34
|
+
export interface ButtonForTranslation {
|
|
35
|
+
buttonId: string;
|
|
36
|
+
pageId?: string;
|
|
37
|
+
pageName?: string;
|
|
38
|
+
label: string;
|
|
39
|
+
message: string;
|
|
40
|
+
textToTranslate: string;
|
|
41
|
+
symbols: SymbolInfo[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* LLM translation result with symbol mappings
|
|
45
|
+
*/
|
|
46
|
+
export interface LLMLTranslationResult {
|
|
47
|
+
buttonId: string;
|
|
48
|
+
translatedLabel?: string;
|
|
49
|
+
translatedMessage?: string;
|
|
50
|
+
symbols?: Array<{
|
|
51
|
+
text: string;
|
|
52
|
+
image?: string;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Extract symbols from a button for LLM-based translation.
|
|
57
|
+
*
|
|
58
|
+
* This is a format-agnostic helper that processors can use to normalize
|
|
59
|
+
* their button data into a common format for LLM processing.
|
|
60
|
+
*
|
|
61
|
+
* @param buttonId - Unique identifier for the button
|
|
62
|
+
* @param label - Button label text
|
|
63
|
+
* @param message - Button message/speak text
|
|
64
|
+
* @param symbols - Array of symbols from the button
|
|
65
|
+
* @param context - Optional page context
|
|
66
|
+
* @returns Normalized button data for translation
|
|
67
|
+
*/
|
|
68
|
+
export declare function normalizeButtonForTranslation(buttonId: string, label: string, message: string, symbols: SymbolInfo[], context?: {
|
|
69
|
+
pageId?: string;
|
|
70
|
+
pageName?: string;
|
|
71
|
+
}): ButtonForTranslation;
|
|
72
|
+
/**
|
|
73
|
+
* Extract symbols from various button formats.
|
|
74
|
+
*
|
|
75
|
+
* This helper handles different ways symbols might be stored in button data:
|
|
76
|
+
* - semanticAction.richText.symbols (gridset format)
|
|
77
|
+
* - symbolLibrary + symbolPath fields
|
|
78
|
+
* - image field with [library]path format
|
|
79
|
+
*
|
|
80
|
+
* @param button - Button object from any AAC format
|
|
81
|
+
* @returns Array of symbol info, or undefined if no symbols
|
|
82
|
+
*/
|
|
83
|
+
export declare function extractSymbolsFromButton(button: any): SymbolInfo[] | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Extract all buttons from a file for LLM translation.
|
|
86
|
+
*
|
|
87
|
+
* This is a convenience method that processors can use to extract all
|
|
88
|
+
* translatable buttons with their symbols in a format-agnostic way.
|
|
89
|
+
*
|
|
90
|
+
* @param buttons - Array of button objects from any AAC format
|
|
91
|
+
* @param contextFn - Optional function to provide page context for each button
|
|
92
|
+
* @returns Array of normalized button data ready for LLM translation
|
|
93
|
+
*/
|
|
94
|
+
export declare function extractAllButtonsForTranslation(buttons: any[], contextFn?: (button: any) => {
|
|
95
|
+
pageId?: string;
|
|
96
|
+
pageName?: string;
|
|
97
|
+
}): ButtonForTranslation[];
|
|
98
|
+
/**
|
|
99
|
+
* Create a prompt for LLM translation with symbol preservation.
|
|
100
|
+
*
|
|
101
|
+
* This generates a structured prompt that instructs the LLM to translate
|
|
102
|
+
* while preserving symbol-to-word associations.
|
|
103
|
+
*
|
|
104
|
+
* @param buttons - Buttons to translate
|
|
105
|
+
* @param targetLanguage - Target language for translation
|
|
106
|
+
* @returns Prompt string for LLM
|
|
107
|
+
*/
|
|
108
|
+
export declare function createTranslationPrompt(buttons: ButtonForTranslation[], targetLanguage: string): string;
|
|
109
|
+
/**
|
|
110
|
+
* Validate LLM translation results before applying.
|
|
111
|
+
*
|
|
112
|
+
* @param translations - LLM translation results
|
|
113
|
+
* @param originalButtonIds - Expected button IDs (optional, for validation)
|
|
114
|
+
* @param options - Validation options
|
|
115
|
+
* @throws Error if validation fails
|
|
116
|
+
*/
|
|
117
|
+
export declare function validateTranslationResults(translations: LLMLTranslationResult[], originalButtonIds?: string[], options?: {
|
|
118
|
+
allowPartial?: boolean;
|
|
119
|
+
}): void;
|