@@ -5,14 +5,11 @@ import createAssistant from "../initializers/initialize";
5
5
import dotenv from "dotenv" ;
6
6
import sax from "sax" ;
7
7
import { Readable } from "stream" ;
8
- import { fileURLToPath } from "url" ;
9
- import { isGeneratorObject } from "util/types" ;
10
- import { AssistantStream } from "openai/lib/AssistantStream.mjs" ;
11
8
12
9
dotenv . config ( ) ;
13
10
14
11
if ( process . env . AI_MODEL === undefined || process . env . API_KEY === undefined ) {
15
- throw Error ( "Please specify AI_MODEL and API_KEY! " ) ;
12
+ throw Error ( "Please specify AI_MODEL and API_KEY" ) ;
16
13
}
17
14
18
15
// initialize OpenAI API
@@ -35,7 +32,10 @@ const MAXLEN = Number(process.env.MAX_LEN) || 3000;
35
32
36
33
// Centralized logging to prevent duplicate messages
37
34
const errorMessages = new Set ( ) ;
38
- function logError ( message : string , error ?: any ) {
35
+ // Track errors by file for summary reporting
36
+ const fileErrors : Record < string , { message : string ; error ?: any } [ ] > = { } ;
37
+
38
+ function logError ( message : string , error ?: any , filePath ?: string ) {
39
39
// Create a unique key for this error message
40
40
const errorKey = message + ( error ? error . toString ( ) : "" ) ;
41
41
// Only log if we haven't seen this exact message before
@@ -46,11 +46,27 @@ function logError(message: string, error?: any) {
46
46
} else {
47
47
console . error ( message ) ;
48
48
}
49
+
50
+ // Store error for the summary log if filePath is provided
51
+ if ( filePath ) {
52
+ if ( ! fileErrors [ filePath ] ) {
53
+ fileErrors [ filePath ] = [ ] ;
54
+ }
55
+ fileErrors [ filePath ] . push ( { message, error } ) ;
56
+ }
49
57
}
50
58
}
51
59
60
+ // Function to get all logged errors for summary
61
+ export function getFileErrors ( ) : Record <
62
+ string ,
63
+ { message : string ; error ?: any } [ ]
64
+ > {
65
+ return fileErrors ;
66
+ }
67
+
52
68
const createParser = ( ) =>
53
- ( sax as any ) . createStream ( true , { trim : false } , { strictEntities : true } ) ;
69
+ ( sax as any ) . createStream ( false , { trim : false } , { strictEntities : true } ) ;
54
70
55
71
async function translate ( language : string , filePath : string ) : Promise < void > {
56
72
const startTime = new Date ( ) . getTime ( ) ;
@@ -81,11 +97,13 @@ async function translate(language: string, filePath: string): Promise<void> {
81
97
fs . writeFileSync ( output_path , translated ) ;
82
98
console . log ( `Translation saved to ${ output_path } ` ) ;
83
99
} catch ( parseErr ) {
84
- logError ( `Error translating file ${ filePath } :` , parseErr ) ;
100
+ logError ( `Error translating file ${ filePath } :` , parseErr , filePath ) ;
101
+ // Re-throw the error to propagate it to the caller
102
+ throw parseErr ;
85
103
} finally {
86
104
if ( assistant ) {
87
105
await ai . beta . assistants . del ( assistant . id ) . catch ( err => {
88
- logError ( `Error deleting assistant:` , err ) ;
106
+ logError ( `Error deleting assistant:` , err , filePath ) ;
89
107
} ) ;
90
108
}
91
109
const elapsed = new Date ( ) . getTime ( ) - startTime ;
@@ -210,7 +228,11 @@ async function recursivelyTranslate(
210
228
subTranslated . push ( segment [ 1 ] ) ;
211
229
}
212
230
} catch ( error ) {
213
- logError ( `Error translating segment in ${ filePath } :` , error ) ;
231
+ logError (
232
+ `Error translating segment in ${ filePath } :` ,
233
+ error ,
234
+ filePath
235
+ ) ;
214
236
// Add error comment and continue with next segment
215
237
subTranslated . push (
216
238
segment [ 1 ] + `<!-- Error translating this segment -->`
@@ -221,13 +243,13 @@ async function recursivelyTranslate(
221
243
} ) ;
222
244
223
245
subParser . on ( "error" , err => {
224
- logError ( `Error in subParser for ${ filePath } :` , err ) ;
246
+ logError ( `Error in subParser for ${ filePath } :` , err , filePath ) ;
225
247
// Try to recover and continue
226
248
try {
227
249
subParser . _parser . error = null ;
228
250
subParser . _parser . resume ( ) ;
229
251
} catch ( resumeErr ) {
230
- logError ( `Could not recover from parser error:` , resumeErr ) ;
252
+ logError ( `Could not recover from parser error:` , resumeErr , filePath ) ;
231
253
reject ( err ) ;
232
254
}
233
255
} ) ;
@@ -262,6 +284,8 @@ async function recursivelyTranslate(
262
284
currentDepth ++ ;
263
285
264
286
// If we're at depth 2, this is the start of a new segment.
287
+ if ( node . name == "SCHEME" || node . name == "SCHEMEINLINE" ) return ;
288
+
265
289
if ( currentDepth === 2 || isRecording ) {
266
290
isRecording = true ;
267
291
currentSegment += `<${ node . name } ${ formatAttributes ( node . attributes ) } >` ;
@@ -275,6 +299,10 @@ async function recursivelyTranslate(
275
299
276
300
parser . on ( "text" , text => {
277
301
text = strongEscapeXML ( text ) ;
302
+
303
+ // ignore all scheme contents
304
+ if ( parser . _parser . tag == "SCHEME" || parser . _parser . tag == "SCHEMEINLINE" ) return ;
305
+
278
306
if ( isRecording ) {
279
307
currentSegment += text ;
280
308
} else {
@@ -289,7 +317,7 @@ async function recursivelyTranslate(
289
317
} ) ;
290
318
291
319
parser . on ( "closetag" , tagName => {
292
- if ( isRecording ) {
320
+ if ( tagName !== "SCHEME" && tagName !== "SCHEMEINLINE" && isRecording ) {
293
321
currentSegment += `</${ tagName } >` ;
294
322
}
295
323
@@ -338,7 +366,11 @@ async function recursivelyTranslate(
338
366
translated . push ( segment [ 1 ] ) ;
339
367
}
340
368
} catch ( error ) {
341
- logError ( `Error translating segment in ${ filePath } :` , error ) ;
369
+ logError (
370
+ `Error translating segment in ${ filePath } :` ,
371
+ error ,
372
+ filePath
373
+ ) ;
342
374
// Add error comment and continue with next segment
343
375
translated . push (
344
376
segment [ 1 ] + `<!-- Error translating this section -->`
@@ -349,13 +381,13 @@ async function recursivelyTranslate(
349
381
} ) ;
350
382
351
383
parser . on ( "error" , err => {
352
- logError ( `Parser error in ${ filePath } :` , err ) ;
384
+ logError ( `Parser error in ${ filePath } :` , err , filePath ) ;
353
385
// Try to recover and continue
354
386
try {
355
387
parser . _parser . error = null ;
356
388
parser . _parser . resume ( ) ;
357
389
} catch ( resumeErr ) {
358
- logError ( `Could not recover from parser error:` , resumeErr ) ;
390
+ logError ( `Could not recover from parser error:` , resumeErr , filePath ) ;
359
391
reject ( err ) ;
360
392
}
361
393
} ) ;
@@ -366,12 +398,13 @@ async function recursivelyTranslate(
366
398
367
399
return translated . join ( "" ) ;
368
400
} catch ( parseErr ) {
369
- logError ( `Error parsing XML in ${ filePath } :` , parseErr ) ;
401
+ logError ( `Error parsing XML in ${ filePath } :` , parseErr , filePath ) ;
370
402
// Return what we have so far plus error comment
371
403
return translated . join ( "" ) + `<!-- Error parsing this file -->` ;
372
404
}
373
405
374
406
async function translateChunk ( chunk : string ) : Promise < string > {
407
+ return chunk ;
375
408
if ( chunk . trim ( ) === "" || chunk . trim ( ) === "," || chunk . trim ( ) === "." ) {
376
409
return chunk ;
377
410
}
@@ -400,6 +433,7 @@ async function recursivelyTranslate(
400
433
const message = messages . data . pop ( ) ! ;
401
434
const messageContent = message ?. content [ 0 ] ;
402
435
436
+ if ( ! messageContent ) throw new Error ( `undefined AI response` ) ;
403
437
if ( messageContent . type !== "text" ) {
404
438
throw new Error (
405
439
`Unexpected message content type: ${ messageContent . type } `
@@ -428,8 +462,7 @@ async function recursivelyTranslate(
428
462
currDepth ++ ;
429
463
if (
430
464
node . name != "WRAPPER" &&
431
- node . name != "TRANSLATE" &&
432
- ! ignoredTags . includes ( node . name )
465
+ node . name != "TRANSLATE"
433
466
) {
434
467
translatedChunk += `<${ node . name } ${ formatAttributes ( node . attributes ) } >` ;
435
468
}
@@ -438,8 +471,7 @@ async function recursivelyTranslate(
438
471
clean . on ( "closetag" , tagName => {
439
472
if (
440
473
tagName != "WRAPPER" &&
441
- tagName != "TRANSLATE" &&
442
- ! ignoredTags . includes ( tagName )
474
+ tagName != "TRANSLATE"
443
475
) {
444
476
translatedChunk += `</${ tagName } >` ;
445
477
}
@@ -456,7 +488,11 @@ async function recursivelyTranslate(
456
488
457
489
clean . on ( "error" , error => {
458
490
// Log only once with abbreviated content
459
- logError ( `XML validation error in ${ filePath } ` , error ) ;
491
+ logError (
492
+ `Error validating AI response for ${ filePath } ` ,
493
+ error ,
494
+ filePath
495
+ ) ;
460
496
461
497
// Attempt to recover using the internal parser
462
498
try {
@@ -478,7 +514,11 @@ async function recursivelyTranslate(
478
514
479
515
return translatedChunk ;
480
516
} catch ( err ) {
481
- logError ( `Error occurred while translating chunk in ${ filePath } :` , err ) ;
517
+ logError (
518
+ `Error occurred while translating chunk in ${ filePath } :` ,
519
+ err ,
520
+ filePath
521
+ ) ;
482
522
// Return the original chunk with error comment rather than throwing
483
523
return chunk + `<!-- Error occurred while translating this section -->` ;
484
524
}
@@ -488,7 +528,7 @@ async function recursivelyTranslate(
488
528
export default translate ;
489
529
490
530
// Helper function to format attributes into a string.
491
- function formatAttributes ( attrs ) {
531
+ function formatAttributes ( attrs : string ) {
492
532
const attrStr = Object . entries ( attrs )
493
533
. map ( ( [ key , val ] ) => `${ key } ="${ val } "` )
494
534
. join ( " " ) ;
0 commit comments