diff --git a/scripts/auto-translate-i18n.ts b/scripts/auto-translate-i18n.ts index de928bee50..2ee50a486f 100644 --- a/scripts/auto-translate-i18n.ts +++ b/scripts/auto-translate-i18n.ts @@ -1,5 +1,6 @@ /** - * 该脚本用于少量自动翻译所有baseLocale以外的文本。待翻译文案必须以[to be translated]开头 + * This script is used for automatic translation of all text except baseLocale. + * Text to be translated must start with [to be translated] * */ import OpenAI from '@cherrystudio/openai' @@ -72,10 +73,10 @@ const translate = async (systemPrompt: string) => { } /** - * 递归翻译对象中的字符串值 - * @param originObj - 原始国际化对象 - * @param systemPrompt - 系统提示词 - * @returns 翻译后的新对象 + * Recursively translate string values in objects + * @param originObj - Original internationalization object + * @param systemPrompt - System prompt + * @returns Translated new object */ const translateRecursively = async (originObj: I18N, systemPrompt: string): Promise => { const newObj = {} @@ -90,7 +91,7 @@ const translateRecursively = async (originObj: I18N, systemPrompt: string): Prom newObj[key] = result } catch (e) { newObj[key] = text - console.error('translate failed.', text) + console.error('Translation failed.', text) } } else { newObj[key] = text @@ -131,7 +132,7 @@ const main = async () => { const fileContent = fs.readFileSync(filePath, 'utf-8') targetJson = JSON.parse(fileContent) } catch (error) { - console.error(`解析 ${filename} 出错,跳过此文件。`, error) + console.error(`Error parsing ${filename}, skipping this file.`, error) continue } const systemPrompt = PROMPT.replace('{{target_language}}', languageMap[filename]) @@ -142,9 +143,9 @@ const main = async () => { try { fs.writeFileSync(filePath, JSON.stringify(result, null, 2) + '\n', 'utf-8') - console.log(`文件 ${filename} 已翻译完毕`) + console.log(`File ${filename} translation completed`) } catch (error) { - console.error(`写入 ${filename} 出错。${error}`) + console.error(`Error writing ${filename}. ${error}`) } } bar.stop() diff --git a/scripts/check-i18n.ts b/scripts/check-i18n.ts index 1ecbed9834..62ad381a47 100644 --- a/scripts/check-i18n.ts +++ b/scripts/check-i18n.ts @@ -12,39 +12,41 @@ type I18NValue = string | { [key: string]: I18NValue } type I18N = { [key: string]: I18NValue } /** - * 递归检查并同步目标对象与模板对象的键值结构 - * 1. 如果目标对象缺少模板对象中的键,抛出错误 - * 2. 如果目标对象存在模板对象中不存在的键,抛出错误 - * 3. 对于嵌套对象,递归执行同步操作 + * Recursively check and synchronize the key-value structure of target object with template object + * 1. If target object is missing keys from template object, throw error + * 2. If target object has keys that don't exist in template object, throw error + * 3. For nested objects, recursively perform synchronization operation * - * 该函数用于确保所有翻译文件与基准模板(通常是中文翻译文件)保持完全一致的键值结构。 - * 任何结构上的差异都会导致错误被抛出,以便及时发现和修复翻译文件中的问题。 + * This function ensures all translation files maintain completely consistent key-value structure + * with the base template (usually the base translation file). + * Any structural differences will cause errors to be thrown for timely detection and fixing + * of translation file issues. * - * @param target 需要检查的目标翻译对象 - * @param template 作为基准的模板对象(通常是中文翻译文件) - * @throws {Error} 当发现键值结构不匹配时抛出错误 + * @param target The target translation object to check + * @param template The template object used as base (usually the base translation file) + * @throws {Error} Thrown when key-value structure mismatch is found */ function checkRecursively(target: I18N, template: I18N): void { for (const key in template) { if (!(key in target)) { - throw new Error(`缺少属性 ${key}`) + throw new Error(`Missing property ${key}`) } if (key.includes('.')) { - throw new Error(`应该使用严格嵌套结构 ${key}`) + throw new Error(`Should use strict nested structure for key ${key}`) } if (typeof template[key] === 'object' && template[key] !== null) { if (typeof target[key] !== 'object' || target[key] === null) { - throw new Error(`属性 ${key} 不是对象`) + throw new Error(`Property ${key} is not an object`) } - // 递归检查子对象 + // Recursively check child objects checkRecursively(target[key], template[key]) } } - // 删除 target 中存在但 template 中没有的 key + // Remove keys that exist in target but not in template for (const targetKey in target) { if (!(targetKey in template)) { - throw new Error(`多余属性 ${targetKey}`) + throw new Error(`Extra property ${targetKey}`) } } } @@ -56,9 +58,9 @@ function isSortedI18N(obj: I18N): boolean { } /** - * 检查 JSON 对象中是否存在重复键,并收集所有重复键 - * @param obj 要检查的对象 - * @returns 返回重复键的数组(若无重复则返回空数组) + * Check for duplicate keys in JSON object and collect all duplicate keys + * @param obj The object to check + * @returns Array of duplicate keys (returns empty array if no duplicates) */ function checkDuplicateKeys(obj: I18N): string[] { const keys = new Set() @@ -69,7 +71,7 @@ function checkDuplicateKeys(obj: I18N): string[] { const fullPath = path ? `${path}.${key}` : key if (keys.has(fullPath)) { - // 发现重复键时,添加到数组中(避免重复添加) + // When duplicate key is found, add to array (avoid duplicate additions) if (!duplicateKeys.includes(fullPath)) { duplicateKeys.push(fullPath) } @@ -77,7 +79,7 @@ function checkDuplicateKeys(obj: I18N): string[] { keys.add(fullPath) } - // 递归检查子对象 + // Recursively check child objects if (typeof obj[key] === 'object' && obj[key] !== null) { checkObject(obj[key], fullPath) } @@ -90,7 +92,7 @@ function checkDuplicateKeys(obj: I18N): string[] { function checkTranslations() { if (!fs.existsSync(baseFilePath)) { - throw new Error(`主模板文件 ${baseFileName} 不存在,请检查路径或文件名`) + throw new Error(`Base template file ${baseFileName} does not exist, please check path or filename`) } const baseContent = fs.readFileSync(baseFilePath, 'utf-8') @@ -98,23 +100,23 @@ function checkTranslations() { try { baseJson = JSON.parse(baseContent) } catch (error) { - throw new Error(`解析 ${baseFileName} 出错。${error}`) + throw new Error(`Error parsing ${baseFileName}. ${error}`) } - // 检查主模板是否存在重复键 + // Check if base template has duplicate keys const duplicateKeys = checkDuplicateKeys(baseJson) if (duplicateKeys.length > 0) { - throw new Error(`主模板文件 ${baseFileName} 存在以下重复键:\n${duplicateKeys.join('\n')}`) + throw new Error(`Base template file ${baseFileName} has the following duplicate keys:\n${duplicateKeys.join('\n')}`) } - // 检查主模板是否有序 + // Check if base template is sorted if (!isSortedI18N(baseJson)) { - throw new Error(`主模板文件 ${baseFileName} 的键值未按字典序排序。`) + throw new Error(`Base template file ${baseFileName} keys are not sorted in dictionary order.`) } const files = fs.readdirSync(translationsDir).filter((file) => file.endsWith('.json') && file !== baseFileName) - // 同步键 + // Sync keys for (const file of files) { const filePath = path.join(translationsDir, file) let targetJson: I18N = {} @@ -122,19 +124,19 @@ function checkTranslations() { const fileContent = fs.readFileSync(filePath, 'utf-8') targetJson = JSON.parse(fileContent) } catch (error) { - throw new Error(`解析 ${file} 出错。`) + throw new Error(`Error parsing ${file}.`) } - // 检查有序性 + // Check if sorted if (!isSortedI18N(targetJson)) { - throw new Error(`翻译文件 ${file} 的键值未按字典序排序。`) + throw new Error(`Translation file ${file} keys are not sorted.`) } try { checkRecursively(targetJson, baseJson) } catch (e) { console.error(e) - throw new Error(`在检查 ${filePath} 时出错`) + throw new Error(`Error while checking ${filePath}`) } } } @@ -142,10 +144,10 @@ function checkTranslations() { export function main() { try { checkTranslations() - console.log('i18n 检查已通过') + console.log('i18n check passed') } catch (e) { console.error(e) - throw new Error(`检查未通过。尝试运行 yarn sync:i18n 以解决问题。`) + throw new Error(`Check failed. Try running yarn i18n:sync to fix the issue.`) } }