Skip to content

Commit d0c95f7

Browse files
committed
Merge branch 'sicp_i18n' into i18n_gh_actions
2 parents d448d72 + cdf91c6 commit d0c95f7

File tree

14 files changed

+882
-848
lines changed

14 files changed

+882
-848
lines changed

i18n/config.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// maximum permissible concurrent translations
2+
export const max_trans_num = Number(process.env.MAX_TRANSLATION_NO) || 5;
3+
4+
// log file configs
5+
export const translationSummaryPrefix: string = "translation-summary";
6+
export const jsonSummaryPrefix: string = "json-summary";
7+
export const ignoredTags: string[] = [
8+
"LATEXINLINE",
9+
"LATEX",
10+
"SNIPPET",
11+
"SCHEMEINLINE",
12+
"SCHEME",
13+
"LONG_PAGE",
14+
"LABEL",
15+
"HISTORY",
16+
"REF",
17+
"FIGURE",
18+
];
19+
export const max_chunk_len: Number = Number(process.env.MAX_LEN) || 3000;

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 "./translator";
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+
}

0 commit comments

Comments
 (0)