11import PathGenerator from "./controllers/path.ts" ;
22import translate , { getFileErrors } from "./controllers/recurTranslate.ts" ;
3- import fs , { Dirent } from "fs" ;
3+ import fs from "fs" ;
44import util from "util" ;
55import path from "path" ;
66import { fileURLToPath } from "url" ;
77import { dirname } from "path" ;
8- import OpenAI from "openai" ;
9- import { max_trans_num , translationSummaryPrefix } from "./config.ts" ;
8+ import { max_trans_num } from "./config.ts" ;
9+ import { saveSummaryLog } from "./controllers/loggers.ts" ;
10+ import { setupCleanupHandlers } from "./initializers/processHandler.ts" ;
11+ import { findAllXmlFiles , needsTranslation } from "./controllers/fileUtilities.ts" ;
1012
1113// Get the directory name of the current module
1214const __filename = fileURLToPath ( import . meta. url ) ;
@@ -27,201 +29,14 @@ let failureCount = 0;
2729let processedCount = 0 ;
2830let failures : { file : string ; error : any } [ ] = [ ] ;
2931
30- // Function to save summary log - can be called from signal handlers
31- async function saveSummaryLog ( ) {
32- try {
33- const ai = new OpenAI ( {
34- apiKey : process . env . API_KEY ,
35- baseURL : process . env . AI_BASEURL
36- } ) ;
37-
38- // list and delete all assistants
39- const assistants = await ai . beta . assistants . list ( { limit : 100 } ) ;
40- const failedDel : string [ ] = [ ] ;
41- await Promise . all (
42- assistants . data . map ( async assistant => {
43- try {
44- await ai . beta . assistants . del ( assistant . id ) ;
45- } catch ( error ) {
46- failedDel . push ( assistant . id ) ;
47- }
48- } )
49- ) . then ( ( ) => console . log ( "successfully removed all assistants" ) ) ;
50-
51- // list and delete all uploaded files
52- const files = await ai . files . list ( ) ;
53- await Promise . all (
54- files . data . map ( async file => {
55- try {
56- await ai . files . del ( file . id ) ;
57- } catch ( error ) {
58- failedDel . push ( file . id ) ;
59- }
60- } )
61- ) . then ( ( ) => console . log ( "successfully deleted all files" ) ) ;
62-
63- const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, "-" ) ;
64- let summaryLog = `
65- Translation Summary (${ timestamp } )
66- ================================
67- Total files scanned: ${ xmlFiles . length }
68- Files needing translation: ${ filesToTranslate . length }
69- Successfully translated: ${ successCount }
70- Failed translations: ${ failureCount }
71- Success rate: ${ filesToTranslate . length > 0 ? ( ( successCount / filesToTranslate . length ) * 100 ) . toFixed ( 2 ) : 0 } %
72- ` ;
73-
74- if ( failedDel . length > 0 ) {
75- summaryLog += `\nFailed to remove ${ failedDel . length } assistants\n` ;
76- failedDel . forEach (
77- ( assistant , index ) => ( summaryLog += `${ index + 1 } . ${ assistant } \n` )
78- ) ;
79- }
80-
81- // Add failed translations to the log
82- if ( failures . length > 0 ) {
83- summaryLog += `\nFailed Translations (High-level errors):\n` ;
84- failures . forEach ( ( failure , index ) => {
85- summaryLog += `${ index + 1 } . ${ failure . file } \n Error: ${ failure . error } \n\n` ;
86- } ) ;
87- }
88-
89- // Add detailed errors captured during translation process
90- const fileErrors = getFileErrors ( ) ;
91- if ( Object . keys ( fileErrors ) . length > 0 ) {
92- failureCount = Object . keys ( fileErrors ) . length ;
93- summaryLog += `\nDetailed Translation Errors:\n` ;
94- summaryLog += `============================\n` ;
95-
96- for ( const [ filePath , errors ] of Object . entries ( fileErrors ) ) {
97- summaryLog += `\nFile: ${ filePath } \n` ;
98- errors . forEach ( ( error , index ) => {
99- summaryLog += ` ${ index + 1 } . ${ error . error } \n` ;
100- if ( error . error ) {
101- // Format the error object/message for better readability
102- const errorStr =
103- typeof error . error === "object"
104- ? JSON . stringify ( error . error , null , 2 ) . substring ( 0 , 500 ) // Limit very long errors
105- : String ( error . error ) ;
106- summaryLog += ` Details: ${ errorStr } \n` ;
107- }
108- } ) ;
109- }
110- }
111-
112- const logDir = path . resolve ( __dirname , "../logs" ) ;
113- if ( ! fs . existsSync ( logDir ) ) {
114- fs . mkdirSync ( logDir , { recursive : true } ) ;
115- }
116-
117- const logPath = path . join ( logDir , `${ translationSummaryPrefix } -${ timestamp } .log` ) ;
118- fs . writeFileSync ( logPath , summaryLog ) ;
119- console . log (
120- `Summary log saved to logs/translation-summary-${ timestamp } .log`
121- ) ;
122- } catch ( logError ) {
123- console . error ( "Failed to save log file:" , logError ) ;
124- }
125- }
126-
127- // Register handlers for various termination signals
128- async function setupCleanupHandlers ( ) {
129- // Handle normal exit
130- process . on ( "exit" , ( ) => {
131- console . log ( "Process is exiting, saving summary..." ) ;
132- // Only synchronous operations work in 'exit' handlers
133- // We can't await here, as exit handlers must be synchronous
134- try {
135- // Use synchronous operations for final cleanup if needed
136- // Note: This won't actually call the async parts of saveSummaryLog properly
137- const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, "-" ) ;
138- const summaryContent = `Translation interrupted during exit at ${ timestamp } ` ;
139- const logDir = path . resolve ( __dirname , "../logs" ) ;
140- if ( ! fs . existsSync ( logDir ) ) {
141- fs . mkdirSync ( logDir , { recursive : true } ) ;
142- }
143- fs . writeFileSync (
144- path . join ( logDir , `emergency-log-${ timestamp } .log` ) ,
145- summaryContent
146- ) ;
147- } catch ( error ) {
148- console . error ( "Failed to save emergency log during exit:" , error ) ;
149- }
150- } ) ;
151-
152- // Handle Ctrl+C
153- process . on ( "SIGINT" , async ( ) => {
154- console . log ( "\nReceived SIGINT (Ctrl+C). Saving summary before exit..." ) ;
155- await saveSummaryLog ( ) ;
156- process . exit ( 1 ) ;
157- } ) ;
158-
159- // Handle SIGTERM (kill command)
160- process . on ( "SIGTERM" , async ( ) => {
161- console . log ( "\nReceived SIGTERM. Saving summary before exit..." ) ;
162- await saveSummaryLog ( ) ;
163- process . exit ( 1 ) ;
164- } ) ;
165-
166- // Handle uncaught exceptions
167- process . on ( "uncaughtException" , async error => {
168- console . error ( "\nUncaught exception:" , error ) ;
169- console . log ( "Saving summary before exit..." ) ;
170- await saveSummaryLog ( ) ;
171- process . exit ( 1 ) ;
172- } ) ;
173- }
174-
175- async function needsTranslation (
176- enFilePath : string ,
177- lang : string
178- ) : Promise < boolean > {
179- const cnFilePath = enFilePath . replace (
180- path . sep + "en" + path . sep ,
181- path . sep + lang + path . sep
182- ) ;
183- try {
184- const cnStats = await fs . promises . stat ( cnFilePath ) ;
185- if ( ! cnStats . isFile ( ) ) {
186- return true ;
187- }
188-
189- const enStats = await fs . promises . stat ( enFilePath ) ;
190- return enStats . mtime > cnStats . mtime ;
191- } catch ( error ) {
192- throw error ;
193- }
194- }
195-
196- // Function to recursively find all XML files in a directory
197- async function findAllXmlFiles ( directory : string ) : Promise < string [ ] > {
198- const files = await fs . promises . readdir ( directory ) ;
199- const xmlFiles : string [ ] = [ ] ;
200-
201- for ( const file of files ) {
202- const fullPath = path . join ( directory , file ) ;
203- const stat = await fs . promises . stat ( fullPath ) ;
204-
205- if ( stat . isDirectory ( ) ) {
206- // Recursively search subdirectories
207- const subDirFiles = await findAllXmlFiles ( fullPath ) ;
208- xmlFiles . push ( ...subDirFiles ) ;
209- } else if ( path . extname ( file ) . toLowerCase ( ) === ".xml" ) {
210- // Add XML files to the list
211- xmlFiles . push ( fullPath ) ;
212- }
213- }
214-
215- return xmlFiles ;
216- }
21732
218- export default async function fancyName ( path : string , language : string ) {
33+ export default async function translateSingle ( path : string , language : string ) {
21934 const fullPath = PathGenerator ( path ) ;
22035 await translate ( language , fullPath ) ;
22136}
22237
22338( async ( ) => {
224- await setupCleanupHandlers ( ) ;
39+ await setupCleanupHandlers ( xmlFiles , failures , filesToTranslate . length , failureCount , successCount ) ;
22540
22641 try {
22742 const languages : string [ ] = await getDirectories (
@@ -354,7 +169,7 @@ export default async function fancyName(path: string, language: string) {
354169 }
355170
356171 // Save a detailed summary to a log file
357- await saveSummaryLog ( ) ;
172+ await saveSummaryLog ( xmlFiles , failures , filesToTranslate . length , failureCount , successCount ) ;
358173 }
359174 } catch ( e ) {
360175 console . error ( "Error during translation process:" , e ) ;
0 commit comments