Repository: linuxdeepin/deepin-auto-translation
Pull request #3: feat: automatic detection and processing of multilingual translation …
Author: login: JWWTSL
Created: 2025-05-08T08:32:31Z | Merged: 2025-05-14T06:21:17Z

…files

Added automatic detection and processing of .ts translation files in the latest Git repository commits. Key improvements include:

1. Added check-ts-files.ts module for intelligent identification and classification of translation files
2. Refactored index.ts to implement a complete translation workflow
3. Implemented differentiated processing strategies for various translation file types:
   - AI model translation for general language files
   - Rule-based processing for Traditional Chinese files
   - Identification and skipping of minor language files
4. Simulate the CI process of reading a Pr by reading a git commit. 5.The response content returned by the beanbag API contains Markdown
 code block markup (```json''), which causes JSON.parse() to fail
  to parse correctly.
6.Added big model tip words (constraints related to laws and regulations
 to avoid risky content)
7.Changed secrets.sample.ts to secrets.sample.ts.

## Summary by Sourcery

Implement an advanced multilingual translation workflow for TypeScript translation files with intelligent file detection, processing, and Git integration

New Features:
- Automatic detection of translation files in Git repository commits
- Intelligent classification of translation files
- Differentiated processing strategies for various translation file types
- Support for Traditional Chinese file conversion
- Integration with Transifex and translation services

Bug Fixes:
- Fixed JSON parsing issues with Markdown code block responses
- Improved handling of translation file naming conventions

Enhancements:
- Improved translation batch processing with rate limiting
- Added safety constraints for AI translation
- Enhanced error handling and logging
- Flexible translation service selection

Chores:
- Refactored translation workflow in index.ts
- Added new utility module for translation file checking
- Updated sample configuration files


Diff:
diff --git a/check-ts-files.ts b/check-ts-files.ts
new file mode 100644
index 0000000..5f33d2d
--- /dev/null
+++ b/check-ts-files.ts
@@ -0,0 +1,502 @@
+// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import fs from 'fs';
+import * as YAML from 'js-yaml';
+import { execSync } from 'child_process';
+import * as Transifex from './transifex';
+import { createTsFileFromTemplate } from './qtlinguist';
+
+// 定义小语种列表
+const MINOR_LANGUAGES = {
+    'es': '西班牙语',
+    'it': '意大利语',
+    'de': '德语',
+    'de_DE': '德语',
+    'ja': '日语',
+    'uk': '乌克兰语',
+    'pt_BR': '巴西葡萄牙语',
+    'sq': '阿尔巴尼亚语'
+};
+
+/**
+ * 从 YAML 文件加载所有项目资源
+ */
+async function loadAllResources() {
+    return await Transifex.getAllLinkedResourcesFromProjects(
+        YAML.load(fs.readFileSync('./transifex-projects.yml', 'utf8')) as string[]
+    );
+}
+
+/**
+ * 从 Transifex 同步翻译文件
+ * 
+ * 功能说明：
+ * 1. 检查仓库中是否有.tx/config文件
+ * 2. 如果存在.tx/config文件，则使用transifex-cli拉取所有翻译
+ * 3. 如果仓库没有.tx/config文件，则输出日志提示
+ */
+function syncTranslationsFromTransifex(repository: string, repoPath: string) {
+    console.log(`正在从Transifex平台同步${repository}仓库的翻译文件...`);
+    try {
+        // 检查仓库中是否有.tx/config文件
+        const txConfigPath = `${repoPath}/.tx/config`;
+        
+        // tx pull命令只支持使用.tx/config配置文件
+        if (fs.existsSync(txConfigPath)) {
+            // 使用transifex-cli拉取所有翻译
+            console.log(`使用tx命令从Transifex拉取${repository}的翻译文件...`);
+            Transifex.downloadTranslationFilesViaCli(repoPath);
+            console.log(`成功从Transifex同步${repository}的翻译文件`);
+        } else {
+            console.log(`仓库${repository}没有.tx/config配置文件，无法从Transifex同步翻译`);
+        }
+    } catch (error) {
+        console.error(`从Transifex同步${repository}翻译文件时出错:`, error);
+        console.log(`请在实际启用此脚本前人工检查${repository}仓库的.tx/config配置是否正确`);
+    }
+}
+
+/**
+ * 处理ts文件，根据语种类型添加到对应列表
+ */
+function processTsFile(
+    tsFile: string, 
+    resource: any, 
+    repoPath: string, 
+    processedFiles: Set<string>,
+    filesToTranslate: { file: string; langCode: string; resource: any; repoPath: string; isTraditionalChinese?: boolean }[]
+): boolean {
+    // 如果文件已经处理过，跳过
+    if (processedFiles.has(tsFile)) {
+        return false;
+    }
+    processedFiles.add(tsFile);
+    
+    // 从文件名中提取语种代码，支持带路径的文件名
+    const match = tsFile.match(/[^/]+_([a-z]{2}(?:_[A-Z]{2})?).ts$/);
+    if (!match) {
+        console.log(`  - ${tsFile} (不符合命名规范，跳过)`);
+        return false;
+    }
+    
+    const langCode = match[1];
+
+    // 检查是否为繁体中文
+    if (['zh_HK', 'zh_TW'].includes(langCode)) {
+        console.log(`  - ${tsFile} (当前文件为简繁体转换文件，采用规则库匹配方式处理)`);
+        filesToTranslate.push({
+            file: tsFile,
+            langCode,
+            resource,
+            repoPath,
+            isTraditionalChinese: true
+        });
+        return true;
+    }
+
+    // 检查是否为小语种
+    if (langCode in MINOR_LANGUAGES) {
+        console.log(`  - ${tsFile} (${MINOR_LANGUAGES[langCode]}小语种，跳过不由脚本处理)`);
+        return true;
+    }
+
+    // 其他语种添加到待翻译列表
+    console.log(`  - ${tsFile} (需要基于AI大模型进行翻译)`);
+    filesToTranslate.push({
+        file: tsFile,
+        langCode,
+        resource,
+        repoPath
+    });
+    return true;
+}
+
+/**
+ * 检查文件内容是否包含未翻译内容
+ */
+function hasUnfinishedTranslations(fileContent: string): boolean {
+    return fileContent.includes('<translation type="unfinished"/>') || 
+           fileContent.includes('<translation type="unfinished"></translation>') ||
+           fileContent.includes('<translation type="unfinished">') ||
+           fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
+}
+
+/**
+ * 检查所有仓库最新提交中是否包含 TypeScript 文件
+ * 
+ * 功能说明：
+ * 1. 从 transifex-projects.yml 文件中读取所有项目信息
+ * 2. 遍历每个项目，检查其本地仓库是否存在
+ * 3. 对每个存在的仓库，检查其最新提交中是否包含 .ts 文件
+ * 4. 对每个找到的 ts 文件进行分析：
+ *    - 如果是繁体中文(zh_HK, zh_TW)，标记为 A 类
+ *    - 如果是小语种(es, it, de等)，显示语种信息并跳过
+ *    - 其他语种则返回给调用者处理
+ * 
+ * 注意事项：
+ * 1. 需要确保 transifex-projects.yml 文件存在且格式正确
+ * 2. 需要确保所有仓库都已克隆到 repo/ 目录下
+ * 3. 需要确保有权限访问这些仓库
+ * 4. 需要确保系统已安装 git 命令
+ * 5. 目前的逻辑只对"最新一次 commit 中的 ts 文件"生效。
+ */
+export async function checkTsFilesInGitLog() {
+    const filesToTranslate: { file: string; langCode: string; resource: any; repoPath: string; isTraditionalChinese?: boolean }[] = [];
+    let totalFilesFound = 0;
+    const processedFiles = new Set<string>(); // 用于跟踪已处理的文件
+    
+    try {
+        // 从 YAML 文件中读取所有项目信息
+        const allResources = await loadAllResources();
+
+        // 遍历每个项目
+        for (const resource of allResources) {
+            const { repository } = resource;
+            const repoPath = `repo/${repository}`;
+
+            // 检查仓库是否存在
+            if (!fs.existsSync(repoPath)) {
+                console.log(`仓库 ${repository} 不存在，跳过检查`);
+                continue;
+            }
+
+            // 使用 git log 命令检查最新提交中的 ts 文件
+            const command = `cd ${repoPath} && git log -1 --name-only --pretty=format: | grep -E "\\.ts$"`;
+            try {
+                const output = execSync(command, { encoding: 'utf8' });
+                
+                if (output.trim()) {
+                    const tsFiles = output.trim().split('\n');
+                    
+                    // 如果找到ts文件，先从Transifex同步翻译
+                    syncTranslationsFromTransifex(repository, repoPath);
+                    
+                    for (const tsFile of tsFiles) {
+                        if (processTsFile(tsFile, resource, repoPath, processedFiles, filesToTranslate)) {
+                            totalFilesFound++;
+                        }
+                    }
+                }
+            } catch (error) {
+                if ((error as any).status === 1) {
+                    // git grep 没有找到匹配项，这是正常情况
+                    continue;
+                } else {
+                    console.error(`检查仓库 ${repository} 时出错:`, error);
+                }
+            }
+        }
+
+        if (totalFilesFound > 0) {
+            console.log(`\n共找到 ${totalFilesFound} 个 ts 文件，其中 ${filesToTranslate.length} 个需要翻译`);
+        } else {
+            console.log('\n没有找到任何 ts 文件');
+        }
+    } catch (error) {
+        console.error('检查过程中出错:', error);
+    }
+
+    return filesToTranslate;
+} 
+
+/**
+ * 检查所有仓库最新提交中是否包含 _en.ts 文件，且提交标题包含"transfix"
+ * 
+ * 功能说明：
+ * 1. 从 transifex-projects.yml 文件中读取所有项目信息
+ * 2. 遍历每个项目，检查其本地仓库是否存在
+ * 3. 对每个存在的仓库，检查其最新提交中是否包含 _en.ts 文件，且提交标题包含"transfix"
+ * 4.  同时，从Transifex平台拉取最新的翻译更新，以确保GitHub和Transifex保持同步
+ * 5. 如果找到符合条件的提交，根据脚本根目录的language.yml中的语种列表，在translations目录下查找对应的ts文件
+ * 6. 对每个找到的ts文件判断：若语种不在language.yml中，则输出日志跳过；若在language.yml中，则进行后续处理：
+ *    - 如果是繁体中文(zh_HK, zh_TW)，标记为需要规则库匹配处理
+ *    - 如果是小语种(es, it, de等)，显示语种信息并跳过
+ *    - 其他语种则返回给调用者处理，用于AI大模型翻译
+ * 7. 如果language.yml中有新增的语言代码但还没有对应的ts文件，则自动基于英文文件创建新的ts文件
+ */
+export async function checkEnTsFilesWithTransfix() {
+    const filesToTranslate: { file: string; langCode: string; resource: any; repoPath: string; isTraditionalChinese?: boolean }[] = [];
+    let totalFilesFound = 0;
+    const processedFiles = new Set<string>(); // 用于跟踪已处理的文件
+    let foundTransfixCommit = false; // 标记是否找到包含"transfix"的提交
+    
+    try {
+        // 读取脚本根目录下的language.yml文件获取语种列表
+        const languageYmlPath = './language.yml';
+        if (!fs.existsSync(languageYmlPath)) {
+            console.log(`脚本根目录不存在language.yml文件，退出检查`);
+            return { filesToTranslate, foundTransfixCommit, totalFilesFound };
+        }
+        
+        const languageCodes = fs.readFileSync(languageYmlPath, 'utf8')
+            .split('\n')
+            .filter(line => line.trim().length > 0)
+            .map(line => line.trim()); // 修复：移除每行可能存在的空格
+            
+        console.log(`从脚本根目录的language.yml中读取到${languageCodes.length}种语言`);
+        
+        // 从 YAML 文件中读取所有项目信息
+        const allResources = await loadAllResources();
+
+        console.log(`从transifex-projects.yml中读取到${allResources.length}个项目`);
+
+        // 遍历每个项目
+        for (const resource of allResources) {
+            const { repository } = resource;
+            const repoPath = `repo/${repository}`;
+
+            // 检查仓库是否存在
+            if (!fs.existsSync(repoPath)) {
+                console.log(`仓库 ${repository} 不存在，跳过检查`);
+                continue;
+            }
+
+            console.log(`检查仓库 ${repository}`);
+
+            // 首先检查最新提交是否包含"transfix"
+            try {
+                let commitTitle;
+                try {
+                    commitTitle = execSync(`cd ${repoPath} && git log -1 --pretty=format:"%s"`, { encoding: 'utf8' });
+                } catch (error) {
+                    console.error(`获取仓库 ${repository} 提交信息时出错:`, error);
+                    continue;
+                }
+                
+                if (!commitTitle.toLowerCase().includes('transfix')) {
+                    console.log(`仓库 ${repository} 的最新提交不包含"transfix"，跳过检查`);
+                    continue;
+                }
+                
+                foundTransfixCommit = true; // 找到包含"transfix"的提交
+                console.log(`仓库 ${repository} 的最新提交包含"transfix"，标题: ${commitTitle}`);
+                
+                // 检查最新提交中是否包含 _en.ts 或 _en_US.ts 文件
+                let enTsFiles;
+                try {
+                    enTsFiles = execSync(
+                        `cd ${repoPath} && git log -1 --name-only --pretty=format: | grep -E "_(en|en_US)\\.ts$"`, 
+                        { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
+                    ).toString().trim();
+                } catch (error) {
+                    if ((error as any).status === 1) {
+                        // grep 没有找到匹配项，这是正常情况
+                        console.log(`仓库 ${repository} 的最新提交不包含_en.ts或_en_US.ts文件，跳过检查`);
+                        continue;
+                    } else {
+                        console.error(`检查仓库 ${repository} 的英文翻译文件时出错:`, error);
+                        continue;
+                    }
+                }
+                
+                if (!enTsFiles) {
+                    console.log(`仓库 ${repository} 的最新提交不包含_en.ts或_en_US.ts文件，跳过检查`);
+                    continue;
+                }
+                
+                foundTransfixCommit = true; // 找到包含"transfix"的提交且包含_en.ts文件
+                const enFiles = enTsFiles.split('\n');
+                console.log(`找到 ${enFiles.length} 个_en.ts文件`);
+                
+                // 在确认有符合条件的提交后，从Transifex平台同步更新翻译文件
+                syncTranslationsFromTransifex(repository, repoPath);
+                
+                // 打印所有language.yml中的语言代码，方便调试
+                console.log(`语言代码列表: [${languageCodes.join(', ')}]`);
+                
+                // 对每个找到的_en.ts文件，查找translations目录下的对应翻译文件
+                for (const enFile of enFiles) {
+                    // 提取基本文件名和目录
+                    const isEnUS = enFile.includes('_en_US.ts');
+                    const baseName = isEnUS ? enFile.replace('_en_US.ts', '') : enFile.replace('_en.ts', '');
+                    const baseDir = enFile.includes('/') ? enFile.substring(0, enFile.lastIndexOf('/') + 1) : '';
+                    
+                    // 记录当前处理的英文文件类型
+                    const enFileType = isEnUS ? '_en_US.ts' : '_en.ts';
+                    console.log(`处理${enFileType}文件: ${enFile}`);
+                    
+                    // 检查英文文件所在的目录结构，判断translations在哪里
+                    let translationsDir;
+                    
+                    // 如果英文文件路径中已包含translations，使用相同的目录结构
+                    if (enFile.includes('/translations/')) {
+                        // 如果英文文件在translations目录中，直接使用该目录
+                        translationsDir = `${repoPath}/${baseDir}`;
+                    } else {
+                        // 否则检查translations子目录
+                        translationsDir = `${repoPath}/${baseDir}translations`;
+                        
+                        // 如果translations子目录不存在，尝试查找英文文件所在目录
+                        if (!fs.existsSync(translationsDir)) {
+                            translationsDir = `${repoPath}/${baseDir}`;
+                        }
+                    }
+                    
+                    console.log(`查找翻译文件的目录: ${translationsDir}`);
+                    
+                    // 根据language.yml中的语种列表查找对应的翻译文件
+                    for (const langCode of languageCodes) {
+                        if (langCode === 'en' || langCode === 'en_US') continue; // 跳过英文
+                        
+                        // 根据英文文件的路径模式构建翻译文件路径
+                        const langFileName = isEnUS ? 
+                            enFile.replace('_en_US.ts', `_${langCode}.ts`) : 
+                            enFile.replace('_en.ts', `_${langCode}.ts`);
+                        const fullPath = `${repoPath}/${langFileName}`;
+                        
+                        // 尝试直接查找文件，如果文件不存在也尝试移除语言代码中可能的空格
+                        let fileExists = fs.existsSync(fullPath);
+                        let actualLangCode = langCode;
+                        let actualFullPath = fullPath;
+                        
+                        // 如果文件不存在，尝试检查是否是因为语言代码中有空格导致的
+                        if (!fileExists && langCode.includes(' ')) {
+                            const cleanLangCode = langCode.replace(/\s+/g, '');
+                            const cleanPath = enFile.replace('_en.ts', `_${cleanLangCode}.ts`);
+                            const cleanFullPath = `${repoPath}/${cleanPath}`;
+                            
+                            if (fs.existsSync(cleanFullPath)) {
+                                console.log(`  - 语言代码 "${langCode}" 包含空格，找到清理后的文件: ${cleanFullPath}`);
+                                fileExists = true;
+                                actualLangCode = cleanLangCode;
+                                actualFullPath = cleanFullPath;
+                            }
+                        }
+                        
+                        // 如果文件不存在，需要基于英文文件创建新的ts文件
+                        if (!fileExists) {
+                            console.log(`  - 未找到语种 ${langCode} 的翻译文件，正在基于英文文件创建...`);
+                            
+                            // 英文模板文件的完整路径
+                            const enFullPath = `${repoPath}/${enFile}`;
+                            console.log(`  - 使用模板文件: ${enFile} (${isEnUS ? 'en_US' : 'en'})`);
+                            
+                            // 创建新的ts文件
+                            const newFilePath = createTsFileFromTemplate(actualLangCode, enFullPath);
+                            
+                            if (newFilePath) {
+                                console.log(`  - 成功创建新的翻译文件: ${newFilePath}`);
+                                fileExists = true;
+                                actualFullPath = newFilePath;
+                                
+                                // 添加到待翻译列表，新创建的文件必然需要翻译
+                                totalFilesFound++;
+                                
+                                // 检查是否为繁体中文
+                                if (['zh_HK', 'zh_TW'].includes(actualLangCode)) {
+                                    console.log(`  - ${langFileName} (当前文件为简繁体转换文件，采用规则库匹配方式处理)`);
+                                    filesToTranslate.push({
+                                        file: langFileName,
+                                        langCode: actualLangCode,
+                                        resource,
+                                        repoPath,
+                                        isTraditionalChinese: true
+                                    });
+                                    continue;
+                                }
+
+                                // 检查是否为小语种
+                                if (actualLangCode in MINOR_LANGUAGES) {
+                                    console.log(`  - ${langFileName} (${MINOR_LANGUAGES[actualLangCode]}小语种，跳过不由脚本处理)`);
+                                    continue;
+                                }
+
+                                // 其他语种添加到待翻译列表
+                                console.log(`  - ${langFileName} (新创建文件，需要基于AI大模型进行翻译)`);
+                                filesToTranslate.push({
+                                    file: langFileName,
+                                    langCode: actualLangCode,
+                                    resource,
+                                    repoPath
+                                });
+                                continue;
+                            } else {
+                                console.error(`  - 创建语种 ${langCode} 的翻译文件失败`);
+                            }
+                        }
+                        
+                        // 检查文件是否存在
+                        if (fileExists) {
+                            // 检查是否包含未翻译内容
+                            const fileContent = fs.readFileSync(actualFullPath, 'utf8');
+                            const unfinishedTranslations = hasUnfinishedTranslations(fileContent);
+                            
+                            if (!unfinishedTranslations) {
+                                console.log(`  - ${langFileName} (没有未翻译内容，跳过)`);
+                                continue;
+                            }
+                            
+                            totalFilesFound++;
+                            
+                            // 如果文件已经处理过，跳过
+                            if (processedFiles.has(langFileName)) {
+                                continue;
+                            }
+                            processedFiles.add(langFileName);
+                            
+                            // 检查是否为繁体中文
+                            if (['zh_HK', 'zh_TW'].includes(actualLangCode.trim())) {
+                                console.log(`  - ${langFileName} (当前文件为简繁体转换文件，采用规则库匹配方式处理)`);
+                                filesToTranslate.push({
+                                    file: langFileName,
+                                    langCode: actualLangCode.trim(),
+                                    resource,
+                                    repoPath,
+                                    isTraditionalChinese: true
+                                });
+                                continue;
+                            }
+
+                            // 检查是否为小语种
+                            if (actualLangCode.trim() in MINOR_LANGUAGES) {
+                                console.log(`  - ${langFileName} (${MINOR_LANGUAGES[actualLangCode.trim()]}小语种，跳过不由脚本处理)`);
+                                continue;
+                            }
+
+                            // 其他语种添加到待翻译列表
+                            console.log(`  - ${langFileName} (需要基于AI大模型进行翻译)`);
+                            filesToTranslate.push({
+                                file: langFileName,
+                                langCode: actualLangCode.trim(),
+                                resource,
+                                repoPath
+                            });
+                        } else {
+                            console.log(`  - 未找到语种 ${actualLangCode} 的翻译文件: ${actualFullPath}`);
+                        }
+                    }
+                }
+            } catch (error) {
+                if ((error as any).status === 1) {
+                    // git grep 没有找到匹配项，这是正常情况
+                    console.log(`仓库 ${repository} 没有找到符合条件的文件`);
+                    continue;
+                } else {
+                    console.error(`检查仓库 ${repository} 时出错:`, error);
+                }
+            }
+        }
+
+        if (totalFilesFound > 0) {
+            // 统计需要繁体翻译的文件数量
+            const traditionalFilesCount = filesToTranslate.filter(item => 
+                ['zh_HK', 'zh_TW'].includes(item.langCode) || item.isTraditionalChinese).length;
+            
+            // 计算小语种文件数量(不需要处理的文件)
+            const skipFilesCount = totalFilesFound - filesToTranslate.length - traditionalFilesCount;
+            
+            console.log(`\n共找到 ${totalFilesFound} 个需要处理的翻译文件，其中：`);
+            console.log(`  - ${filesToTranslate.length - traditionalFilesCount} 个需要AI翻译`);
+            console.log(`  - ${traditionalFilesCount} 个需要繁体中文转换处理`);
+            console.log(`  - ${skipFilesCount} 个是小语种文件，跳过不处理`);
+        } else {
+            console.log('\n没有找到任何需要处理的翻译文件');
+        }
+    } catch (error) {
+        console.error('检查过程中出错:', error);
+    }
+
+    // 返回包含两个元素的数组：需要翻译的文件列表和是否找到了包含"transfix"的提交
+    return { filesToTranslate, foundTransfixCommit, totalFilesFound };
+} 
\ No newline at end of file
diff --git a/deepin-translation-utils b/deepin-translation-utils
new file mode 100755
index 0000000..619d12a
Binary files /dev/null and b/deepin-translation-utils differ
diff --git a/doubao.ts b/doubao.ts
index ce3255b..3ea89bf 100644
--- a/doubao.ts
+++ b/doubao.ts
@@ -45,7 +45,10 @@ export async function fetchTranslations(messages: MessageData[], targetLanguage:
     }).then(response => {
         // response as json array
         console.log(response.data.choices[0].message.content);
-        const responsedTranslations = JSON.parse(response.data.choices[0].message.content);
+        // 豆包 API 返回的响应内容中包含了 Markdown 代码块标记（```json），这导致 JSON.parse() 无法正确解析
+        // 在解析 JSON 之前，先移除响应内容中可能存在的 Markdown 代码块标记（```json 和 ```）。
+        const content = response.data.choices[0].message.content.replace(/```json\n?|\n?```/g, '').trim();
+        const responsedTranslations = JSON.parse(content);
         if (Array.isArray(responsedTranslations) && responsedTranslations.length === messages.length) {
             console.log(`Translated ${messages.length} strings`);
             for (let i = 0; i < messages.length; i++) {
diff --git a/index.ts b/index.ts
index 082cd2b..3180a61 100644
--- a/index.ts
+++ b/index.ts
@@ -1,7 +1,5 @@
 // SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
-//
 // SPDX-License-Identifier: GPL-3.0-or-later
-
 import fs from 'node:fs';
 import * as YAML from 'js-yaml';
 import * as QtLinguist from './qtlinguist';
@@ -9,45 +7,414 @@ import * as OpenAI from './openai';
 import * as Ollama from './ollama';
 import * as Transifex from './transifex';
 import * as GitRepo from './gitrepo';
+import * as Doubao from './doubao';
 import { MessageData, TransifexRepo, TransifexResource } from './types';
 import { exit } from 'node:process';
 import * as Translator from './translator';
+import { checkTsFilesInGitLog, checkEnTsFilesWithTransfix } from './check-ts-files';
+import { execSync } from 'child_process';
+import path from 'path';
+
+// 选择翻译服务
+const TRANSLATION_SERVICE = {
+    DOUBAO: Doubao.fetchTranslations,
+    OPENAI: OpenAI.fetchTranslations,
+//   OLLAMA: Ollama.fetchTranslations 这个还没验证过，暂时不使用
+};
+
+// 在这里选择要使用的翻译服务，同理管理使用的模型接口
+const selectedTranslationService = TRANSLATION_SERVICE.OPENAI;
+
+/*
+ 需要获取所有项目名后，再挑选需要的项目
+ const transifexProjects = await Transifex.getAllProjects('o:linuxdeepin');
+ // 过滤掉指定的项目
+ const filteredProjects = transifexProjects.filter(project => 
+   !['o:linuxdeepin:p:linyaps', 'o:linuxdeepin:p:other-products', 'o:linuxdeepin:p:scan-assistant'].includes(project)
+ );
+ fs.writeFileSync('./transifex-projects.yml', YAML.dump(filteredProjects));
+*/
+
+const allResources = await Transifex.getAllLinkedResourcesFromProjects(YAML.load(fs.readFileSync('./transifex-projects.yml', 'utf8')) as string[]);
+
+// 记录简体中文文件路径，用于后续处理繁体中文
+const zhCNFilePaths = new Map<string, string>();
+
+// 直接调用Translator进行翻译，跳过Transifex上传操作
+async function translateTsFile(filePath: string, langCode: string): Promise<boolean> {
+    try {
+        console.log(`直接翻译文件: ${filePath} (${langCode})`);
+        
+        // 检查文件是否存在
+        if (!fs.existsSync(filePath)) {
+            console.error(`文件不存在: ${filePath}`);
+            return false;
+        }
+        
+        // 读取文件内容
+        const fileContent = fs.readFileSync(filePath, 'utf8');
+        
+        // 检查文件中是否包含未翻译内容
+        const hasUnfinishedTranslations = fileContent.includes('<translation type="unfinished"/>') || 
+                                          fileContent.includes('<translation type="unfinished"></translation>') ||
+                                          fileContent.includes('<translation type="unfinished">') ||
+                                          fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
+        
+        if (!hasUnfinishedTranslations) {
+            console.log(`文件 ${filePath} 没有未翻译内容，跳过处理`);
+            return false;
+        }
+        
+        // 使用Translator提取并翻译内容
+        console.log(`开始处理文件: ${filePath}`);
+        const translatedCount = await Translator.translateLinguistTsFile(
+            selectedTranslationService,
+            filePath,
+            langCode,
+            false
+        );
+        
+        console.log(`文件 ${filePath} 翻译完成，翻译了 ${translatedCount} 个字符串`);
+        return translatedCount > 0;
+    } catch (error) {
+        console.error(`翻译文件 ${filePath} 时出错:`, error);
+        return false;
+    }
+}
+
+// 检查最新的 git log 中的 ts 文件并进行翻译
+async function main() {
+    console.log('开始检查并处理翻译文件...');
+    
+    // 首先尝试检查包含"transfix"的提交中的_en.ts文件
+    console.log('尝试检查包含"transfix"的提交中的_en.ts文件...');
+    const { filesToTranslate: transfixFiles, foundTransfixCommit, totalFilesFound } = await checkEnTsFilesWithTransfix();
+
+    // 修改判断逻辑：只有当没有找到包含"transfix"的提交时，才使用常规方法
+    let filesToTranslate = transfixFiles;
+    if (!foundTransfixCommit) {
+        console.log('没有找到包含"transfix"的提交中的_en.ts文件，尝试使用常规方法检查...');
+        filesToTranslate = await checkTsFilesInGitLog();
+    } else {
+        console.log(`开始处理翻译文件...`);
+    }
+
+    if (filesToTranslate.length === 0) {
+        console.log('没有需要翻译的文件');
+        return;
+    }
+
+    console.log(`\n开始处理 ${filesToTranslate.length} 个需要翻译的文件`);
+    
+    // 记录成功翻译的文件和它们的仓库路径
+    const translatedFiles = new Map<string, Set<string>>();
+    
+    // 将文件分为两类：繁体中文和非繁体中文
+    const traditionalFiles = filesToTranslate.filter(item => 
+        ['zh_HK', 'zh_TW'].includes(item.langCode) || item.isTraditionalChinese);
+    const nonTraditionalFiles = filesToTranslate.filter(item => 
+        !(['zh_HK', 'zh_TW'].includes(item.langCode) || item.isTraditionalChinese));
+    
+    // 创建用于记录繁体中文处理信息的数据结构
+    const tcFileMap = new Map<string, Map<string, string>>();
+    
+    // 记录需要上传到Transifex的文件
+    const transifexFilesToUpload: { file: string; language: string; resource: TransifexResource }[] = [];
+    
+    // 第一步：处理所有非繁体中文文件（包括简体中文和其他语言）
+    console.log('\n===== 步骤1：处理非繁体中文文件 =====');
+    
+    // 串行处理每个翻译任务
+    console.log(`开始串行处理 ${nonTraditionalFiles.length} 个翻译文件...`);
+    let successfullyTranslatedCount = 0;
+    
+    for (const { file, langCode, resource, repoPath } of nonTraditionalFiles) {
+        if (!repoPath) {
+            console.warn(`警告: 文件 ${file} 没有关联的仓库路径，跳过处理`);
+            continue;
+        }
+        
+        const fullPath = `${repoPath}/${file}`;
+        console.log(`\n[${successfullyTranslatedCount+1}/${nonTraditionalFiles.length}] 开始翻译 ${file} (${langCode}) (仓库路径: ${repoPath})`);
+        
+        try {
+            // 直接翻译文件，不上传到Transifex
+            const translated = await translateTsFile(fullPath, langCode);
+            
+            if (translated) {
+                successfullyTranslatedCount++;
+                
+                // 如果是简体中文文件，记录路径用于后续处理繁体中文
+                if (langCode === 'zh_CN') {
+                    const baseFileName = path.basename(file).replace(/_zh_CN\.ts$/, '');
+                    zhCNFilePaths.set(baseFileName, fullPath);
+                }
+                
+                // 记录成功翻译的文件
+                if (!translatedFiles.has(repoPath)) {
+                    translatedFiles.set(repoPath, new Set());
+                }
+                translatedFiles.get(repoPath)?.add(file);
+                
+                // 添加到待上传列表
+                transifexFilesToUpload.push({
+                    file: fullPath,
+                    language: langCode,
+                    resource
+                });
+                
+                console.log(`翻译完成: ${file} (${langCode})`);
+            } else {
+                console.log(`文件 ${file} 无需翻译或翻译失败`);
+            }
+        } catch (error) {
+            console.error(`处理 ${file} (${langCode}) 时出错:`, error);
+        }
+    }
+    
+    console.log(`\n所有 ${nonTraditionalFiles.length} 个非繁体中文文件处理完成，成功翻译: ${successfullyTranslatedCount} 个`);
+    
+    // 第二步：收集并处理繁体中文文件
+    console.log('\n===== 步骤2：处理繁体中文文件 =====');
+    
+    // 收集繁体中文文件信息
+    for (const { file, langCode, resource, repoPath } of traditionalFiles) {
+        if (!repoPath) continue;
+        
+        console.log(`\n收集繁体中文文件信息: ${file} (${langCode})`);
+        const baseFileName = path.basename(file).replace(/_([a-z]{2}(?:_[A-Z]{2})?)\.ts$/, '');
+        
+        // 检查对应的简体中文文件是否存在
+        // 如果没有在之前的处理中找到，尝试从文件系统中查找
+        if (!zhCNFilePaths.has(baseFileName)) {
+            const possibleZhCNPath = `${repoPath}/${file.replace(/_zh_[A-Z]{2}\.ts$/, '_zh_CN.ts')}`;
+            if (fs.existsSync(possibleZhCNPath)) {
+                console.log(`找到对应的简体中文文件: ${possibleZhCNPath}`);
+                zhCNFilePaths.set(baseFileName, possibleZhCNPath);
+            } else {
+                // 再尝试在translations目录下查找
+                const altPath = `${repoPath}/translations/${baseFileName}_zh_CN.ts`;
+                if (fs.existsSync(altPath)) {
+                    console.log(`找到对应的简体中文文件: ${altPath}`);
+                    zhCNFilePaths.set(baseFileName, altPath);
+                } else {
+                    console.warn(`未找到与 ${baseFileName} 对应的简体中文文件，无法处理繁体中文`);
+                    continue;
+                }
+            }
+        }
+        
+        // 记录到繁体中文处理映射中
+        if (!tcFileMap.has(repoPath)) {
+            tcFileMap.set(repoPath, new Map());
+        }
+        tcFileMap.get(repoPath)?.set(baseFileName, langCode);
+    }
+    
+    // 使用 deepin-translation-utils 处理繁体中文文件
+    if (tcFileMap.size > 0) {
+        console.log('\n开始使用 deepin-translation-utils 处理繁体中文文件...');
+        const tcFilesResult = await processTraditionalChineseFiles(tcFileMap, translatedFiles);
+        
+        // 添加繁体中文文件到待上传列表
+        for (const { filePath, langCode, resource } of tcFilesResult) {
+            transifexFilesToUpload.push({
+                file: filePath,
+                language: langCode,
+                resource
+            });
+        }
+    } else {
+        console.log('\n没有需要处理的繁体中文文件');
+    }
+    
+    // 第三步：统一上传所有文件到Transifex
+    if (transifexFilesToUpload.length > 0) {
+        console.log(`\n===== 步骤3：上传 ${transifexFilesToUpload.length} 个翻译文件到Transifex =====`);
+        
+        for (let i = 0; i < transifexFilesToUpload.length; i++) {
+            const { file, language, resource } = transifexFilesToUpload[i];
+            console.log(`\n[${i+1}/${transifexFilesToUpload.length}] 上传文件到Transifex: ${file} (${language})`);
+            
+            try {
+                // 使用Transifex模块的uploadTranslation方法上传
+                await Transifex.uploadTranslatedFileToTransifex(language, file, resource.transifexResourceId);
+                console.log(`文件 ${file} 上传成功`);
+            } catch (error) {
+                console.error(`上传文件 ${file} 到Transifex时出错:`, error);
+            }
+        }
+    }
+    
+    // 将所有翻译好的文件提交到 Git 仓库
+    if (translatedFiles.size > 0) {
+        console.log(`\n===== 步骤4：将 ${Array.from(translatedFiles.values()).reduce((acc, files) => acc + files.size, 0)} 个翻译文件提交到Git =====`);
+        commitTranslatedFiles(translatedFiles);
+    }
+}
+
+/**
+ * 使用 deepin-translation-utils 处理繁体中文文件
+ * @param tcFileMap 繁体中文文件映射：仓库路径 -> (基本文件名 -> 语言代码)
+ * @param translatedFiles 记录翻译文件的Map
+ * @returns 处理成功的繁体中文文件信息数组
+ */
+async function processTraditionalChineseFiles(
+    tcFileMap: Map<string, Map<string, string>>,
+    translatedFiles: Map<string, Set<string>>
+): Promise<{ filePath: string; langCode: string; resource: any }[]> {
+    const processedFiles: { filePath: string; langCode: string; resource: any }[] = [];
+    
+    for (const [repoPath, fileMap] of tcFileMap.entries()) {
+        for (const [baseFileName, langCode] of fileMap.entries()) {
+            // 检查对应的简体中文文件是否存在
+            const zhCNFilePath = zhCNFilePaths.get(baseFileName);
+            
+            if (!zhCNFilePath) {
+                console.warn(`警告: 未找到与 ${baseFileName} 对应的简体中文文件，无法处理繁体中文`);
+                continue;
+            }
+            
+            // 检查繁体中文文件是否存在未翻译内容
+            const targetFilePath = zhCNFilePath.replace('_zh_CN.ts', `_${langCode}.ts`);
+            
+            // 确认文件存在
+            if (!fs.existsSync(targetFilePath)) {
+                console.warn(`警告: 繁体中文文件 ${targetFilePath} 不存在，无法检查翻译状态`);
+                continue;
+            }
+            
+            // 读取文件内容并检查是否有未翻译内容
+            try {
+                const fileContent = fs.readFileSync(targetFilePath, 'utf8');
+                
+                // 检查文件中是否包含 <translation type="unfinished"/> 标签
+                const hasUnfinishedTranslations = fileContent.includes('<translation type="unfinished"/>') || 
+                                                  fileContent.includes('<translation type="unfinished"></translation>') ||
+                                                  fileContent.includes('<translation type="unfinished">') ||
+                                                  fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
+                
+                if (!hasUnfinishedTranslations) {
+                    console.log(`繁体中文文件 ${targetFilePath} 没有未翻译内容，跳过处理`);
+                    continue;
+                }
+                
+                console.log(`检测到繁体中文文件 ${targetFilePath} 有未翻译内容，开始处理...`);
+            } catch (error) {
+                console.error(`读取繁体中文文件 ${targetFilePath} 时出错:`, error);
+                continue;
+            }
+            
+            console.log(`处理繁体中文文件 (${langCode}): ${baseFileName}`);
+            try {
+                // 执行 deepin-translation-utils 命令，只处理特定的目标语言
+                const command = `./deepin-translation-utils zhconv --target-languages ${langCode} ${zhCNFilePath}`;
+                console.log(`执行命令: ${command}`);
+                try {
+                    execSync(command, { encoding: 'utf8', stdio: 'inherit' });
+                } catch (error) {
+                    console.error(`执行 deepin-translation-utils 命令失败 (${baseFileName}, ${langCode}):`, error);
+                    continue;
+                }
+                
+                // 记录生成的文件，以便后续进行 Git 提交
+                if (!translatedFiles.has(repoPath)) {
+                    translatedFiles.set(repoPath, new Set());
+                }
+                
+                // 提取相对路径而不仅仅是文件名
+                const repoPrefix = `${repoPath}/`;
+                const targetRelativePath = targetFilePath.startsWith(repoPrefix) 
+                    ? targetFilePath.substring(repoPrefix.length) 
+                    : targetFilePath;
+                
+                translatedFiles.get(repoPath)?.add(targetRelativePath);
+                
+                // 添加到处理成功的文件列表
+                // 查找匹配的resource
+                const resource = allResources.find(res => res.repository === repoPath.replace('repo/', ''));
+                if (resource) {
+                    processedFiles.push({
+                        filePath: targetFilePath,
+                        langCode,
+                        resource
+                    });
+                }
+                
+                console.log(`繁体中文文件处理完成: ${targetFilePath}`);
+            } catch (error) {
+                console.error(`处理繁体中文文件 ${baseFileName} (${langCode}) 时出错:`, error);
+            }
+        }
+    }
+    
+    return processedFiles;
+}
+
+/**
+ * 将翻译后的文件通过 git 添加到当前提交
+ * @param translatedFiles 翻译文件映射：仓库路径 -> 文件集合
+ */
+function commitTranslatedFiles(translatedFiles: Map<string, Set<string>>): void {
+    for (const [repoPath, files] of translatedFiles.entries()) {
+        try {
+            console.log(`\n处理仓库 ${repoPath} 中的翻译文件...`);
+            const fileList = Array.from(files).join('" "');
+            
+            // 执行 git add 和 git commit --amend
+            try {
+                execSync(`cd ${repoPath} && git add "${fileList}" && git commit --amend --no-edit`, { encoding: 'utf8' });
+                console.log(`成功将 ${files.size} 个文件添加到 ${repoPath} 的当前提交`);
+            } catch (error) {
+                console.error(`Git 命令执行失败 (${repoPath}): ${error}`);
+            }
+        } catch (error) {
+            console.error(`Git 操作失败 (${repoPath}): ${error}`);
+        }
+    }
+}
+
+// 执行主函数
+main().catch(error => {
+    console.error('程序执行出错:', error);
+    process.exit(1);
+});
 
 // You need to do the main auto-translate logic here.
 
 // The following one is just for demo purpose:
-await OpenAI.fetchTranslations([
-    {
-        translationElement: null,
-        context: "AppItemMenu",
-        source: "Move to Top",
-        comment: null
-    },
-    {
-        translationElement: null,
-        context: "BottomBar",
-        source: "Full-screen Mode",
-        comment: null
-    },
-    {
-        translationElement: null,
-        context: "DummyAppItemMenu",
-        source: "Install",
-        comment: null
-    },
-    {
-        translationElement: null,
-        context: "DummyAppItemMenu",
-        source: "Remove",
-        comment: null
-    },
-    {
-        translationElement: null,
-        context: "UninstallDialog",
-        source: "Are you sure you want to uninstall %1?",
-        comment: null
-    }
-], 'ar', true);
+// await OpenAI.fetchTranslations([
+//     {
+//         translationElement: null,
+//         context: "AppItemMenu",
+//         source: "Move to Top",
+//         comment: null
+//     },
+//     {
+//         translationElement: null,
+//         context: "BottomBar",
+//         source: "Full-screen Mode",
+//         comment: null
+//     },
+//     {
+//         translationElement: null,
+//         context: "DummyAppItemMenu",
+//         source: "Install",
+//         comment: null
+//     },
+//     {
+//         translationElement: null,
+//         context: "DummyAppItemMenu",
+//         source: "Remove",
+//         comment: null
+//     },
+//     {
+//         translationElement: null,
+//         context: "UninstallDialog",
+//         source: "Are you sure you want to uninstall %1?",
+//         comment: null
+//     }
+// ], 'ar', true);
 
 /*
 
@@ -93,4 +460,4 @@ Then you can use the following code to download resources, translate all resourc
 // Download, Translate, and Upload in a single step.
 Translator.translateTransifexRepos(OpenAI.fetchTranslations, repos)
 
-*/
+*/
\ No newline at end of file
diff --git a/language.yml b/language.yml
new file mode 100644
index 0000000..c505a11
--- /dev/null
+++ b/language.yml
@@ -0,0 +1,43 @@
+af
+am_ET
+ar
+ast
+az
+bg
+bn
+bo
+ca
+cs
+da
+de
+en
+en_US
+es
+et
+fi
+fr
+gl_ES
+he
+hr
+hu
+it
+ja
+lt
+ms
+nl
+pl
+pt_BR
+pt
+ro
+ru
+sk
+sl
+sq
+sr
+tr
+ug
+uk
+vi
+zh_CN
+zh_HK
+zh_TW 
\ No newline at end of file
diff --git a/openai.ts b/openai.ts
index 208f076..606e4b4 100644
--- a/openai.ts
+++ b/openai.ts
@@ -45,28 +45,121 @@ export async function fetchTranslations(messages: MessageData[], targetLanguage:
         // response as json array
         console.groupCollapsed("Translation status");
         console.log(response.data.choices[0].message.content);
-        const responsedTranslations = JSON.parse(response.data.choices[0].message.content);
-        if (Array.isArray(responsedTranslations) && responsedTranslations.length === messages.length) {
-            console.log(`Translated ${messages.length} strings`);
-            for (let i = 0; i < messages.length; i++) {
-                const translation = responsedTranslations[i];
-                let translationElement = messages[i].translationElement;
-                if (translationElement) {
-                    translationElement.textContent = translation.translation;
-                    // also check if we need to remove the type=unfinished attribute
-                    if (!keepUnfinishedTypeAttr && translationElement.getAttribute('type') === 'unfinished') {
-                        translationElement.removeAttribute('type');
+        // 对返回内容进行预处理，移除可能的Markdown代码块标记和清理内容
+        const content = response.data.choices[0].message.content.replace(/```json\n?|\n?```/g, '').trim();
+        try {
+            // 尝试修复JSON格式问题，处理可能的未终止字符串
+            let fixedContent = content;
+            // 检查是否是有效的JSON
+            if (!isValidJson(fixedContent)) {
+                console.log("尝试修复不完整的JSON...");
+                // 1. 查找缺少闭合引号的字符串
+                fixedContent = fixJsonString(fixedContent);
+                // 2. 如果仍然无效，尝试更简单的方式：截断内容到最后一个完整的JSON对象
+                if (!isValidJson(fixedContent)) {
+                    console.log("尝试截断到最后一个完整对象...");
+                    fixedContent = truncateToValidJson(fixedContent);
+                }
+                console.log("修复后的JSON:", fixedContent);
+            }
+            const responsedTranslations = JSON.parse(fixedContent);
+            if (Array.isArray(responsedTranslations) && responsedTranslations.length === messages.length) {
+                console.log(`Translated ${messages.length} strings`);
+                for (let i = 0; i < messages.length; i++) {
+                    const translation = responsedTranslations[i];
+                    let translationElement = messages[i].translationElement;
+                    if (translationElement) {
+                        translationElement.textContent = translation.translation;
+                        // also check if we need to remove the type=unfinished attribute
+                        if (!keepUnfinishedTypeAttr && translationElement.getAttribute('type') === 'unfinished') {
+                            translationElement.removeAttribute('type');
+                        }
                     }
                 }
+            } else {
+                console.log(Array.isArray(responsedTranslations), responsedTranslations.length, messages.length);
+                console.error(`Unexpected response from OpenAI endpoint: ${responsedTranslations}`);
             }
-        } else {
-            console.log(Array.isArray(responsedTranslations), responsedTranslations.length, messages.length);
-            console.error(`Unexpected response from OpenAI endpoint: ${responsedTranslations}`);
+            // also log token usage
+            console.log(response.data.usage);
+            console.groupEnd();
+        } catch (error) {
+            console.error("JSON解析错误:", error);
+            console.error("原始内容:", response.data.choices[0].message.content);
+            console.error("处理后内容:", content);
+            console.groupEnd();
         }
-        // also log token usage
-        console.log(response.data.usage);
-        console.groupEnd();
     }).catch(error => {
         console.error(error);
     });
+}
+
+// 检查字符串是否为有效的JSON
+function isValidJson(str) {
+    try {
+        JSON.parse(str);
+        return true;
+    } catch (e) {
+        return false;
+    }
+}
+
+// 尝试修复未终止的JSON字符串
+function fixJsonString(str) {
+    // 如果是数组开头但没有数组结尾，添加结尾
+    if (str.trim().startsWith('[') && !str.trim().endsWith(']')) {
+        str = str.trim() + ']';
+    }
+    
+    // 查找可能未闭合的对象
+    const matches = str.match(/{[^}]*$/g);
+    if (matches) {
+        str = str.replace(/{[^}]*$/g, '}');
+    }
+    
+    // 查找可能未闭合的引号
+    let inString = false;
+    let lastQuotePos = -1;
+    let needsClosingQuote = false;
+    
+    for (let i = 0; i < str.length; i++) {
+        if (str[i] === '"' && (i === 0 || str[i-1] !== '\\')) {
+            if (inString) {
+                inString = false;
+            } else {
+                inString = true;
+                lastQuotePos = i;
+            }
+        }
+    }
+    
+    // 如果字符串以未闭合的引号结束，添加闭合引号
+    if (inString) {
+        const beforeQuote = str.substring(0, lastQuotePos);
+        const afterQuote = str.substring(lastQuotePos);
+        // 限制翻译内容长度，避免过长的无效内容
+        const maxLength = 100; // 合理的翻译长度限制
+        const quotedContent = afterQuote.substring(1, Math.min(maxLength, afterQuote.length));
+        str = beforeQuote + '"' + quotedContent + '"' + (afterQuote.length > maxLength ? '}]' : '');
+    }
+    
+    return str;
+}
+
+// 截断内容到最后一个完整的JSON对象
+function truncateToValidJson(str) {
+    // 如果是数组格式，尝试保留开头和必要的部分
+    if (str.trim().startsWith('[')) {
+        // 尝试保留开头的[和第一个完整对象
+        const match = str.match(/\[\s*{[^{]*?}\s*(?:,|$)/);
+        if (match) {
+            return match[0].endsWith(',') ? match[0].slice(0, -1) + ']' : match[0] + ']';
+        }
+        
+        // 如果无法找到完整对象，但有开始标记，构建一个最小有效的JSON数组
+        return '[{"source":"Error parsing response","translation":"翻译解析错误"}]';
+    }
+    
+    // 无法修复，返回一个最小有效的JSON
+    return '[{"source":"Error parsing response","translation":"翻译解析错误"}]';
 }
\ No newline at end of file
diff --git a/prompt.ts b/prompt.ts
index a1e6791..0c8e1df 100644
--- a/prompt.ts
+++ b/prompt.ts
@@ -19,14 +19,22 @@ messages:
 你需要最终返回一个 Json，内容为一个数组，其内容为按原有 YAML 所提供的顺序依次每一项的原文以及对应的翻译文案。
 最终 Json 数组包含的译文数量与原始提供的待翻译原文数量等长。除此 Json 外无需附加任何额外描述。
 
-对于上述示例，返回的 Json 应为：
+【重要安全约束】
+1. 翻译内容必须严格遵守中国法律法规，不得包含任何违法违规内容
+2. 严禁出现任何涉及政治敏感、宗教敏感的内容
+3. 严禁出现任何危害国家安全、破坏民族团结的内容
+4. 严禁出现任何宣扬恐怖主义、极端主义的内容
+5. 严禁出现任何涉及色情、暴力、赌博等不良信息
+6. 翻译内容必须符合社会主义核心价值观
+7. 如果遇到可能涉及敏感内容的原文，必须使用安全、中性的替代翻译
+8. 所有翻译必须经过严格的内容安全审查，确保符合网络安全要求
+
+对于上述示例，返回的 Json 应为（注意：直接返回JSON，不要包含任何代码块标记如\`\`\`json或\`\`\`）：
 
-\`\`\`json
 [
     { "source": "Move to Top", "translation": "نقل إلى الأعلى" },
     { "source": "Full-screen Mode", "translation": "وضع الشاشة الكاملة" }
 ]
-\`\`\`
 
 另一个示例：
 
@@ -40,14 +48,12 @@ messages:
   source: "Full-screen Mode"
 \`\`\`
 
-则对于上述示例，返回的 Json 应为：
+则对于上述示例，返回的 Json 应为（注意：直接返回JSON，不要包含任何代码块标记）：
 
-\`\`\`json
 [
     { "source": "Move to Top", "translation": "Déplacer vers le haut" },
     { "source": "Full-screen Mode", "translation": "Mode plein écran" }
 ]
-\`\`\`
 `;
 
 const I18nResponseStructure = z.array(z.object({
diff --git a/qtlinguist.ts b/qtlinguist.ts
index 7cb3eb9..2ac98c2 100644
--- a/qtlinguist.ts
+++ b/qtlinguist.ts
@@ -3,11 +3,40 @@
 // SPDX-License-Identifier: GPL-3.0-or-later
 
 import { MessageData } from './types';
-import { Document } from '@xmldom/xmldom'
+import { Document } from '@xmldom/xmldom';
+const { execSync } = require('child_process');
+const fs = require('fs');
+const path = require('path');
 
-function createTsFileFromTemplate(targetLanguageCode: string, templateFilePath: string)
+export function createTsFileFromTemplate(targetLanguageCode: string, templateFilePath: string): string | null
 {
-    // /usr/lib/qt6/bin/lconvert -i ./dde-launchpad_ru.ts -o dde-launchpad_ar.ts -target-language ar -drop-translations
+    try {
+        // 构建目标文件路径
+        const templateDir = path.dirname(templateFilePath);
+        const templateFileName = path.basename(templateFilePath);
+        const targetFileName = templateFileName.replace(/_[a-z]{2}(?:_[A-Z]{2})?\.ts$/, `_${targetLanguageCode}.ts`);
+        const targetFilePath = path.join(templateDir, targetFileName);
+        
+        // 使用lconvert工具从模板创建目标语言文件
+        const command = `lconvert -i "${templateFilePath}" -o "${targetFilePath}" -target-language ${targetLanguageCode} -drop-translations`;
+        
+        console.log(`执行命令: ${command}`);
+        try {
+            execSync(command, { encoding: 'utf8' });
+        } catch (error) {
+            console.error(`执行lconvert命令失败:`, error);
+            return null;
+        }
+        
+        // 检查文件是否成功创建
+        if (fs.existsSync(targetFilePath)) {
+            return targetFilePath;
+        }
+        return null;
+    } catch (error) {
+        console.error('创建翻译文件模板失败:', error);
+        return null;
+    }
 }
 
 export function extractStringsFromDocument(doc: Document) : MessageData[]
diff --git a/secrets.sample.ts b/secrets.sample.ts
index a315867..64911a3 100644
--- a/secrets.sample.ts
+++ b/secrets.sample.ts
@@ -1,18 +1,17 @@
 // SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
 //
 // SPDX-License-Identifier: CC0-1.0
-
 export const doubao = {
-    model: 'ep-xxxxxxxxxxxxxxx',
-    accessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxx'
+    model: 'xxxxxxxxxxxxxxxxxxxxx',
+    accessKey: 'xxxxxxxxxxxxxxxxxxxxxxx '
 };
 
 export const openai = {
-    accessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxx'
+    accessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
 };
 
 export const transifex = {
-    accessKey: 'x/xxxxxxxxxxxxxxxxxxxxxxxxxx'
+    accessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
 }
 
 export default {doubao, openai, transifex};
\ No newline at end of file
diff --git a/transifex-projects.yml b/transifex-projects.yml
new file mode 100644
index 0000000..e904623
--- /dev/null
+++ b/transifex-projects.yml
@@ -0,0 +1 @@
+- o:JWWTSL:p:AITest
diff --git a/translator.ts b/translator.ts
index 86c87d4..780f07b 100644
--- a/translator.ts
+++ b/translator.ts
@@ -40,11 +40,13 @@ export async function translateLinguistTsFile(translator: TranslationOperation,
 
     let translationQueue = QtLinguist.extractStringsFromDocument(doc);
     console.log(`Extracted ${translationQueue.length} untranslated strings from file: ${inputFilePath}`)
-    // split translationQueue into batches, each batch contains 25 messages
-    const batchSize = 25;
+    // split translationQueue into batches, each batch contains 10 messages
+    const batchSize = 10;
     for (let i = 0; i < translationQueue.length; i += batchSize) {
         const batch = translationQueue.slice(i, i + batchSize);
         await translator(batch, targetLanguage, keepUnfinishedTypeAttr);
+        // 添加5秒延迟，让翻译更稳定
+        await new Promise(resolve => setTimeout(resolve, 5000));
         fs.writeFileSync(inputFilePath, new XMLSerializer().serializeToString(doc));
     }
 


Reviews:
user: login: BLumia
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T02:34:47Z
user: login: BLumia
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T05:33:43Z
user: login: BLumia
body: 测试分支，可合入
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-14T06:21:04Z

Review comments:
diff_hunk: @@ -0,0 +1,488 @@
+// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import fs from 'fs';
+import * as YAML from 'js-yaml';
+import { execSync } from 'child_process';
+import * as Transifex from './transifex';
+import { createTsFileFromTemplate } from './qtlinguist';
+
+// 定义小语种列表
+const MINOR_LANGUAGES = {
+    'es': '西班牙语',
+    'it': '意大利语',
+    'de': '德语',
+    'de_DE': '德语',
+    'ja': '日语',
+    'uk': '乌克兰语',
+    'pt_BR': '巴西葡萄牙语',
+    'sq': '阿尔巴尼亚语'
+};
+
+/**
+ * 从 YAML 文件加载所有项目资源
+ */
+async function loadAllResources() {
+    return await Transifex.getAllLinkedResourcesFromProjects(
+        YAML.load(fs.readFileSync('./transifex-projects.yml', 'utf8')) as string[]
+    );
+}
+
+/**
+ * 从 Transifex 同步翻译文件
+ */
+function syncTranslationsFromTransifex(repository: string, repoPath: string) {
+    console.log(`正在从Transifex平台同步${repository}仓库的翻译文件...`);
+    try {
+        // 检查仓库中是否有.tx目录
+        const txConfigPath = `${repoPath}/.tx/config`;
+        const txYamlPath = `${repoPath}/.tx/transifex.yaml`;
+        
+        if (fs.existsSync(txYamlPath) || fs.existsSync(txConfigPath)) {
path: check-ts-files.ts
user: login: BLumia
body: ```suggestion
        if (fs.existsSync(txConfigPath)) {
```

tx pull 是必须有 .tx/config ，`transifex.yaml` 不能起到相同作用。

如果预期是 transifex.yaml 的也拉取的话，就需要用 deepin-translation-utils 生成 .tx/config 了。

另外有个别项目的 .tx/config 是有问题的，需要在实际启用此脚本前人工检查一下。
created_at: 2025-05-14T02:12:26Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,488 @@
+// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import fs from 'fs';
+import * as YAML from 'js-yaml';
+import { execSync } from 'child_process';
+import * as Transifex from './transifex';
+import { createTsFileFromTemplate } from './qtlinguist';
+
+// 定义小语种列表
+const MINOR_LANGUAGES = {
+    'es': '西班牙语',
+    'it': '意大利语',
+    'de': '德语',
+    'de_DE': '德语',
+    'ja': '日语',
+    'uk': '乌克兰语',
+    'pt_BR': '巴西葡萄牙语',
+    'sq': '阿尔巴尼亚语'
+};
+
+/**
+ * 从 YAML 文件加载所有项目资源
+ */
+async function loadAllResources() {
+    return await Transifex.getAllLinkedResourcesFromProjects(
+        YAML.load(fs.readFileSync('./transifex-projects.yml', 'utf8')) as string[]
+    );
+}
+
+/**
+ * 从 Transifex 同步翻译文件
+ */
+function syncTranslationsFromTransifex(repository: string, repoPath: string) {
+    console.log(`正在从Transifex平台同步${repository}仓库的翻译文件...`);
+    try {
+        // 检查仓库中是否有.tx目录
+        const txConfigPath = `${repoPath}/.tx/config`;
+        const txYamlPath = `${repoPath}/.tx/transifex.yaml`;
+        
+        if (fs.existsSync(txYamlPath) || fs.existsSync(txConfigPath)) {
+            // 使用transifex-cli拉取所有翻译
+            console.log(`使用tx命令从Transifex拉取${repository}的翻译文件...`);
+            Transifex.downloadTranslationFilesViaCli(repoPath);
+            console.log(`成功从Transifex同步${repository}的翻译文件`);
+        } else {
+            console.log(`仓库${repository}没有.tx配置文件，无法从Transifex同步翻译`);
+        }
+    } catch (error) {
+        console.error(`从Transifex同步${repository}翻译文件时出错:`, error);
+    }
+}
+
+/**
+ * 处理ts文件，根据语种类型添加到对应列表
+ */
+function processTsFile(
+    tsFile: string, 
+    resource: any, 
+    repoPath: string, 
+    processedFiles: Set<string>,
+    filesToTranslate: { file: string; langCode: string; resource: any; repoPath: string; isTraditionalChinese?: boolean }[]
+): boolean {
+    // 如果文件已经处理过，跳过
+    if (processedFiles.has(tsFile)) {
+        return false;
+    }
+    processedFiles.add(tsFile);
+    
+    // 从文件名中提取语种代码，支持带路径的文件名
+    const match = tsFile.match(/[^/]+_([a-z]{2}(?:_[A-Z]{2})?).ts$/);
+    if (!match) {
+        console.log(`  - ${tsFile} (不符合命名规范，跳过)`);
+        return false;
+    }
+    
+    const langCode = match[1];
+
+    // 检查是否为繁体中文
+    if (['zh_HK', 'zh_TW'].includes(langCode)) {
+        console.log(`  - ${tsFile} (当前文件为简繁体转换文件，采用规则库匹配方式处理)`);
+        filesToTranslate.push({
+            file: tsFile,
+            langCode,
+            resource,
+            repoPath,
+            isTraditionalChinese: true
+        });
+        return true;
+    }
+
+    // 检查是否为小语种
+    if (langCode in MINOR_LANGUAGES) {
+        console.log(`  - ${tsFile} (${MINOR_LANGUAGES[langCode]}小语种，跳过不由脚本处理)`);
+        return true;
+    }
+
+    // 其他语种添加到待翻译列表
+    console.log(`  - ${tsFile} (需要基于AI大模型进行翻译)`);
+    filesToTranslate.push({
+        file: tsFile,
+        langCode,
+        resource,
+        repoPath
+    });
+    return true;
+}
+
+/**
+ * 检查文件内容是否包含未翻译内容
+ */
+function hasUnfinishedTranslations(fileContent: string): boolean {
+    return fileContent.includes('<translation type="unfinished"/>') || 
+           fileContent.includes('<translation type="unfinished"></translation>') ||
+           fileContent.includes('<translation type="unfinished">') ||
+           fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
+}
+
+/**
+ * 检查所有仓库最新提交中是否包含 TypeScript 文件
+ * 
+ * 功能说明：
+ * 1. 从 transifex-projects.yml 文件中读取所有项目信息
+ * 2. 遍历每个项目，检查其本地仓库是否存在
+ * 3. 对每个存在的仓库，检查其最新提交中是否包含 .ts 文件
+ * 4. 对每个找到的 ts 文件进行分析：
+ *    - 如果是繁体中文(zh_HK, zh_TW)，标记为 A 类
+ *    - 如果是小语种(es, it, de等)，显示语种信息并跳过
+ *    - 其他语种则返回给调用者处理
+ * 
+ * 注意事项：
+ * 1. 需要确保 transifex-projects.yml 文件存在且格式正确
+ * 2. 需要确保所有仓库都已克隆到 repo/ 目录下
+ * 3. 需要确保有权限访问这些仓库
+ * 4. 需要确保系统已安装 git 命令
+ * 5. 目前的逻辑只对"最新一次 commit 中的 ts 文件"生效。
+ */
+export async function checkTsFilesInGitLog() {
+    const filesToTranslate: { file: string; langCode: string; resource: any; repoPath: string; isTraditionalChinese?: boolean }[] = [];
+    let totalFilesFound = 0;
+    const processedFiles = new Set<string>(); // 用于跟踪已处理的文件
+    
+    try {
+        // 从 YAML 文件中读取所有项目信息
+        const allResources = await loadAllResources();
+
+        // 遍历每个项目
+        for (const resource of allResources) {
+            const { repository } = resource;
+            const repoPath = `repo/${repository}`;
+
+            // 检查仓库是否存在
+            if (!fs.existsSync(repoPath)) {
+                console.log(`仓库 ${repository} 不存在，跳过检查`);
+                continue;
+            }
+
+            // 使用 git log 命令检查最新提交中的 ts 文件
+            const command = `cd ${repoPath} && git log -1 --name-only --pretty=format: | grep -E "\\.ts$"`;
+            try {
+                const output = execSync(command, { encoding: 'utf8' });
+                
+                if (output.trim()) {
+                    const tsFiles = output.trim().split('\n');
+                    
+                    // 如果找到ts文件，先从Transifex同步翻译
+                    syncTranslationsFromTransifex(repository, repoPath);
+                    
+                    for (const tsFile of tsFiles) {
+                        if (processTsFile(tsFile, resource, repoPath, processedFiles, filesToTranslate)) {
+                            totalFilesFound++;
+                        }
+                    }
+                }
+            } catch (error) {
+                if ((error as any).status === 1) {
+                    // git grep 没有找到匹配项，这是正常情况
+                    continue;
+                } else {
+                    console.error(`检查仓库 ${repository} 时出错:`, error);
+                }
+            }
+        }
+
+        if (totalFilesFound > 0) {
+            console.log(`\n共找到 ${totalFilesFound} 个 ts 文件，其中 ${filesToTranslate.length} 个需要翻译`);
+        } else {
+            console.log('\n没有找到任何 ts 文件');
+        }
+    } catch (error) {
+        console.error('检查过程中出错:', error);
+    }
+
+    return filesToTranslate;
+} 
+
+/**
+ * 检查所有仓库最新提交中是否包含 _en.ts 文件，且提交标题包含"transfix"
+ * 
+ * 功能说明：
+ * 1. 从 transifex-projects.yml 文件中读取所有项目信息
+ * 2. 遍历每个项目，检查其本地仓库是否存在
+ * 3. 对每个存在的仓库，检查其最新提交中是否包含 _en.ts 文件，且提交标题包含"transfix"
+ * 4.  同时，从Transifex平台拉取最新的翻译更新，以确保GitHub和Transifex保持同步
+ * 5. 如果找到符合条件的提交，根据脚本根目录的language.yml中的语种列表，在translations目录下查找对应的ts文件
+ * 6. 对每个找到的ts文件判断：若语种不在language.yml中，则输出日志跳过；若在language.yml中，则进行后续处理：
+ *    - 如果是繁体中文(zh_HK, zh_TW)，标记为需要规则库匹配处理
+ *    - 如果是小语种(es, it, de等)，显示语种信息并跳过
+ *    - 其他语种则返回给调用者处理，用于AI大模型翻译
+ * 7. 如果language.yml中有新增的语言代码但还没有对应的ts文件，则自动基于英文文件创建新的ts文件
+ */
+export async function checkEnTsFilesWithTransfix() {
+    const filesToTranslate: { file: string; langCode: string; resource: any; repoPath: string; isTraditionalChinese?: boolean }[] = [];
+    let totalFilesFound = 0;
+    const processedFiles = new Set<string>(); // 用于跟踪已处理的文件
+    let foundTransfixCommit = false; // 标记是否找到包含"transfix"的提交
+    
+    try {
+        // 读取脚本根目录下的language.yml文件获取语种列表
+        const languageYmlPath = './language.yml';
+        if (!fs.existsSync(languageYmlPath)) {
+            console.log(`脚本根目录不存在language.yml文件，退出检查`);
+            return { filesToTranslate, foundTransfixCommit, totalFilesFound };
+        }
+        
+        const languageCodes = fs.readFileSync(languageYmlPath, 'utf8')
+            .split('\n')
+            .filter(line => line.trim().length > 0)
+            .map(line => line.trim()); // 修复：移除每行可能存在的空格
+            
+        console.log(`从脚本根目录的language.yml中读取到${languageCodes.length}种语言`);
+        
+        // 从 YAML 文件中读取所有项目信息
+        const allResources = await loadAllResources();
+
+        console.log(`从transifex-projects.yml中读取到${allResources.length}个项目`);
+
+        // 遍历每个项目
+        for (const resource of allResources) {
+            const { repository } = resource;
+            const repoPath = `repo/${repository}`;
+
+            // 检查仓库是否存在
+            if (!fs.existsSync(repoPath)) {
+                console.log(`仓库 ${repository} 不存在，跳过检查`);
+                continue;
+            }
+
+            console.log(`检查仓库 ${repository}`);
+
+            // 首先检查最新提交是否包含"transfix"
+            try {
+                let commitTitle;
+                try {
+                    commitTitle = execSync(`cd ${repoPath} && git log -1 --pretty=format:"%s"`, { encoding: 'utf8' });
+                } catch (error) {
+                    console.error(`获取仓库 ${repository} 提交信息时出错:`, error);
+                    continue;
+                }
+                
+                if (!commitTitle.toLowerCase().includes('transfix')) {
+                    console.log(`仓库 ${repository} 的最新提交不包含"transfix"，跳过检查`);
+                    continue;
+                }
+                
+                foundTransfixCommit = true; // 找到包含"transfix"的提交
+                console.log(`仓库 ${repository} 的最新提交包含"transfix"，标题: ${commitTitle}`);
+                
+                // 检查最新提交中是否包含 _en.ts 文件
+                let enTsFiles;
+                try {
+                    enTsFiles = execSync(
+                        `cd ${repoPath} && git log -1 --name-only --pretty=format: | grep -E "_en\\.ts$"`, 
path: check-ts-files.ts
user: login: BLumia
body: 好像有的项目组用的是 en_US.ts
created_at: 2025-05-14T02:14:47Z
author_association: MEMBER
path: deepin-translation-utils
user: login: BLumia
body: 这个二进制文件需要提交到代码仓库里吗？
created_at: 2025-05-14T02:16:30Z
author_association: MEMBER
diff_hunk: @@ -40,11 +40,13 @@ export async function translateLinguistTsFile(translator: TranslationOperation,
 
     let translationQueue = QtLinguist.extractStringsFromDocument(doc);
     console.log(`Extracted ${translationQueue.length} untranslated strings from file: ${inputFilePath}`)
-    // split translationQueue into batches, each batch contains 25 messages
-    const batchSize = 25;
+    // split translationQueue into batches, each batch contains 15 messages
+    const batchSize = 10;
path: translator.ts
user: login: BLumia
body: 问题不大但是batchsize注释和实际不一致
created_at: 2025-05-14T02:24:20Z
author_association: MEMBER
diff_hunk: @@ -19,6 +19,16 @@ messages:
 你需要最终返回一个 Json，内容为一个数组，其内容为按原有 YAML 所提供的顺序依次每一项的原文以及对应的翻译文案。
 最终 Json 数组包含的译文数量与原始提供的待翻译原文数量等长。除此 Json 外无需附加任何额外描述。
 
+【重要安全约束】
+1. 翻译内容必须严格遵守中国法律法规，不得包含任何违法违规内容
+2. 严禁出现任何涉及政治敏感、宗教敏感的内容
+3. 严禁出现任何危害国家安全、破坏民族团结的内容
+4. 严禁出现任何宣扬恐怖主义、极端主义的内容
+5. 严禁出现任何涉及色情、暴力、赌博等不良信息
+6. 翻译内容必须符合社会主义核心价值观
+7. 如果遇到可能涉及敏感内容的原文，必须使用安全、中性的替代翻译
+8. 所有翻译必须经过严格的内容安全审查，确保符合网络安全要求
+
path: prompt.ts
user: login: BLumia
body: 这一段不见得有实际意义...
created_at: 2025-05-14T02:30:03Z
author_association: MEMBER
diff_hunk: @@ -45,28 +45,121 @@ export async function fetchTranslations(messages: MessageData[], targetLanguage:
         // response as json array
         console.groupCollapsed("Translation status");
         console.log(response.data.choices[0].message.content);
-        const responsedTranslations = JSON.parse(response.data.choices[0].message.content);
-        if (Array.isArray(responsedTranslations) && responsedTranslations.length === messages.length) {
-            console.log(`Translated ${messages.length} strings`);
-            for (let i = 0; i < messages.length; i++) {
-                const translation = responsedTranslations[i];
-                let translationElement = messages[i].translationElement;
-                if (translationElement) {
-                    translationElement.textContent = translation.translation;
-                    // also check if we need to remove the type=unfinished attribute
-                    if (!keepUnfinishedTypeAttr && translationElement.getAttribute('type') === 'unfinished') {
-                        translationElement.removeAttribute('type');
+        // 对返回内容进行预处理，移除可能的Markdown代码块标记和清理内容
+        const content = response.data.choices[0].message.content.replace(/```json\n?|\n?```/g, '').trim();
+        try {
+            // 尝试修复JSON格式问题，处理可能的未终止字符串
+            let fixedContent = content;
+            // 检查是否是有效的JSON
+            if (!isValidJson(fixedContent)) {
+                console.log("尝试修复不完整的JSON...");
+                // 1. 查找缺少闭合引号的字符串
+                fixedContent = fixJsonString(fixedContent);
+                // 2. 如果仍然无效，尝试更简单的方式：截断内容到最后一个完整的JSON对象
+                if (!isValidJson(fixedContent)) {
+                    console.log("尝试截断到最后一个完整对象...");
+                    fixedContent = truncateToValidJson(fixedContent);
+                }
+                console.log("修复后的JSON:", fixedContent);
+            }
+            const responsedTranslations = JSON.parse(fixedContent);
+            if (Array.isArray(responsedTranslations) && responsedTranslations.length === messages.length) {
+                console.log(`Translated ${messages.length} strings`);
+                for (let i = 0; i < messages.length; i++) {
+                    const translation = responsedTranslations[i];
+                    let translationElement = messages[i].translationElement;
+                    if (translationElement) {
+                        translationElement.textContent = translation.translation;
+                        // also check if we need to remove the type=unfinished attribute
+                        if (!keepUnfinishedTypeAttr && translationElement.getAttribute('type') === 'unfinished') {
+                            translationElement.removeAttribute('type');
+                        }
                     }
                 }
+            } else {
+                console.log(Array.isArray(responsedTranslations), responsedTranslations.length, messages.length);
+                console.error(`Unexpected response from OpenAI endpoint: ${responsedTranslations}`);
             }
-        } else {
-            console.log(Array.isArray(responsedTranslations), responsedTranslations.length, messages.length);
-            console.error(`Unexpected response from OpenAI endpoint: ${responsedTranslations}`);
+            // also log token usage
+            console.log(response.data.usage);
+            console.groupEnd();
+        } catch (error) {
+            console.error("JSON解析错误:", error);
+            console.error("原始内容:", response.data.choices[0].message.content);
+            console.error("处理后内容:", content);
+            console.groupEnd();
         }
-        // also log token usage
-        console.log(response.data.usage);
-        console.groupEnd();
     }).catch(error => {
         console.error(error);
     });
+}
+
+// 检查字符串是否为有效的JSON
+function isValidJson(str) {
+    try {
+        JSON.parse(str);
+        return true;
+    } catch (e) {
+        return false;
+    }
+}
+
+// 尝试修复未终止的JSON字符串
+function fixJsonString(str) {
+    // 如果是数组开头但没有数组结尾，添加结尾
+    if (str.trim().startsWith('[') && !str.trim().endsWith(']')) {
+        str = str.trim() + ']';
+    }
+    
+    // 查找可能未闭合的对象
+    const matches = str.match(/{[^}]*$/g);
+    if (matches) {
+        str = str.replace(/{[^}]*$/g, '}');
+    }
+    
+    // 查找可能未闭合的引号
+    let inString = false;
+    let lastQuotePos = -1;
+    let needsClosingQuote = false;
+    
+    for (let i = 0; i < str.length; i++) {
+        if (str[i] === '"' && (i === 0 || str[i-1] !== '\\')) {
+            if (inString) {
+                inString = false;
+            } else {
+                inString = true;
+                lastQuotePos = i;
+            }
+        }
+    }
+    
+    // 如果字符串以未闭合的引号结束，添加闭合引号
+    if (inString) {
+        const beforeQuote = str.substring(0, lastQuotePos);
+        const afterQuote = str.substring(lastQuotePos);
+        // 限制翻译内容长度，避免过长的无效内容
+        const maxLength = 100; // 合理的翻译长度限制
+        const quotedContent = afterQuote.substring(1, Math.min(maxLength, afterQuote.length));
+        str = beforeQuote + '"' + quotedContent + '"' + (afterQuote.length > maxLength ? '}]' : '');
+    }
+    
+    return str;
+}
+
+// 截断内容到最后一个完整的JSON对象
+function truncateToValidJson(str) {
+    // 如果是数组格式，尝试保留开头和必要的部分
+    if (str.trim().startsWith('[')) {
+        // 尝试保留开头的[和第一个完整对象
+        const match = str.match(/\[\s*{[^{]*?}\s*(?:,|$)/);
+        if (match) {
+            return match[0].endsWith(',') ? match[0].slice(0, -1) + ']' : match[0] + ']';
+        }
+        
+        // 如果无法找到完整对象，但有开始标记，构建一个最小有效的JSON数组
+        return '[{"source":"Error parsing response","translation":"翻译解析错误"}]';
+    }
+    
+    // 无法修复，返回一个最小有效的JSON
+    return '[{"source":"Error parsing response","translation":"翻译解析错误"}]';
path: openai.ts
user: login: BLumia
body: LLM 回应中原本带了 json codeblock 是因为两个原因：

1. 我们提示词中给的示例带了，并且没有明确显式告知那个并非示例的一部分。
2. 调用的大模型平台不支持格式化输出功能

如果2受限于平台无法解决的话，其实不如直接从1入手解决，即直接从提示词中删除输出示例前后的 json codeblock 标记。
created_at: 2025-05-14T02:33:22Z
author_association: MEMBER
diff_hunk: @@ -1,53 +1,420 @@
 // SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
-//
 // SPDX-License-Identifier: GPL-3.0-or-later
-
 import fs from 'node:fs';
 import * as YAML from 'js-yaml';
 import * as QtLinguist from './qtlinguist';
 import * as OpenAI from './openai';
 import * as Ollama from './ollama';
 import * as Transifex from './transifex';
 import * as GitRepo from './gitrepo';
+import * as Doubao from './doubao';
 import { MessageData, TransifexRepo, TransifexResource } from './types';
 import { exit } from 'node:process';
 import * as Translator from './translator';
+import { checkTsFilesInGitLog, checkEnTsFilesWithTransfix } from './check-ts-files';
+import { execSync } from 'child_process';
+import path from 'path';
+
+// 选择翻译服务
+const TRANSLATION_SERVICE = {
+    DOUBAO: Doubao.fetchTranslations,
+    OPENAI: OpenAI.fetchTranslations,
+//   OLLAMA: Ollama.fetchTranslations 这个还没验证过，暂时不使用
+};
+
+// 在这里选择要使用的翻译服务，同理管理使用的模型接口
+const selectedTranslationService = TRANSLATION_SERVICE.OPENAI;
+
+/*
+ 需要获取所有项目名后，再挑选需要的项目
+ const transifexProjects = await Transifex.getAllProjects('o:linuxdeepin');
+ // 过滤掉指定的项目
+ const filteredProjects = transifexProjects.filter(project => 
+   !['o:linuxdeepin:p:linyaps', 'o:linuxdeepin:p:other-products', 'o:linuxdeepin:p:scan-assistant'].includes(project)
+ );
+ fs.writeFileSync('./transifex-projects.yml', YAML.dump(filteredProjects));
+*/
+
+const allResources = await Transifex.getAllLinkedResourcesFromProjects(YAML.load(fs.readFileSync('./transifex-projects.yml', 'utf8')) as string[]);
+
+// 记录简体中文文件路径，用于后续处理繁体中文
+const zhCNFilePaths = new Map<string, string>();
+
+// 直接调用Translator进行翻译，跳过Transifex上传操作
+async function translateTsFile(filePath: string, langCode: string): Promise<boolean> {
+    try {
+        console.log(`直接翻译文件: ${filePath} (${langCode})`);
+        
+        // 检查文件是否存在
+        if (!fs.existsSync(filePath)) {
+            console.error(`文件不存在: ${filePath}`);
+            return false;
+        }
+        
+        // 读取文件内容
+        const fileContent = fs.readFileSync(filePath, 'utf8');
+        
+        // 检查文件中是否包含未翻译内容
+        const hasUnfinishedTranslations = fileContent.includes('<translation type="unfinished"/>') || 
+                                          fileContent.includes('<translation type="unfinished"></translation>') ||
+                                          fileContent.includes('<translation type="unfinished">') ||
+                                          fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
+        
+        if (!hasUnfinishedTranslations) {
+            console.log(`文件 ${filePath} 没有未翻译内容，跳过处理`);
+            return false;
+        }
+        
+        // 使用Translator提取并翻译内容
+        console.log(`开始处理文件: ${filePath}`);
+        const translatedCount = await Translator.translateLinguistTsFile(
+            selectedTranslationService,
+            filePath,
+            langCode,
+            false
+        );
+        
+        console.log(`文件 ${filePath} 翻译完成，翻译了 ${translatedCount} 个字符串`);
+        return translatedCount > 0;
+    } catch (error) {
+        console.error(`翻译文件 ${filePath} 时出错:`, error);
+        return false;
+    }
+}
+
+// 检查最新的 git log 中的 ts 文件并进行翻译
+async function main() {
+    console.log('开始检查并处理翻译文件...');
+    
+    // 首先尝试检查包含"transfix"的提交中的_en.ts文件
+    console.log('尝试检查包含"transfix"的提交中的_en.ts文件...');
+    const { filesToTranslate: transfixFiles, foundTransfixCommit, totalFilesFound } = await checkEnTsFilesWithTransfix();
+
+    // 修改判断逻辑：只有当没有找到包含"transfix"的提交时，才使用常规方法
+    let filesToTranslate = transfixFiles;
+    if (!foundTransfixCommit) {
+        console.log('没有找到包含"transfix"的提交中的_en.ts文件，尝试使用常规方法检查...');
+        filesToTranslate = await checkTsFilesInGitLog();
+    } else {
+        console.log(`开始处理翻译文件...`);
+    }
+
+    if (filesToTranslate.length === 0) {
+        console.log('没有需要翻译的文件');
+        return;
+    }
+
+    console.log(`\n开始处理 ${filesToTranslate.length} 个需要翻译的文件`);
+    
+    // 记录成功翻译的文件和它们的仓库路径
+    const translatedFiles = new Map<string, Set<string>>();
+    
+    // 将文件分为两类：繁体中文和非繁体中文
+    const traditionalFiles = filesToTranslate.filter(item => 
+        ['zh_HK', 'zh_TW'].includes(item.langCode) || item.isTraditionalChinese);
+    const nonTraditionalFiles = filesToTranslate.filter(item => 
+        !(['zh_HK', 'zh_TW'].includes(item.langCode) || item.isTraditionalChinese));
+    
+    // 创建用于记录繁体中文处理信息的数据结构
+    const tcFileMap = new Map<string, Map<string, string>>();
+    
+    // 记录需要上传到Transifex的文件
+    const transifexFilesToUpload: { file: string; language: string; resource: TransifexResource }[] = [];
+    
+    // 第一步：处理所有非繁体中文文件（包括简体中文和其他语言）
+    console.log('\n===== 步骤1：处理非繁体中文文件 =====');
+    
+    // 串行处理每个翻译任务
+    console.log(`开始串行处理 ${nonTraditionalFiles.length} 个翻译文件...`);
+    let successfullyTranslatedCount = 0;
+    
+    for (const { file, langCode, resource, repoPath } of nonTraditionalFiles) {
+        if (!repoPath) {
+            console.warn(`警告: 文件 ${file} 没有关联的仓库路径，跳过处理`);
+            continue;
+        }
+        
+        const fullPath = `${repoPath}/${file}`;
+        console.log(`\n[${successfullyTranslatedCount+1}/${nonTraditionalFiles.length}] 开始翻译 ${file} (${langCode}) (仓库路径: ${repoPath})`);
+        
+        try {
+            // 直接翻译文件，不上传到Transifex
+            const translated = await translateTsFile(fullPath, langCode);
+            
+            if (translated) {
+                successfullyTranslatedCount++;
+                
+                // 如果是简体中文文件，记录路径用于后续处理繁体中文
+                if (langCode === 'zh_CN') {
+                    const baseFileName = path.basename(file).replace(/_zh_CN\.ts$/, '');
+                    zhCNFilePaths.set(baseFileName, fullPath);
+                }
+                
+                // 记录成功翻译的文件
+                if (!translatedFiles.has(repoPath)) {
+                    translatedFiles.set(repoPath, new Set());
+                }
+                translatedFiles.get(repoPath)?.add(file);
+                
+                // 添加到待上传列表
+                transifexFilesToUpload.push({
+                    file: fullPath,
+                    language: langCode,
+                    resource
+                });
+                
+                console.log(`翻译完成: ${file} (${langCode})`);
+            } else {
+                console.log(`文件 ${file} 无需翻译或翻译失败`);
+            }
+        } catch (error) {
+            console.error(`处理 ${file} (${langCode}) 时出错:`, error);
+        }
+    }
+    
+    console.log(`\n所有 ${nonTraditionalFiles.length} 个非繁体中文文件处理完成，成功翻译: ${successfullyTranslatedCount} 个`);
+    
+    // 第二步：收集并处理繁体中文文件
+    console.log('\n===== 步骤2：处理繁体中文文件 =====');
+    
+    // 收集繁体中文文件信息
+    for (const { file, langCode, resource, repoPath } of traditionalFiles) {
+        if (!repoPath) continue;
+        
+        console.log(`\n收集繁体中文文件信息: ${file} (${langCode})`);
+        const baseFileName = path.basename(file).replace(/_([a-z]{2}(?:_[A-Z]{2})?)\.ts$/, '');
+        
+        // 检查对应的简体中文文件是否存在
+        // 如果没有在之前的处理中找到，尝试从文件系统中查找
+        if (!zhCNFilePaths.has(baseFileName)) {
+            const possibleZhCNPath = `${repoPath}/${file.replace(/_zh_[A-Z]{2}\.ts$/, '_zh_CN.ts')}`;
+            if (fs.existsSync(possibleZhCNPath)) {
+                console.log(`找到对应的简体中文文件: ${possibleZhCNPath}`);
+                zhCNFilePaths.set(baseFileName, possibleZhCNPath);
+            } else {
+                // 再尝试在translations目录下查找
+                const altPath = `${repoPath}/translations/${baseFileName}_zh_CN.ts`;
+                if (fs.existsSync(altPath)) {
+                    console.log(`找到对应的简体中文文件: ${altPath}`);
+                    zhCNFilePaths.set(baseFileName, altPath);
+                } else {
+                    console.warn(`未找到与 ${baseFileName} 对应的简体中文文件，无法处理繁体中文`);
+                    continue;
+                }
+            }
+        }
+        
+        // 记录到繁体中文处理映射中
+        if (!tcFileMap.has(repoPath)) {
+            tcFileMap.set(repoPath, new Map());
+        }
+        tcFileMap.get(repoPath)?.set(baseFileName, langCode);
+    }
+    
+    // 使用 deepin-translation-utils 处理繁体中文文件
+    if (tcFileMap.size > 0) {
+        console.log('\n开始使用 deepin-translation-utils 处理繁体中文文件...');
+        const tcFilesResult = await processTraditionalChineseFiles(tcFileMap, translatedFiles);
+        
+        // 添加繁体中文文件到待上传列表
+        for (const { filePath, langCode, resource } of tcFilesResult) {
+            transifexFilesToUpload.push({
+                file: filePath,
+                language: langCode,
+                resource
+            });
+        }
+    } else {
+        console.log('\n没有需要处理的繁体中文文件');
+    }
+    
+    // 第三步：统一上传所有文件到Transifex
+    if (transifexFilesToUpload.length > 0) {
+        console.log(`\n===== 步骤3：上传 ${transifexFilesToUpload.length} 个翻译文件到Transifex =====`);
+        
+        for (let i = 0; i < transifexFilesToUpload.length; i++) {
+            const { file, language, resource } = transifexFilesToUpload[i];
+            console.log(`\n[${i+1}/${transifexFilesToUpload.length}] 上传文件到Transifex: ${file} (${language})`);
+            
+            try {
+                // 使用Transifex模块的uploadTranslation方法上传
+                await Transifex.uploadTranslatedFileToTransifex(language, file, resource.transifexResourceId);
+                console.log(`文件 ${file} 上传成功`);
+            } catch (error) {
+                console.error(`上传文件 ${file} 到Transifex时出错:`, error);
+            }
+        }
+    }
+    
+    // 将所有翻译好的文件提交到 Git 仓库
+    if (translatedFiles.size > 0) {
+        console.log(`\n===== 步骤4：将 ${Array.from(translatedFiles.values()).reduce((acc, files) => acc + files.size, 0)} 个翻译文件提交到Git =====`);
+        commitTranslatedFiles(translatedFiles);
+    }
+}
+
+/**
+ * 使用 deepin-translation-utils 处理繁体中文文件
+ * @param tcFileMap 繁体中文文件映射：仓库路径 -> (基本文件名 -> 语言代码)
+ * @param translatedFiles 记录翻译文件的Map
+ * @returns 处理成功的繁体中文文件信息数组
+ */
+async function processTraditionalChineseFiles(
+    tcFileMap: Map<string, Map<string, string>>,
+    translatedFiles: Map<string, Set<string>>
+): Promise<{ filePath: string; langCode: string; resource: any }[]> {
+    const processedFiles: { filePath: string; langCode: string; resource: any }[] = [];
+    
+    for (const [repoPath, fileMap] of tcFileMap.entries()) {
+        for (const [baseFileName, langCode] of fileMap.entries()) {
+            // 检查对应的简体中文文件是否存在
+            const zhCNFilePath = zhCNFilePaths.get(baseFileName);
+            
+            if (!zhCNFilePath) {
+                console.warn(`警告: 未找到与 ${baseFileName} 对应的简体中文文件，无法处理繁体中文`);
+                continue;
+            }
+            
+            // 检查繁体中文文件是否存在未翻译内容
+            const targetFilePath = zhCNFilePath.replace('_zh_CN.ts', `_${langCode}.ts`);
+            
+            // 确认文件存在
+            if (!fs.existsSync(targetFilePath)) {
+                console.warn(`警告: 繁体中文文件 ${targetFilePath} 不存在，无法检查翻译状态`);
+                continue;
+            }
+            
+            // 读取文件内容并检查是否有未翻译内容
+            try {
+                const fileContent = fs.readFileSync(targetFilePath, 'utf8');
+                
+                // 检查文件中是否包含 <translation type="unfinished"/> 标签
+                const hasUnfinishedTranslations = fileContent.includes('<translation type="unfinished"/>') || 
+                                                  fileContent.includes('<translation type="unfinished"></translation>') ||
+                                                  fileContent.includes('<translation type="unfinished">') ||
+                                                  fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
+                
+                if (!hasUnfinishedTranslations) {
+                    console.log(`繁体中文文件 ${targetFilePath} 没有未翻译内容，跳过处理`);
+                    continue;
+                }
+                
+                console.log(`检测到繁体中文文件 ${targetFilePath} 有未翻译内容，开始处理...`);
+            } catch (error) {
+                console.error(`读取繁体中文文件 ${targetFilePath} 时出错:`, error);
+                continue;
+            }
path: index.ts
user: login: BLumia
body: ```suggestion
            // 检查繁体中文文件是否存在未翻译内容
            const targetFilePath = zhCNFilePath.replace('_zh_CN.ts', `_${langCode}.ts`);
            
            // 确认文件存在
            if (!fs.existsSync(targetFilePath)) {
                console.warn(`警告: 繁体中文文件 ${targetFilePath} 不存在，无法检查翻译状态`);
                continue;
            }
            
            // 读取文件内容并检查是否有未翻译内容
            try {
                const fileContent = fs.readFileSync(targetFilePath, 'utf8');
                
                // 检查文件中是否包含 <translation type="unfinished"/> 标签
                const hasUnfinishedTranslations = fileContent.includes('<translation type="unfinished"/>') || 
                                                  fileContent.includes('<translation type="unfinished"></translation>') ||
                                                  fileContent.includes('<translation type="unfinished">') ||
                                                  fileContent.match(/<translation(\s+type="unfinished"[^>]*)\s*\/>/g) !== null;
                
                if (!hasUnfinishedTranslations) {
                    console.log(`繁体中文文件 ${targetFilePath} 没有未翻译内容，跳过处理`);
                    continue;
                }
                
                console.log(`检测到繁体中文文件 ${targetFilePath} 有未翻译内容，开始处理...`);
            } catch (error) {
                console.error(`读取繁体中文文件 ${targetFilePath} 时出错:`, error);
                continue;
            }
```

deepin-translation-utils 的 zhconv 命令默认做了检查，只会处理未翻译的部分。不存在繁体中文的情况默认也会创建新的。可以确认下这段是否有必要。
created_at: 2025-05-14T05:27:47Z
author_association: MEMBER

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-05-08T08:35:09Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide

This pull request overhauls the translation process by implementing an automated workflow that detects and processes `.ts` translation files directly from the latest Git commits. The core logic, primarily in `index.ts`, uses a new `check-ts-files.ts` module to scan repositories (defined in `transifex-projects.yml`) for recently changed `.ts` files. The system employs differentiated translation strategies: AI models (OpenAI or Doubao, now configurable) handle general languages, rule-based conversion using `deepin-translation-utils` is applied to Traditional Chinese files, and minor language files are skipped. Processed files are then automatically amended to the latest git commit. The update also includes enhancements to AI translation, such as adding safety prompts and fixing response parsing for the Doubao API, alongside stability improvements in batch processing.

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Implemented a new translation workflow driven by Git commit analysis. | <ul><li>Overhauled main script to process `.ts` files from the latest git commit.</li><li>Introduced a new module for detecting and classifying recently modified `.ts` files from specified repositories.</li><li>Automated amending of translated files to the last git commit.</li></ul> | `index.ts`<br/>`check-ts-files.ts`<br/>`transifex-projects.yml` |
| Introduced differentiated strategies for processing translation files. | <ul><li>Implemented rule-based processing for Traditional Chinese files using `deepin-translation-utils`.</li><li>Added logic to identify and skip translation for minor language files.</li><li>Enabled AI translation for general languages via configurable service providers.</li></ul> | `index.ts`<br/>`check-ts-files.ts` |
| Enhanced AI translation capabilities and reliability. | <ul><li>Added safety guidelines to AI translation prompts.</li><li>Adjusted batch processing parameters (size and delay) for improved API stability.</li><li>Corrected JSON parsing for Doubao API responses by removing Markdown fences.</li></ul> | `prompt.ts`<br/>`translator.ts`<br/>`doubao.ts` |
| Removed sample secrets configuration file. | <ul><li>Deleted the sample secrets file from the repository.</li></ul> | `secrets.sample.ts` |

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it. You can also reply to a
  review comment with `@sourcery-ai issue` to create an issue from it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time. You can also comment
  `@sourcery-ai title` on the pull request to (re-)generate the title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time exactly where you
  want it. You can also comment `@sourcery-ai summary` on the pull request to
  (re-)generate the summary at any time.
- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull
  request to (re-)generate the reviewer's guide at any time.
- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the
  pull request to resolve all Sourcery comments. Useful if you've already
  addressed all the comments and don't want to see them anymore.
- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull
  request to dismiss all existing Sourcery reviews. Especially useful if you
  want to start fresh with a new review - don't forget to comment
  `@sourcery-ai review` to trigger a new review!

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: NONE
user: login: deepin-ci-robot
created_at: 2025-05-13T11:30:02Z
body: ### deepin pr auto review
代码审查意见：

1. **安全性**：在处理文件路径时，应该使用`path.join`来确保路径的正确性，避免路径注入攻击。

2. **错误处理**：在执行`execSync`命令时，应该捕获并处理可能的异常，避免程序因为未处理的错误而崩溃。

3. **代码重复**：`checkTsFilesInGitLog`和`checkEnTsFilesWithTransfix`函数中存在大量重复代码，建议提取公共逻辑到单独的函数中，减少代码重复。

4. **性能**：在处理大量文件时，可以考虑使用并发处理（如`Promise.all`）来提高性能。

5. **日志记录**：代码中缺少详细的日志记录，建议增加日志记录，以便于问题的追踪和调试。

6. **资源管理**：在处理文件和执行命令时，应该确保资源的正确释放，避免资源泄露。

7. **类型检查**：在处理JSON解析时，应该对输入进行类型检查，避免因为输入类型错误导致的解析失败。

8. **国际化**：代码中硬编码了一些字符串，建议使用国际化（i18n）库来处理这些字符串，以便于支持多语言。

9. **配置管理**：代码中使用了硬编码的配置信息，建议将这些配置信息提取到配置文件中，以便于管理和修改。

10. **代码注释**：代码中缺少足够的注释，建议增加注释，以便于其他开发者理解代码的逻辑和意图。

11. **异常处理**：在处理异常时，应该提供详细的错误信息，以便于问题的定位和修复。

12. **代码风格**：代码中存在一些风格不一致的问题，建议统一代码风格，以提高代码的可读性和可维护性。

13. **依赖管理**：代码中使用了多个外部库，建议在`package.json`中声明依赖，并使用`npm install`命令来安装依赖，以确保依赖的正确性和版本一致性。

14. **代码优化**：在处理繁体中文文件时，可以尝试使用更高效的算法来提高处理速度。

15. **代码测试**：代码中缺少单元测试和集成测试，建议增加测试用例，以确保代码的质量和稳定性。
author_association: NONE
user: login: deepin-ci-robot
created_at: 2025-05-14T06:21:10Z
body: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/linuxdeepin/deepin-auto-translation/pull/3#pullrequestreview-2838878075" title="Approved">BLumia</a>*, *<a href="https://github.com/linuxdeepin/deepin-auto-translation/pull/3#" title="Author self-approved">JWWTSL</a>*


The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=linuxdeepin%2Fdeepin-auto-translation).

<details open>
Needs approval from an approver in each of these files:

- **[debian/deepin/OWNERS](https://github.com/linuxdeepin/deepin-auto-translation/blob/develop/test/debian/deepin/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE