Skip to content

Commit 12531b3

Browse files
committed
refactored index.ts
1 parent 23da84c commit 12531b3

File tree

4 files changed

+219
-193
lines changed

4 files changed

+219
-193
lines changed

i18n/controllers/fileUtilities.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import path from "path";
2+
import fs from "fs";
3+
4+
export async function needsTranslation(
5+
enFilePath: string,
6+
lang: string
7+
): Promise<boolean> {
8+
const cnFilePath = enFilePath.replace(
9+
path.sep + "en" + path.sep,
10+
path.sep + lang + path.sep
11+
);
12+
try {
13+
const cnStats = await fs.promises.stat(cnFilePath);
14+
if (!cnStats.isFile()) {
15+
return true;
16+
}
17+
18+
const enStats = await fs.promises.stat(enFilePath);
19+
return enStats.mtime > cnStats.mtime;
20+
} catch (error) {
21+
throw error;
22+
}
23+
}
24+
25+
// Function to recursively find all XML files in a directory
26+
export async function findAllXmlFiles(directory: string): Promise<string[]> {
27+
const files = await fs.promises.readdir(directory);
28+
const xmlFiles: string[] = [];
29+
30+
for (const file of files) {
31+
const fullPath = path.join(directory, file);
32+
const stat = await fs.promises.stat(fullPath);
33+
34+
if (stat.isDirectory()) {
35+
// Recursively search subdirectories
36+
const subDirFiles = await findAllXmlFiles(fullPath);
37+
xmlFiles.push(...subDirFiles);
38+
} else if (path.extname(file).toLowerCase() === ".xml") {
39+
// Add XML files to the list
40+
xmlFiles.push(fullPath);
41+
}
42+
}
43+
44+
return xmlFiles;
45+
}

i18n/controllers/loggers.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import OpenAI from "openai";
2+
import path from "path";
3+
import fs from "fs";
4+
import { getFileErrors } from "./recurTranslate";
5+
import { translationSummaryPrefix } from "../config";
6+
7+
// Function to save summary log - can be called from signal handlers
8+
export async function saveSummaryLog(
9+
xmlFiles: string[],
10+
failures: { file: string; error: any }[],
11+
translateNum: number,
12+
failureCount: number,
13+
successCount: number
14+
) {
15+
try {
16+
const ai = new OpenAI({
17+
apiKey: process.env.API_KEY,
18+
baseURL: process.env.AI_BASEURL
19+
});
20+
21+
// list and delete all assistants
22+
const assistants = await ai.beta.assistants.list({ limit: 100 });
23+
const failedDel: string[] = [];
24+
await Promise.all(
25+
assistants.data.map(async assistant => {
26+
try {
27+
await ai.beta.assistants.del(assistant.id);
28+
} catch (error) {
29+
failedDel.push(assistant.id);
30+
}
31+
})
32+
).then(() => console.log("successfully removed all assistants"));
33+
34+
// list and delete all uploaded files
35+
const files = await ai.files.list();
36+
await Promise.all(
37+
files.data.map(async file => {
38+
try {
39+
await ai.files.del(file.id);
40+
} catch (error) {
41+
failedDel.push(file.id);
42+
}
43+
})
44+
).then(() => console.log("successfully deleted all files"));
45+
46+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
47+
let summaryLog = `
48+
Translation Summary (${timestamp})
49+
================================
50+
Total files scanned: ${xmlFiles.length}
51+
Files needing translation: ${translateNum}
52+
Successfully translated: ${successCount}
53+
Failed translations: ${failureCount}
54+
Success rate: ${translateNum > 0 ? ((successCount / translateNum) * 100).toFixed(2) : 0}%
55+
`;
56+
57+
if (failedDel.length > 0) {
58+
summaryLog += `\nFailed to remove ${failedDel.length} assistants\n`;
59+
failedDel.forEach(
60+
(assistant, index) => (summaryLog += `${index + 1}. ${assistant}\n`)
61+
);
62+
}
63+
64+
// Add failed translations to the log
65+
if (failures.length > 0) {
66+
summaryLog += `\nFailed Translations (High-level errors):\n`;
67+
failures.forEach((failure, index) => {
68+
summaryLog += `${index + 1}. ${failure.file}\n Error: ${failure.error}\n\n`;
69+
});
70+
}
71+
72+
// Add detailed errors captured during translation process
73+
const fileErrors = getFileErrors();
74+
if (Object.keys(fileErrors).length > 0) {
75+
failureCount = Object.keys(fileErrors).length;
76+
summaryLog += `\nDetailed Translation Errors:\n`;
77+
summaryLog += `============================\n`;
78+
79+
for (const [filePath, errors] of Object.entries(fileErrors)) {
80+
summaryLog += `\nFile: ${filePath}\n`;
81+
errors.forEach((error, index) => {
82+
summaryLog += ` ${index + 1}. ${error.error}\n`;
83+
if (error.error) {
84+
// Format the error object/message for better readability
85+
const errorStr =
86+
typeof error.error === "object"
87+
? JSON.stringify(error.error, null, 2).substring(0, 500) // Limit very long errors
88+
: String(error.error);
89+
summaryLog += ` Details: ${errorStr}\n`;
90+
}
91+
});
92+
}
93+
}
94+
95+
const logDir = path.resolve(__dirname, "../logs");
96+
if (!fs.existsSync(logDir)) {
97+
fs.mkdirSync(logDir, { recursive: true });
98+
}
99+
100+
const logPath = path.join(
101+
logDir,
102+
`${translationSummaryPrefix}-${timestamp}.log`
103+
);
104+
fs.writeFileSync(logPath, summaryLog);
105+
console.log(
106+
`Summary log saved to logs/translation-summary-${timestamp}.log`
107+
);
108+
} catch (logError) {
109+
console.error("Failed to save log file:", logError);
110+
}
111+
}

i18n/index.ts

Lines changed: 8 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import PathGenerator from "./controllers/path.ts";
22
import translate, { getFileErrors } from "./controllers/recurTranslate.ts";
3-
import fs, { Dirent } from "fs";
3+
import fs from "fs";
44
import util from "util";
55
import path from "path";
66
import { fileURLToPath } from "url";
77
import { 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
1214
const __filename = fileURLToPath(import.meta.url);
@@ -27,201 +29,14 @@ let failureCount = 0;
2729
let processedCount = 0;
2830
let 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

Comments
 (0)