vite-plugin-ai-i18n 1.0.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -376,8 +376,8 @@ var I18nTranslator = class {
|
|
|
376
376
|
openAIApiKey: options.apiKey,
|
|
377
377
|
configuration: { baseURL: options.apiUrl },
|
|
378
378
|
modelName: options.model,
|
|
379
|
-
temperature: 0.3,
|
|
380
|
-
maxTokens: 4e3
|
|
379
|
+
temperature: options.temperature ?? 0.3,
|
|
380
|
+
maxTokens: options.maxTokens ?? 4e3
|
|
381
381
|
});
|
|
382
382
|
}
|
|
383
383
|
}
|
|
@@ -692,6 +692,8 @@ function vitePluginAII18n(options = {}) {
|
|
|
692
692
|
apiKey = process.env.OPENAI_API_KEY || "",
|
|
693
693
|
apiUrl = process.env.OPENAI_API_URL || "https://api.openai.com/v1",
|
|
694
694
|
model = "gpt-4",
|
|
695
|
+
temperature = 0.3,
|
|
696
|
+
maxTokens = 4e3,
|
|
695
697
|
include = ["src/**/*.vue", "src/**/*.ts"],
|
|
696
698
|
exclude = ["node_modules/**", "dist/**"],
|
|
697
699
|
localesDir = "src/locales",
|
|
@@ -701,7 +703,13 @@ function vitePluginAII18n(options = {}) {
|
|
|
701
703
|
autoTranslate = true
|
|
702
704
|
} = options;
|
|
703
705
|
const scanner = new I18nScanner({ include, exclude });
|
|
704
|
-
const translator = new I18nTranslator({
|
|
706
|
+
const translator = new I18nTranslator({
|
|
707
|
+
apiKey,
|
|
708
|
+
apiUrl,
|
|
709
|
+
model,
|
|
710
|
+
temperature,
|
|
711
|
+
maxTokens
|
|
712
|
+
});
|
|
705
713
|
const generator = new I18nGenerator({ localesDir, defaultLocale });
|
|
706
714
|
let scannedTexts = /* @__PURE__ */ new Map();
|
|
707
715
|
return {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/scanner.ts","../src/utils.ts","../src/translator.ts","../src/generator.ts"],"sourcesContent":["/**\r\n * AI 国际化助手插件入口\r\n *\r\n * 功能:\r\n * - 扫描源码中的中文文本\r\n * - 使用 AI 自动翻译\r\n * - 生成/更新 i18n 语言文件\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport { I18nScanner } from \"./scanner\";\r\nimport { I18nTranslator } from \"./translator\";\r\nimport { I18nGenerator } from \"./generator\";\r\n\r\nexport interface I18nPluginOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n // 扫描配置\r\n include?: string[];\r\n exclude?: string[];\r\n // 输出配置\r\n localesDir?: string;\r\n defaultLocale?: string;\r\n targetLocales?: string[];\r\n // 功能开关\r\n autoScan?: boolean;\r\n autoTranslate?: boolean;\r\n}\r\n\r\nexport function vitePluginAII18n(options: I18nPluginOptions = {}): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = \"gpt-4\",\r\n include = [\"src/**/*.vue\", \"src/**/*.ts\"],\r\n exclude = [\"node_modules/**\", \"dist/**\"],\r\n localesDir = \"src/locales\",\r\n defaultLocale = \"zh-CN\",\r\n targetLocales = [\"en-US\"],\r\n autoScan = true,\r\n autoTranslate = true,\r\n } = options;\r\n\r\n const scanner = new I18nScanner({ include, exclude });\r\n const translator = new I18nTranslator({ apiKey, apiUrl, model });\r\n const generator = new I18nGenerator({ localesDir, defaultLocale });\r\n\r\n let scannedTexts: Map<string, string[]> = new Map();\r\n\r\n return {\r\n name: \"vite-plugin-ai-i18n\",\r\n enforce: \"pre\",\r\n\r\n configResolved(config) {\r\n // 🔥 关键:在最早期就创建空文件,确保 TypeScript 编译时文件存在\r\n const fullLocalesDir = path.resolve(config.root, localesDir);\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(fullLocalesDir)) {\r\n fs.mkdirSync(fullLocalesDir, { recursive: true });\r\n }\r\n\r\n // 为所有语言创建空文件(如果不存在)\r\n const allLocales = [defaultLocale, ...targetLocales];\r\n for (const locale of allLocales) {\r\n const filePath = path.join(fullLocalesDir, `${locale}.json`);\r\n if (!fs.existsSync(filePath)) {\r\n fs.writeFileSync(filePath, \"{}\", \"utf-8\");\r\n console.log(\r\n pc.green(\r\n `✅ 已生成 ${pc.cyan(locale)} 语言文件: ${pc.gray(\r\n `src\\\\locales\\\\${locale}.json`,\r\n )}`,\r\n ),\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.cyan(\"\\n🌍 AI 国际化助手已启动...\"));\r\n console.log(`📂 语言文件目录: ${pc.yellow(localesDir)}`);\r\n console.log(`🔤 默认语言: ${pc.cyan(defaultLocale)}`);\r\n console.log(`🎯 目标语言: ${pc.cyan(targetLocales.join(\", \"))}`);\r\n console.log(`🔍 自动扫描: ${autoScan ? pc.green(\"✅\") : pc.red(\"❌\")}`);\r\n console.log(\r\n `🤖 自动翻译: ${autoTranslate ? pc.green(\"✅\") : pc.red(\"❌\")}`,\r\n );\r\n console.log(\r\n `🔑 API Key: ${apiKey ? pc.green(\"已配置\") : pc.yellow(\"未配置\")}\\n`,\r\n );\r\n },\r\n\r\n async buildStart() {\r\n if (!autoScan) return;\r\n\r\n console.log(pc.cyan(\"🔍 正在扫描中文文本...\\n\"));\r\n scannedTexts = await scanner.scan();\r\n\r\n const totalTexts = Array.from(scannedTexts.values()).flat().length;\r\n console.log(\r\n pc.blue(`📝 发现 ${pc.yellow(totalTexts.toString())} 条待翻译文本\\n`),\r\n );\r\n\r\n if (totalTexts === 0) return;\r\n\r\n // 生成默认语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n // 自动翻译到目标语言\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n console.log(pc.cyan(`\\n🌐 正在翻译到 ${pc.yellow(locale)}...`));\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n scannedTexts,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n scannedTexts,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.green(\"\\n✨ 国际化处理完成\\n\"));\r\n },\r\n\r\n // 监听文件变化,增量更新\r\n async handleHotUpdate({ file, server }) {\r\n if (!autoScan) return;\r\n if (!file.match(/\\.(vue|ts|tsx)$/)) return;\r\n if (file.includes(\"node_modules\") || file.includes(localesDir)) return;\r\n\r\n console.log(`\\n🔄 检测到文件变化: ${file}`);\r\n const texts = scanner.scanFile(file);\r\n\r\n if (texts.length > 0) {\r\n console.log(`📝 发现 ${texts.length} 条新文本`);\r\n scannedTexts.set(file, texts);\r\n\r\n // 更新语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n const fileTextsMap = new Map([[file, texts]]);\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n fileTextsMap,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n fileTextsMap,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAII18n;\r\n","/**\r\n * 中文文本扫描器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { glob } from \"./utils\";\r\n\r\nexport interface ScannerOptions {\r\n include: string[];\r\n exclude: string[];\r\n debug?: boolean; // 调试模式\r\n}\r\n\r\nexport class I18nScanner {\r\n private options: ScannerOptions;\r\n\r\n // 匹配中文字符的正则\r\n private chineseRegex = /[\\u4e00-\\u9fa5]+[^\\n<>{}]*[\\u4e00-\\u9fa5]*/g;\r\n\r\n // 需要忽略的模式\r\n private ignorePatterns = [\r\n /console\\.(log|warn|error|info)/,\r\n /\\/\\/.*$/,\r\n /\\/\\*[\\s\\S]*?\\*\\//,\r\n /<!--[\\s\\S]*?-->/,\r\n ];\r\n\r\n constructor(options: ScannerOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 扫描所有匹配的文件\r\n */\r\n async scan(): Promise<Map<string, string[]>> {\r\n const results = new Map<string, string[]>();\r\n const files = await glob(this.options.include, this.options.exclude);\r\n\r\n for (const file of files) {\r\n const texts = this.scanFile(file);\r\n if (texts.length > 0) {\r\n results.set(file, texts);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 扫描单个文件\r\n */\r\n scanFile(filePath: string): string[] {\r\n if (!fs.existsSync(filePath)) return [];\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n const ext = path.extname(filePath);\r\n\r\n let texts: string[] = [];\r\n\r\n if (ext === \".vue\") {\r\n texts = this.scanVueFile(content);\r\n } else if ([\".ts\", \".tsx\", \".js\", \".jsx\"].includes(ext)) {\r\n texts = this.scanScriptFile(content);\r\n }\r\n\r\n // 使用 Set 去重并过滤(性能优化)\r\n const uniqueTexts = new Set(texts);\r\n return Array.from(uniqueTexts).filter((t) => this.isValidText(t));\r\n }\r\n\r\n /**\r\n * 扫描 Vue 文件\r\n */\r\n private scanVueFile(content: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 扫描 template 部分\r\n const templateMatch = content.match(\r\n /<template[^>]*>([\\s\\S]*?)<\\/template>/\r\n );\r\n if (templateMatch) {\r\n texts.push(...this.extractChineseFromTemplate(templateMatch[1]));\r\n }\r\n\r\n // 扫描 script 部分\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (scriptMatch) {\r\n texts.push(...this.extractChineseFromScript(scriptMatch[1]));\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 扫描脚本文件\r\n */\r\n private scanScriptFile(content: string): string[] {\r\n return this.extractChineseFromScript(content);\r\n }\r\n\r\n /**\r\n * 安全地移除注释(不影响字符串中的内容)\r\n */\r\n private removeComments(code: string): string {\r\n let result = \"\";\r\n let inString = false;\r\n let stringChar = \"\";\r\n let inBlockComment = false;\r\n let inLineComment = false;\r\n\r\n for (let i = 0; i < code.length; i++) {\r\n const char = code[i];\r\n const nextChar = code[i + 1];\r\n const prevChar = code[i - 1];\r\n\r\n // 处理字符串\r\n if (!inBlockComment && !inLineComment) {\r\n if (\r\n (char === '\"' || char === \"'\" || char === \"`\") &&\r\n prevChar !== \"\\\\\"\r\n ) {\r\n if (!inString) {\r\n inString = true;\r\n stringChar = char;\r\n } else if (char === stringChar) {\r\n inString = false;\r\n }\r\n }\r\n }\r\n\r\n // 在字符串中,保留所有字符\r\n if (inString) {\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 处理块注释\r\n if (char === \"/\" && nextChar === \"*\" && !inLineComment) {\r\n inBlockComment = true;\r\n i++; // 跳过 *\r\n continue;\r\n }\r\n if (char === \"*\" && nextChar === \"/\" && inBlockComment) {\r\n inBlockComment = false;\r\n i++; // 跳过 /\r\n continue;\r\n }\r\n\r\n // 处理行注释\r\n if (char === \"/\" && nextChar === \"/\" && !inBlockComment) {\r\n inLineComment = true;\r\n i++; // 跳过第二个 /\r\n continue;\r\n }\r\n if (char === \"\\n\" && inLineComment) {\r\n inLineComment = false;\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 跳过注释内容\r\n if (inBlockComment || inLineComment) {\r\n continue;\r\n }\r\n\r\n result += char;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * 从模板中提取中文\r\n */\r\n private extractChineseFromTemplate(template: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 移除注释\r\n let cleaned = template.replace(/<!--[\\s\\S]*?-->/g, \"\");\r\n\r\n // 1. 提取 t() 或 $t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /(?:\\$t|\\bt)\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取标签内的纯文本(不包含插值表达式)\r\n const tagTextRegex = />([^<{]+)</g;\r\n while ((match = tagTextRegex.exec(cleaned)) !== null) {\r\n const text = match[1].trim();\r\n if (text && /[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 3. 提取插值中的字符串字面量(不包括 t() 调用)\r\n const interpolationRegex = /\\{\\{\\s*[\"']([^\"']+)[\"']\\s*\\}\\}/g;\r\n while ((match = interpolationRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 4. 提取静态属性中的中文\r\n const staticAttrRegex =\r\n /(?:placeholder|title|label|alt|content|aria-label)=[\"']([^\"']*[\\u4e00-\\u9fa5][^\"']*)[\"']/g;\r\n while ((match = staticAttrRegex.exec(cleaned)) !== null) {\r\n texts.push(match[1]);\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 从脚本中提取中文\r\n */\r\n private extractChineseFromScript(script: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 安全地移除注释\r\n const cleaned = this.removeComments(script);\r\n\r\n // 1. 提取 t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /\\bt\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取所有字符串字面量(不在 t() 中的)\r\n const allStrings: string[] = [];\r\n\r\n // 单引号字符串\r\n const singleQuoteRegex = /'([^'\\\\]*(\\\\.[^'\\\\]*)*)'/g;\r\n while ((match = singleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 双引号字符串\r\n const doubleQuoteRegex = /\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"/g;\r\n while ((match = doubleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 模板字符串(简单情况,不包含插值)\r\n const templateRegex = /`([^`$\\\\]*(\\\\.[^`$\\\\]*)*)`/g;\r\n while ((match = templateRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 3. 过滤出包含中文的字符串\r\n for (const str of allStrings) {\r\n if (/[\\u4e00-\\u9fa5]/.test(str)) {\r\n texts.push(str);\r\n }\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 验证文本是否有效\r\n */\r\n private isValidText(text: string): boolean {\r\n const debug = this.options.debug;\r\n\r\n // 1. 基础过滤\r\n if (text.length < 2) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本太短`);\r\n return false;\r\n }\r\n\r\n if (!/[\\u4e00-\\u9fa5]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 不包含中文`);\r\n return false;\r\n }\r\n\r\n if (/^\\s*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯空格`);\r\n return false;\r\n }\r\n\r\n // 2. 过滤 i18n 相关\r\n if (/^\\$t\\(|^t\\(|^i18n\\.|_uni_app$|^[a-z_]+_[a-z_]+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: i18n key`);\r\n return false;\r\n }\r\n\r\n // 3. 过滤系统提示信息\r\n if (/^[⚠️❌✅🔍📝💡🎯🚀🔧📊]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 系统提示`);\r\n return false;\r\n }\r\n\r\n // 4. 过滤技术术语\r\n if (\r\n /\\.(json|js|ts|vue|md|txt|html|css|jsx|tsx)\\s*(文件|不存在|已|错误)/.test(\r\n text\r\n )\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术术语`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9_\\-\\.]+\\s*(文件|不存在|错误|失败)/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术错误信息`);\r\n return false;\r\n }\r\n\r\n // 5. 过滤变量名和路径\r\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 变量名`);\r\n return false;\r\n }\r\n\r\n if (/^\\/[a-zA-Z0-9_\\-\\/]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 路径`);\r\n return false;\r\n }\r\n\r\n // 6. 过滤 URL 和邮箱\r\n if (/^https?:\\/\\//.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: URL`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 邮箱`);\r\n return false;\r\n }\r\n\r\n // 7. 过滤纯数字和日期\r\n if (/^\\d+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯数字`);\r\n return false;\r\n }\r\n\r\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 日期`);\r\n return false;\r\n }\r\n\r\n // 8. 过滤代码片段\r\n if (\r\n /^(const|let|var|function|class|import|export|return)\\s/.test(text)\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 代码片段`);\r\n return false;\r\n }\r\n\r\n // 9. 过滤过长的文本(可能是代码)\r\n if (text.length > 100) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本过长`);\r\n return false;\r\n }\r\n\r\n // 10. 过滤包含特殊字符过多的文本\r\n const specialCharCount = (\r\n text.match(/[^\\u4e00-\\u9fa5a-zA-Z0-9\\s,。!?、;:\"\"''()《》]/g) || []\r\n ).length;\r\n if (specialCharCount > text.length * 0.3) {\r\n if (debug)\r\n console.log(`[过滤] \"${text}\" - 原因: 特殊字符过多 (${specialCharCount})`);\r\n return false;\r\n }\r\n\r\n if (debug) console.log(`[保留] \"${text}\"`);\r\n return true;\r\n }\r\n}\r\n","/**\r\n * i18n 插件工具函数\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\n\r\n/**\r\n * 简单的 glob 实现\r\n */\r\nexport async function glob(\r\n patterns: string[],\r\n excludePatterns: string[]\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n const cwd = process.cwd();\r\n\r\n for (const pattern of patterns) {\r\n const files = await matchPattern(pattern, cwd);\r\n results.push(...files);\r\n }\r\n\r\n // 过滤排除的文件\r\n return results.filter((file) => {\r\n const relativePath = path.relative(cwd, file);\r\n return !excludePatterns.some((p) => matchGlob(relativePath, p));\r\n });\r\n}\r\n\r\n/**\r\n * 匹配单个 glob 模式\r\n */\r\nasync function matchPattern(pattern: string, cwd: string): Promise<string[]> {\r\n const results: string[] = [];\r\n\r\n // 解析模式\r\n const parts = pattern.split(\"/\");\r\n const hasGlobstar = parts.includes(\"**\");\r\n\r\n if (hasGlobstar) {\r\n // 递归搜索\r\n const baseDir = parts.slice(0, parts.indexOf(\"**\")).join(\"/\") || \".\";\r\n const filePattern = parts.slice(parts.indexOf(\"**\") + 1).join(\"/\");\r\n await walkDir(path.join(cwd, baseDir), filePattern, results);\r\n } else {\r\n // 直接匹配\r\n const fullPath = path.join(cwd, pattern);\r\n if (fs.existsSync(fullPath)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * 递归遍历目录\r\n */\r\nasync function walkDir(\r\n dir: string,\r\n pattern: string,\r\n results: string[]\r\n): Promise<void> {\r\n if (!fs.existsSync(dir)) return;\r\n\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n // 跳过 node_modules 和隐藏目录\r\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) {\r\n continue;\r\n }\r\n await walkDir(fullPath, pattern, results);\r\n } else if (entry.isFile()) {\r\n if (matchGlob(entry.name, pattern)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 简单的 glob 匹配\r\n */\r\nfunction matchGlob(str: string, pattern: string): boolean {\r\n // 转换 glob 模式为正则\r\n const regexPattern = pattern\r\n .replace(/\\./g, \"\\\\.\")\r\n .replace(/\\*\\*/g, \".*\")\r\n .replace(/\\*/g, \"[^/]*\")\r\n .replace(/\\?/g, \".\");\r\n\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(str);\r\n}\r\n\r\n/**\r\n * 生成唯一 ID\r\n */\r\nexport function generateId(): string {\r\n return Math.random().toString(36).slice(2, 10);\r\n}\r\n\r\n/**\r\n * 格式化文件大小\r\n */\r\nexport function formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n}\r\n","/**\r\n * AI 翻译器\r\n */\r\n\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface TranslatorOptions {\r\n apiKey: string;\r\n apiUrl: string;\r\n model: string;\r\n}\r\n\r\nexport class I18nTranslator {\r\n private llm: ChatOpenAI | null = null;\r\n private options: TranslatorOptions;\r\n\r\n constructor(options: TranslatorOptions) {\r\n this.options = options;\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: 0.3,\r\n maxTokens: 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 批量翻译文本 - 支持增量翻译(跳过已翻译的文本)\r\n */\r\n async translate(\r\n texts: Map<string, string[]>,\r\n sourceLocale: string,\r\n targetLocale: string,\r\n existingTranslations?: Record<string, string>,\r\n ): Promise<Map<string, Map<string, string>>> {\r\n if (!this.llm) {\r\n console.warn(pc.yellow(\"⚠️ 未配置 API Key,跳过翻译\"));\r\n // 返回原文作为翻译\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n\r\n const results = new Map<string, Map<string, string>>();\r\n const allTexts = Array.from(texts.values()).flat();\r\n const uniqueTexts = [...new Set(allTexts)];\r\n\r\n if (uniqueTexts.length === 0) return results;\r\n\r\n // 过滤出需要翻译的文本(未翻译的)\r\n const textsToTranslate: string[] = [];\r\n const cachedTranslations = new Map<string, string>();\r\n\r\n uniqueTexts.forEach((text) => {\r\n const key = this.generateKey(text);\r\n if (existingTranslations && existingTranslations[key]) {\r\n // 使用已有翻译\r\n cachedTranslations.set(text, existingTranslations[key]);\r\n } else {\r\n // 需要翻译\r\n textsToTranslate.push(text);\r\n }\r\n });\r\n\r\n console.log(pc.blue(\"📊 翻译统计:\"));\r\n console.log(` 总计: ${pc.cyan(uniqueTexts.length.toString())} 条`);\r\n console.log(\r\n ` ✅ 已有: ${pc.green(cachedTranslations.size.toString())} 条`,\r\n );\r\n console.log(\r\n ` 🆕 新增: ${pc.yellow(textsToTranslate.length.toString())} 条`,\r\n );\r\n\r\n try {\r\n // 只翻译新增的文本\r\n let newTranslations: string[] = [];\r\n if (textsToTranslate.length > 0) {\r\n console.log(pc.cyan(`\\n🤖 正在翻译新增文本...`));\r\n newTranslations = await this.batchTranslate(\r\n textsToTranslate,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n }\r\n\r\n // 创建完整的翻译映射(原文 -> 译文)\r\n const translationMap = new Map<string, string>(cachedTranslations);\r\n textsToTranslate.forEach((text, index) => {\r\n translationMap.set(text, newTranslations[index] || text);\r\n });\r\n\r\n // 按文件组织翻译结果\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => {\r\n textMap.set(text, translationMap.get(text) || text);\r\n });\r\n results.set(file, textMap);\r\n }\r\n\r\n return results;\r\n } catch (error: any) {\r\n console.error(pc.red(\"❌ 翻译失败:\"), error.message);\r\n // 返回原文\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n }\r\n\r\n /**\r\n * 生成翻译 key(与 generator 保持一致)\r\n */\r\n private generateKey(text: string): string {\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n\r\n /**\r\n * 批量翻译(分批处理避免超限)\r\n */\r\n private async batchTranslate(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const batchSize = 20;\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i += batchSize) {\r\n const batch = texts.slice(i, i + batchSize);\r\n const translated = await this.translateBatch(\r\n batch,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n results.push(...translated);\r\n\r\n // 显示进度\r\n const progress = Math.min(i + batchSize, texts.length);\r\n console.log(\r\n pc.gray(\r\n ` 📊 翻译进度: ${pc.cyan(progress.toString())}/${pc.cyan(\r\n texts.length.toString(),\r\n )}`,\r\n ),\r\n );\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 翻译一批文本\r\n */\r\n private async translateBatch(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const localeName = this.getLocaleName(targetLocale);\r\n\r\n const systemPrompt = new SystemMessage(\r\n `你是专业的软件本地化翻译专家。请将以下${this.getLocaleName(\r\n sourceLocale,\r\n )}文本翻译成${localeName}。\r\n要求:\r\n1. 保持专业术语的准确性\r\n2. 翻译要自然流畅,符合目标语言习惯\r\n3. 保留原文中的变量占位符(如 {name}、%s 等)\r\n4. 每行一个翻译,与输入顺序严格对应\r\n5. 只输出翻译结果,不要解释\r\n6. 如果原文包含换行,翻译也保持相同的换行`,\r\n );\r\n\r\n const userPrompt = new HumanMessage(\r\n texts.map((t, i) => `${i + 1}. ${t}`).join(\"\\n\"),\r\n );\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n const content = response.content.toString();\r\n\r\n // 解析翻译结果\r\n const lines = content.split(\"\\n\").filter((l) => l.trim());\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i++) {\r\n if (i < lines.length) {\r\n // 移除序号前缀\r\n const translated = lines[i].replace(/^\\d+\\.\\s*/, \"\").trim();\r\n results.push(translated);\r\n } else {\r\n // 如果翻译结果不够,使用原文\r\n results.push(texts[i]);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 获取语言名称\r\n */\r\n private getLocaleName(locale: string): string {\r\n const names: Record<string, string> = {\r\n \"zh-CN\": \"简体中文\",\r\n \"zh-TW\": \"繁体中文\",\r\n \"en-US\": \"英语\",\r\n \"ja-JP\": \"日语\",\r\n \"ko-KR\": \"韩语\",\r\n \"fr-FR\": \"法语\",\r\n \"de-DE\": \"德语\",\r\n \"es-ES\": \"西班牙语\",\r\n \"pt-BR\": \"葡萄牙语\",\r\n \"ru-RU\": \"俄语\",\r\n \"ar-SA\": \"阿拉伯语\",\r\n };\r\n return names[locale] || locale;\r\n }\r\n}\r\n","/**\r\n * i18n 语言文件生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface GeneratorOptions {\r\n localesDir: string;\r\n defaultLocale: string;\r\n}\r\n\r\nexport class I18nGenerator {\r\n private options: GeneratorOptions;\r\n\r\n constructor(options: GeneratorOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 生成语言文件\r\n */\r\n async generate(texts: Map<string, string[]>, locale: string): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象\r\n const translations: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n\r\n for (const [file, fileTexts] of texts) {\r\n for (const text of fileTexts) {\r\n const key = this.generateKey(text);\r\n if (!translations[key]) {\r\n translations[key] = text;\r\n newCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translations)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translations[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 生成语言文件(用于目标语言)- 支持增量翻译\r\n */\r\n async generateTranslated(\r\n originalTexts: Map<string, string[]>,\r\n translations: Map<string, Map<string, string>>,\r\n locale: string,\r\n ): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象(保留已有翻译)\r\n const translationsObj: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n let skippedCount = 0;\r\n\r\n for (const [file, textMap] of translations) {\r\n for (const [originalText, translatedText] of textMap) {\r\n const key = this.generateKey(originalText);\r\n if (!translationsObj[key]) {\r\n translationsObj[key] = translatedText;\r\n newCount++;\r\n } else {\r\n skippedCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translationsObj)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translationsObj[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` ✨ 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n if (skippedCount > 0) {\r\n console.log(pc.gray(` ⏭️ 跳过 ${pc.blue(skippedCount.toString())} 条已有翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 加载已有翻译\r\n */\r\n loadExistingTranslations(locale: string): Record<string, string> {\r\n const { localesDir } = this.options;\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n return {};\r\n }\r\n\r\n /**\r\n * 生成翻译 key\r\n */\r\n private generateKey(text: string): string {\r\n // 使用拼音首字母或简化文本作为 key\r\n // 这里使用简单的哈希方式\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n // 截取前 30 个字符\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n // 如果太短,添加哈希后缀\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n}\r\n"],"mappings":"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","names":["import_fs","import_path","import_picocolors","import_fs","import_path","path","fs","fs","path","pc","results","import_fs","import_path","import_picocolors","fs","path","pc","path","fs","pc"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/scanner.ts","../src/utils.ts","../src/translator.ts","../src/generator.ts"],"sourcesContent":["/**\r\n * AI 国际化助手插件入口\r\n *\r\n * 功能:\r\n * - 扫描源码中的中文文本\r\n * - 使用 AI 自动翻译\r\n * - 生成/更新 i18n 语言文件\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport { I18nScanner } from \"./scanner\";\r\nimport { I18nTranslator } from \"./translator\";\r\nimport { I18nGenerator } from \"./generator\";\r\n\r\nexport interface I18nPluginOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n // 扫描配置\r\n include?: string[];\r\n exclude?: string[];\r\n // 输出配置\r\n localesDir?: string;\r\n defaultLocale?: string;\r\n targetLocales?: string[];\r\n // 功能开关\r\n autoScan?: boolean;\r\n autoTranslate?: boolean;\r\n}\r\n\r\nexport function vitePluginAII18n(options: I18nPluginOptions = {}): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = \"gpt-4\",\r\n temperature = 0.3,\r\n maxTokens = 4000,\r\n include = [\"src/**/*.vue\", \"src/**/*.ts\"],\r\n exclude = [\"node_modules/**\", \"dist/**\"],\r\n localesDir = \"src/locales\",\r\n defaultLocale = \"zh-CN\",\r\n targetLocales = [\"en-US\"],\r\n autoScan = true,\r\n autoTranslate = true,\r\n } = options;\r\n\r\n const scanner = new I18nScanner({ include, exclude });\r\n const translator = new I18nTranslator({\r\n apiKey,\r\n apiUrl,\r\n model,\r\n temperature,\r\n maxTokens,\r\n });\r\n const generator = new I18nGenerator({ localesDir, defaultLocale });\r\n\r\n let scannedTexts: Map<string, string[]> = new Map();\r\n\r\n return {\r\n name: \"vite-plugin-ai-i18n\",\r\n enforce: \"pre\",\r\n\r\n configResolved(config) {\r\n // 🔥 关键:在最早期就创建空文件,确保 TypeScript 编译时文件存在\r\n const fullLocalesDir = path.resolve(config.root, localesDir);\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(fullLocalesDir)) {\r\n fs.mkdirSync(fullLocalesDir, { recursive: true });\r\n }\r\n\r\n // 为所有语言创建空文件(如果不存在)\r\n const allLocales = [defaultLocale, ...targetLocales];\r\n for (const locale of allLocales) {\r\n const filePath = path.join(fullLocalesDir, `${locale}.json`);\r\n if (!fs.existsSync(filePath)) {\r\n fs.writeFileSync(filePath, \"{}\", \"utf-8\");\r\n console.log(\r\n pc.green(\r\n `✅ 已生成 ${pc.cyan(locale)} 语言文件: ${pc.gray(\r\n `src\\\\locales\\\\${locale}.json`,\r\n )}`,\r\n ),\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.cyan(\"\\n🌍 AI 国际化助手已启动...\"));\r\n console.log(`📂 语言文件目录: ${pc.yellow(localesDir)}`);\r\n console.log(`🔤 默认语言: ${pc.cyan(defaultLocale)}`);\r\n console.log(`🎯 目标语言: ${pc.cyan(targetLocales.join(\", \"))}`);\r\n console.log(`🔍 自动扫描: ${autoScan ? pc.green(\"✅\") : pc.red(\"❌\")}`);\r\n console.log(\r\n `🤖 自动翻译: ${autoTranslate ? pc.green(\"✅\") : pc.red(\"❌\")}`,\r\n );\r\n console.log(\r\n `🔑 API Key: ${apiKey ? pc.green(\"已配置\") : pc.yellow(\"未配置\")}\\n`,\r\n );\r\n },\r\n\r\n async buildStart() {\r\n if (!autoScan) return;\r\n\r\n console.log(pc.cyan(\"🔍 正在扫描中文文本...\\n\"));\r\n scannedTexts = await scanner.scan();\r\n\r\n const totalTexts = Array.from(scannedTexts.values()).flat().length;\r\n console.log(\r\n pc.blue(`📝 发现 ${pc.yellow(totalTexts.toString())} 条待翻译文本\\n`),\r\n );\r\n\r\n if (totalTexts === 0) return;\r\n\r\n // 生成默认语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n // 自动翻译到目标语言\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n console.log(pc.cyan(`\\n🌐 正在翻译到 ${pc.yellow(locale)}...`));\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n scannedTexts,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n scannedTexts,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.green(\"\\n✨ 国际化处理完成\\n\"));\r\n },\r\n\r\n // 监听文件变化,增量更新\r\n async handleHotUpdate({ file, server }) {\r\n if (!autoScan) return;\r\n if (!file.match(/\\.(vue|ts|tsx)$/)) return;\r\n if (file.includes(\"node_modules\") || file.includes(localesDir)) return;\r\n\r\n console.log(`\\n🔄 检测到文件变化: ${file}`);\r\n const texts = scanner.scanFile(file);\r\n\r\n if (texts.length > 0) {\r\n console.log(`📝 发现 ${texts.length} 条新文本`);\r\n scannedTexts.set(file, texts);\r\n\r\n // 更新语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n const fileTextsMap = new Map([[file, texts]]);\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n fileTextsMap,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n fileTextsMap,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAII18n;\r\n","/**\r\n * 中文文本扫描器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { glob } from \"./utils\";\r\n\r\nexport interface ScannerOptions {\r\n include: string[];\r\n exclude: string[];\r\n debug?: boolean; // 调试模式\r\n}\r\n\r\nexport class I18nScanner {\r\n private options: ScannerOptions;\r\n\r\n // 匹配中文字符的正则\r\n private chineseRegex = /[\\u4e00-\\u9fa5]+[^\\n<>{}]*[\\u4e00-\\u9fa5]*/g;\r\n\r\n // 需要忽略的模式\r\n private ignorePatterns = [\r\n /console\\.(log|warn|error|info)/,\r\n /\\/\\/.*$/,\r\n /\\/\\*[\\s\\S]*?\\*\\//,\r\n /<!--[\\s\\S]*?-->/,\r\n ];\r\n\r\n constructor(options: ScannerOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 扫描所有匹配的文件\r\n */\r\n async scan(): Promise<Map<string, string[]>> {\r\n const results = new Map<string, string[]>();\r\n const files = await glob(this.options.include, this.options.exclude);\r\n\r\n for (const file of files) {\r\n const texts = this.scanFile(file);\r\n if (texts.length > 0) {\r\n results.set(file, texts);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 扫描单个文件\r\n */\r\n scanFile(filePath: string): string[] {\r\n if (!fs.existsSync(filePath)) return [];\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n const ext = path.extname(filePath);\r\n\r\n let texts: string[] = [];\r\n\r\n if (ext === \".vue\") {\r\n texts = this.scanVueFile(content);\r\n } else if ([\".ts\", \".tsx\", \".js\", \".jsx\"].includes(ext)) {\r\n texts = this.scanScriptFile(content);\r\n }\r\n\r\n // 使用 Set 去重并过滤(性能优化)\r\n const uniqueTexts = new Set(texts);\r\n return Array.from(uniqueTexts).filter((t) => this.isValidText(t));\r\n }\r\n\r\n /**\r\n * 扫描 Vue 文件\r\n */\r\n private scanVueFile(content: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 扫描 template 部分\r\n const templateMatch = content.match(\r\n /<template[^>]*>([\\s\\S]*?)<\\/template>/\r\n );\r\n if (templateMatch) {\r\n texts.push(...this.extractChineseFromTemplate(templateMatch[1]));\r\n }\r\n\r\n // 扫描 script 部分\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (scriptMatch) {\r\n texts.push(...this.extractChineseFromScript(scriptMatch[1]));\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 扫描脚本文件\r\n */\r\n private scanScriptFile(content: string): string[] {\r\n return this.extractChineseFromScript(content);\r\n }\r\n\r\n /**\r\n * 安全地移除注释(不影响字符串中的内容)\r\n */\r\n private removeComments(code: string): string {\r\n let result = \"\";\r\n let inString = false;\r\n let stringChar = \"\";\r\n let inBlockComment = false;\r\n let inLineComment = false;\r\n\r\n for (let i = 0; i < code.length; i++) {\r\n const char = code[i];\r\n const nextChar = code[i + 1];\r\n const prevChar = code[i - 1];\r\n\r\n // 处理字符串\r\n if (!inBlockComment && !inLineComment) {\r\n if (\r\n (char === '\"' || char === \"'\" || char === \"`\") &&\r\n prevChar !== \"\\\\\"\r\n ) {\r\n if (!inString) {\r\n inString = true;\r\n stringChar = char;\r\n } else if (char === stringChar) {\r\n inString = false;\r\n }\r\n }\r\n }\r\n\r\n // 在字符串中,保留所有字符\r\n if (inString) {\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 处理块注释\r\n if (char === \"/\" && nextChar === \"*\" && !inLineComment) {\r\n inBlockComment = true;\r\n i++; // 跳过 *\r\n continue;\r\n }\r\n if (char === \"*\" && nextChar === \"/\" && inBlockComment) {\r\n inBlockComment = false;\r\n i++; // 跳过 /\r\n continue;\r\n }\r\n\r\n // 处理行注释\r\n if (char === \"/\" && nextChar === \"/\" && !inBlockComment) {\r\n inLineComment = true;\r\n i++; // 跳过第二个 /\r\n continue;\r\n }\r\n if (char === \"\\n\" && inLineComment) {\r\n inLineComment = false;\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 跳过注释内容\r\n if (inBlockComment || inLineComment) {\r\n continue;\r\n }\r\n\r\n result += char;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * 从模板中提取中文\r\n */\r\n private extractChineseFromTemplate(template: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 移除注释\r\n let cleaned = template.replace(/<!--[\\s\\S]*?-->/g, \"\");\r\n\r\n // 1. 提取 t() 或 $t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /(?:\\$t|\\bt)\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取标签内的纯文本(不包含插值表达式)\r\n const tagTextRegex = />([^<{]+)</g;\r\n while ((match = tagTextRegex.exec(cleaned)) !== null) {\r\n const text = match[1].trim();\r\n if (text && /[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 3. 提取插值中的字符串字面量(不包括 t() 调用)\r\n const interpolationRegex = /\\{\\{\\s*[\"']([^\"']+)[\"']\\s*\\}\\}/g;\r\n while ((match = interpolationRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 4. 提取静态属性中的中文\r\n const staticAttrRegex =\r\n /(?:placeholder|title|label|alt|content|aria-label)=[\"']([^\"']*[\\u4e00-\\u9fa5][^\"']*)[\"']/g;\r\n while ((match = staticAttrRegex.exec(cleaned)) !== null) {\r\n texts.push(match[1]);\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 从脚本中提取中文\r\n */\r\n private extractChineseFromScript(script: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 安全地移除注释\r\n const cleaned = this.removeComments(script);\r\n\r\n // 1. 提取 t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /\\bt\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取所有字符串字面量(不在 t() 中的)\r\n const allStrings: string[] = [];\r\n\r\n // 单引号字符串\r\n const singleQuoteRegex = /'([^'\\\\]*(\\\\.[^'\\\\]*)*)'/g;\r\n while ((match = singleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 双引号字符串\r\n const doubleQuoteRegex = /\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"/g;\r\n while ((match = doubleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 模板字符串(简单情况,不包含插值)\r\n const templateRegex = /`([^`$\\\\]*(\\\\.[^`$\\\\]*)*)`/g;\r\n while ((match = templateRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 3. 过滤出包含中文的字符串\r\n for (const str of allStrings) {\r\n if (/[\\u4e00-\\u9fa5]/.test(str)) {\r\n texts.push(str);\r\n }\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 验证文本是否有效\r\n */\r\n private isValidText(text: string): boolean {\r\n const debug = this.options.debug;\r\n\r\n // 1. 基础过滤\r\n if (text.length < 2) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本太短`);\r\n return false;\r\n }\r\n\r\n if (!/[\\u4e00-\\u9fa5]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 不包含中文`);\r\n return false;\r\n }\r\n\r\n if (/^\\s*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯空格`);\r\n return false;\r\n }\r\n\r\n // 2. 过滤 i18n 相关\r\n if (/^\\$t\\(|^t\\(|^i18n\\.|_uni_app$|^[a-z_]+_[a-z_]+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: i18n key`);\r\n return false;\r\n }\r\n\r\n // 3. 过滤系统提示信息\r\n if (/^[⚠️❌✅🔍📝💡🎯🚀🔧📊]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 系统提示`);\r\n return false;\r\n }\r\n\r\n // 4. 过滤技术术语\r\n if (\r\n /\\.(json|js|ts|vue|md|txt|html|css|jsx|tsx)\\s*(文件|不存在|已|错误)/.test(\r\n text\r\n )\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术术语`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9_\\-\\.]+\\s*(文件|不存在|错误|失败)/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术错误信息`);\r\n return false;\r\n }\r\n\r\n // 5. 过滤变量名和路径\r\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 变量名`);\r\n return false;\r\n }\r\n\r\n if (/^\\/[a-zA-Z0-9_\\-\\/]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 路径`);\r\n return false;\r\n }\r\n\r\n // 6. 过滤 URL 和邮箱\r\n if (/^https?:\\/\\//.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: URL`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 邮箱`);\r\n return false;\r\n }\r\n\r\n // 7. 过滤纯数字和日期\r\n if (/^\\d+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯数字`);\r\n return false;\r\n }\r\n\r\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 日期`);\r\n return false;\r\n }\r\n\r\n // 8. 过滤代码片段\r\n if (\r\n /^(const|let|var|function|class|import|export|return)\\s/.test(text)\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 代码片段`);\r\n return false;\r\n }\r\n\r\n // 9. 过滤过长的文本(可能是代码)\r\n if (text.length > 100) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本过长`);\r\n return false;\r\n }\r\n\r\n // 10. 过滤包含特殊字符过多的文本\r\n const specialCharCount = (\r\n text.match(/[^\\u4e00-\\u9fa5a-zA-Z0-9\\s,。!?、;:\"\"''()《》]/g) || []\r\n ).length;\r\n if (specialCharCount > text.length * 0.3) {\r\n if (debug)\r\n console.log(`[过滤] \"${text}\" - 原因: 特殊字符过多 (${specialCharCount})`);\r\n return false;\r\n }\r\n\r\n if (debug) console.log(`[保留] \"${text}\"`);\r\n return true;\r\n }\r\n}\r\n","/**\r\n * i18n 插件工具函数\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\n\r\n/**\r\n * 简单的 glob 实现\r\n */\r\nexport async function glob(\r\n patterns: string[],\r\n excludePatterns: string[]\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n const cwd = process.cwd();\r\n\r\n for (const pattern of patterns) {\r\n const files = await matchPattern(pattern, cwd);\r\n results.push(...files);\r\n }\r\n\r\n // 过滤排除的文件\r\n return results.filter((file) => {\r\n const relativePath = path.relative(cwd, file);\r\n return !excludePatterns.some((p) => matchGlob(relativePath, p));\r\n });\r\n}\r\n\r\n/**\r\n * 匹配单个 glob 模式\r\n */\r\nasync function matchPattern(pattern: string, cwd: string): Promise<string[]> {\r\n const results: string[] = [];\r\n\r\n // 解析模式\r\n const parts = pattern.split(\"/\");\r\n const hasGlobstar = parts.includes(\"**\");\r\n\r\n if (hasGlobstar) {\r\n // 递归搜索\r\n const baseDir = parts.slice(0, parts.indexOf(\"**\")).join(\"/\") || \".\";\r\n const filePattern = parts.slice(parts.indexOf(\"**\") + 1).join(\"/\");\r\n await walkDir(path.join(cwd, baseDir), filePattern, results);\r\n } else {\r\n // 直接匹配\r\n const fullPath = path.join(cwd, pattern);\r\n if (fs.existsSync(fullPath)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * 递归遍历目录\r\n */\r\nasync function walkDir(\r\n dir: string,\r\n pattern: string,\r\n results: string[]\r\n): Promise<void> {\r\n if (!fs.existsSync(dir)) return;\r\n\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n // 跳过 node_modules 和隐藏目录\r\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) {\r\n continue;\r\n }\r\n await walkDir(fullPath, pattern, results);\r\n } else if (entry.isFile()) {\r\n if (matchGlob(entry.name, pattern)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 简单的 glob 匹配\r\n */\r\nfunction matchGlob(str: string, pattern: string): boolean {\r\n // 转换 glob 模式为正则\r\n const regexPattern = pattern\r\n .replace(/\\./g, \"\\\\.\")\r\n .replace(/\\*\\*/g, \".*\")\r\n .replace(/\\*/g, \"[^/]*\")\r\n .replace(/\\?/g, \".\");\r\n\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(str);\r\n}\r\n\r\n/**\r\n * 生成唯一 ID\r\n */\r\nexport function generateId(): string {\r\n return Math.random().toString(36).slice(2, 10);\r\n}\r\n\r\n/**\r\n * 格式化文件大小\r\n */\r\nexport function formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n}\r\n","/**\r\n * AI 翻译器\r\n */\r\n\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface TranslatorOptions {\r\n apiKey: string;\r\n apiUrl: string;\r\n model: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n}\r\n\r\nexport class I18nTranslator {\r\n private llm: ChatOpenAI | null = null;\r\n private options: TranslatorOptions;\r\n\r\n constructor(options: TranslatorOptions) {\r\n this.options = options;\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: options.temperature ?? 0.3,\r\n maxTokens: options.maxTokens ?? 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 批量翻译文本 - 支持增量翻译(跳过已翻译的文本)\r\n */\r\n async translate(\r\n texts: Map<string, string[]>,\r\n sourceLocale: string,\r\n targetLocale: string,\r\n existingTranslations?: Record<string, string>,\r\n ): Promise<Map<string, Map<string, string>>> {\r\n if (!this.llm) {\r\n console.warn(pc.yellow(\"⚠️ 未配置 API Key,跳过翻译\"));\r\n // 返回原文作为翻译\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n\r\n const results = new Map<string, Map<string, string>>();\r\n const allTexts = Array.from(texts.values()).flat();\r\n const uniqueTexts = [...new Set(allTexts)];\r\n\r\n if (uniqueTexts.length === 0) return results;\r\n\r\n // 过滤出需要翻译的文本(未翻译的)\r\n const textsToTranslate: string[] = [];\r\n const cachedTranslations = new Map<string, string>();\r\n\r\n uniqueTexts.forEach((text) => {\r\n const key = this.generateKey(text);\r\n if (existingTranslations && existingTranslations[key]) {\r\n // 使用已有翻译\r\n cachedTranslations.set(text, existingTranslations[key]);\r\n } else {\r\n // 需要翻译\r\n textsToTranslate.push(text);\r\n }\r\n });\r\n\r\n console.log(pc.blue(\"📊 翻译统计:\"));\r\n console.log(` 总计: ${pc.cyan(uniqueTexts.length.toString())} 条`);\r\n console.log(\r\n ` ✅ 已有: ${pc.green(cachedTranslations.size.toString())} 条`,\r\n );\r\n console.log(\r\n ` 🆕 新增: ${pc.yellow(textsToTranslate.length.toString())} 条`,\r\n );\r\n\r\n try {\r\n // 只翻译新增的文本\r\n let newTranslations: string[] = [];\r\n if (textsToTranslate.length > 0) {\r\n console.log(pc.cyan(`\\n🤖 正在翻译新增文本...`));\r\n newTranslations = await this.batchTranslate(\r\n textsToTranslate,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n }\r\n\r\n // 创建完整的翻译映射(原文 -> 译文)\r\n const translationMap = new Map<string, string>(cachedTranslations);\r\n textsToTranslate.forEach((text, index) => {\r\n translationMap.set(text, newTranslations[index] || text);\r\n });\r\n\r\n // 按文件组织翻译结果\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => {\r\n textMap.set(text, translationMap.get(text) || text);\r\n });\r\n results.set(file, textMap);\r\n }\r\n\r\n return results;\r\n } catch (error: any) {\r\n console.error(pc.red(\"❌ 翻译失败:\"), error.message);\r\n // 返回原文\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n }\r\n\r\n /**\r\n * 生成翻译 key(与 generator 保持一致)\r\n */\r\n private generateKey(text: string): string {\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n\r\n /**\r\n * 批量翻译(分批处理避免超限)\r\n */\r\n private async batchTranslate(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const batchSize = 20;\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i += batchSize) {\r\n const batch = texts.slice(i, i + batchSize);\r\n const translated = await this.translateBatch(\r\n batch,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n results.push(...translated);\r\n\r\n // 显示进度\r\n const progress = Math.min(i + batchSize, texts.length);\r\n console.log(\r\n pc.gray(\r\n ` 📊 翻译进度: ${pc.cyan(progress.toString())}/${pc.cyan(\r\n texts.length.toString(),\r\n )}`,\r\n ),\r\n );\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 翻译一批文本\r\n */\r\n private async translateBatch(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const localeName = this.getLocaleName(targetLocale);\r\n\r\n const systemPrompt = new SystemMessage(\r\n `你是专业的软件本地化翻译专家。请将以下${this.getLocaleName(\r\n sourceLocale,\r\n )}文本翻译成${localeName}。\r\n要求:\r\n1. 保持专业术语的准确性\r\n2. 翻译要自然流畅,符合目标语言习惯\r\n3. 保留原文中的变量占位符(如 {name}、%s 等)\r\n4. 每行一个翻译,与输入顺序严格对应\r\n5. 只输出翻译结果,不要解释\r\n6. 如果原文包含换行,翻译也保持相同的换行`,\r\n );\r\n\r\n const userPrompt = new HumanMessage(\r\n texts.map((t, i) => `${i + 1}. ${t}`).join(\"\\n\"),\r\n );\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n const content = response.content.toString();\r\n\r\n // 解析翻译结果\r\n const lines = content.split(\"\\n\").filter((l) => l.trim());\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i++) {\r\n if (i < lines.length) {\r\n // 移除序号前缀\r\n const translated = lines[i].replace(/^\\d+\\.\\s*/, \"\").trim();\r\n results.push(translated);\r\n } else {\r\n // 如果翻译结果不够,使用原文\r\n results.push(texts[i]);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 获取语言名称\r\n */\r\n private getLocaleName(locale: string): string {\r\n const names: Record<string, string> = {\r\n \"zh-CN\": \"简体中文\",\r\n \"zh-TW\": \"繁体中文\",\r\n \"en-US\": \"英语\",\r\n \"ja-JP\": \"日语\",\r\n \"ko-KR\": \"韩语\",\r\n \"fr-FR\": \"法语\",\r\n \"de-DE\": \"德语\",\r\n \"es-ES\": \"西班牙语\",\r\n \"pt-BR\": \"葡萄牙语\",\r\n \"ru-RU\": \"俄语\",\r\n \"ar-SA\": \"阿拉伯语\",\r\n };\r\n return names[locale] || locale;\r\n }\r\n}\r\n","/**\r\n * i18n 语言文件生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface GeneratorOptions {\r\n localesDir: string;\r\n defaultLocale: string;\r\n}\r\n\r\nexport class I18nGenerator {\r\n private options: GeneratorOptions;\r\n\r\n constructor(options: GeneratorOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 生成语言文件\r\n */\r\n async generate(texts: Map<string, string[]>, locale: string): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象\r\n const translations: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n\r\n for (const [file, fileTexts] of texts) {\r\n for (const text of fileTexts) {\r\n const key = this.generateKey(text);\r\n if (!translations[key]) {\r\n translations[key] = text;\r\n newCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translations)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translations[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 生成语言文件(用于目标语言)- 支持增量翻译\r\n */\r\n async generateTranslated(\r\n originalTexts: Map<string, string[]>,\r\n translations: Map<string, Map<string, string>>,\r\n locale: string,\r\n ): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象(保留已有翻译)\r\n const translationsObj: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n let skippedCount = 0;\r\n\r\n for (const [file, textMap] of translations) {\r\n for (const [originalText, translatedText] of textMap) {\r\n const key = this.generateKey(originalText);\r\n if (!translationsObj[key]) {\r\n translationsObj[key] = translatedText;\r\n newCount++;\r\n } else {\r\n skippedCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translationsObj)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translationsObj[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` ✨ 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n if (skippedCount > 0) {\r\n console.log(pc.gray(` ⏭️ 跳过 ${pc.blue(skippedCount.toString())} 条已有翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 加载已有翻译\r\n */\r\n loadExistingTranslations(locale: string): Record<string, string> {\r\n const { localesDir } = this.options;\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n return {};\r\n }\r\n\r\n /**\r\n * 生成翻译 key\r\n */\r\n private generateKey(text: string): string {\r\n // 使用拼音首字母或简化文本作为 key\r\n // 这里使用简单的哈希方式\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n // 截取前 30 个字符\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n // 如果太短,添加哈希后缀\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,IAAAA,aAAe;AACf,IAAAC,eAAiB;AACjB,IAAAC,qBAAe;;;ACRf,IAAAC,aAAe;AACf,IAAAC,eAAiB;;;ACDjB,gBAAe;AACf,kBAAiB;AAKjB,eAAsB,KACpB,UACA,iBACmB;AACnB,QAAM,UAAoB,CAAC;AAC3B,QAAM,MAAM,QAAQ,IAAI;AAExB,aAAW,WAAW,UAAU;AAC9B,UAAM,QAAQ,MAAM,aAAa,SAAS,GAAG;AAC7C,YAAQ,KAAK,GAAG,KAAK;AAAA,EACvB;AAGA,SAAO,QAAQ,OAAO,CAAC,SAAS;AAC9B,UAAM,eAAe,YAAAC,QAAK,SAAS,KAAK,IAAI;AAC5C,WAAO,CAAC,gBAAgB,KAAK,CAAC,MAAM,UAAU,cAAc,CAAC,CAAC;AAAA,EAChE,CAAC;AACH;AAKA,eAAe,aAAa,SAAiB,KAAgC;AAC3E,QAAM,UAAoB,CAAC;AAG3B,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,QAAM,cAAc,MAAM,SAAS,IAAI;AAEvC,MAAI,aAAa;AAEf,UAAM,UAAU,MAAM,MAAM,GAAG,MAAM,QAAQ,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK;AACjE,UAAM,cAAc,MAAM,MAAM,MAAM,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,GAAG;AACjE,UAAM,QAAQ,YAAAA,QAAK,KAAK,KAAK,OAAO,GAAG,aAAa,OAAO;AAAA,EAC7D,OAAO;AAEL,UAAM,WAAW,YAAAA,QAAK,KAAK,KAAK,OAAO;AACvC,QAAI,UAAAC,QAAG,WAAW,QAAQ,GAAG;AAC3B,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAe,QACb,KACA,SACA,SACe;AACf,MAAI,CAAC,UAAAA,QAAG,WAAW,GAAG,EAAG;AAEzB,QAAM,UAAU,UAAAA,QAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAE3D,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAW,YAAAD,QAAK,KAAK,KAAK,MAAM,IAAI;AAE1C,QAAI,MAAM,YAAY,GAAG;AAEvB,UAAI,MAAM,SAAS,kBAAkB,MAAM,KAAK,WAAW,GAAG,GAAG;AAC/D;AAAA,MACF;AACA,YAAM,QAAQ,UAAU,SAAS,OAAO;AAAA,IAC1C,WAAW,MAAM,OAAO,GAAG;AACzB,UAAI,UAAU,MAAM,MAAM,OAAO,GAAG;AAClC,gBAAQ,KAAK,QAAQ;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;AAKA,SAAS,UAAU,KAAa,SAA0B;AAExD,QAAM,eAAe,QAClB,QAAQ,OAAO,KAAK,EACpB,QAAQ,SAAS,IAAI,EACrB,QAAQ,OAAO,OAAO,EACtB,QAAQ,OAAO,GAAG;AAErB,QAAM,QAAQ,IAAI,OAAO,IAAI,YAAY,GAAG;AAC5C,SAAO,MAAM,KAAK,GAAG;AACvB;;;ADnFO,IAAM,cAAN,MAAkB;AAAA,EAcvB,YAAY,SAAyB;AAVrC;AAAA,SAAQ,eAAe;AAGvB;AAAA,SAAQ,iBAAiB;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGE,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAuC;AAC3C,UAAM,UAAU,oBAAI,IAAsB;AAC1C,UAAM,QAAQ,MAAM,KAAK,KAAK,QAAQ,SAAS,KAAK,QAAQ,OAAO;AAEnE,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,KAAK,SAAS,IAAI;AAChC,UAAI,MAAM,SAAS,GAAG;AACpB,gBAAQ,IAAI,MAAM,KAAK;AAAA,MACzB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,UAA4B;AACnC,QAAI,CAAC,WAAAE,QAAG,WAAW,QAAQ,EAAG,QAAO,CAAC;AAEtC,UAAM,UAAU,WAAAA,QAAG,aAAa,UAAU,OAAO;AACjD,UAAM,MAAM,aAAAC,QAAK,QAAQ,QAAQ;AAEjC,QAAI,QAAkB,CAAC;AAEvB,QAAI,QAAQ,QAAQ;AAClB,cAAQ,KAAK,YAAY,OAAO;AAAA,IAClC,WAAW,CAAC,OAAO,QAAQ,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AACvD,cAAQ,KAAK,eAAe,OAAO;AAAA,IACrC;AAGA,UAAM,cAAc,IAAI,IAAI,KAAK;AACjC,WAAO,MAAM,KAAK,WAAW,EAAE,OAAO,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,SAA2B;AAC7C,UAAM,QAAkB,CAAC;AAGzB,UAAM,gBAAgB,QAAQ;AAAA,MAC5B;AAAA,IACF;AACA,QAAI,eAAe;AACjB,YAAM,KAAK,GAAG,KAAK,2BAA2B,cAAc,CAAC,CAAC,CAAC;AAAA,IACjE;AAGA,UAAM,cAAc,QAAQ,MAAM,mCAAmC;AACrE,QAAI,aAAa;AACf,YAAM,KAAK,GAAG,KAAK,yBAAyB,YAAY,CAAC,CAAC,CAAC;AAAA,IAC7D;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,SAA2B;AAChD,WAAO,KAAK,yBAAyB,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,MAAsB;AAC3C,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,aAAa;AACjB,QAAI,iBAAiB;AACrB,QAAI,gBAAgB;AAEpB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,WAAW,KAAK,IAAI,CAAC;AAC3B,YAAM,WAAW,KAAK,IAAI,CAAC;AAG3B,UAAI,CAAC,kBAAkB,CAAC,eAAe;AACrC,aACG,SAAS,OAAO,SAAS,OAAO,SAAS,QAC1C,aAAa,MACb;AACA,cAAI,CAAC,UAAU;AACb,uBAAW;AACX,yBAAa;AAAA,UACf,WAAW,SAAS,YAAY;AAC9B,uBAAW;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAGA,UAAI,UAAU;AACZ,kBAAU;AACV;AAAA,MACF;AAGA,UAAI,SAAS,OAAO,aAAa,OAAO,CAAC,eAAe;AACtD,yBAAiB;AACjB;AACA;AAAA,MACF;AACA,UAAI,SAAS,OAAO,aAAa,OAAO,gBAAgB;AACtD,yBAAiB;AACjB;AACA;AAAA,MACF;AAGA,UAAI,SAAS,OAAO,aAAa,OAAO,CAAC,gBAAgB;AACvD,wBAAgB;AAChB;AACA;AAAA,MACF;AACA,UAAI,SAAS,QAAQ,eAAe;AAClC,wBAAgB;AAChB,kBAAU;AACV;AAAA,MACF;AAGA,UAAI,kBAAkB,eAAe;AACnC;AAAA,MACF;AAEA,gBAAU;AAAA,IACZ;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,2BAA2B,UAA4B;AAC7D,UAAM,QAAkB,CAAC;AAGzB,QAAI,UAAU,SAAS,QAAQ,oBAAoB,EAAE;AAGrD,UAAM,iBAAiB;AACvB,QAAI;AACJ,YAAQ,QAAQ,eAAe,KAAK,OAAO,OAAO,MAAM;AACtD,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,kBAAkB,KAAK,IAAI,GAAG;AAChC,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAGA,UAAM,eAAe;AACrB,YAAQ,QAAQ,aAAa,KAAK,OAAO,OAAO,MAAM;AACpD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,UAAI,QAAQ,kBAAkB,KAAK,IAAI,GAAG;AACxC,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAGA,UAAM,qBAAqB;AAC3B,YAAQ,QAAQ,mBAAmB,KAAK,OAAO,OAAO,MAAM;AAC1D,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,kBAAkB,KAAK,IAAI,GAAG;AAChC,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAGA,UAAM,kBACJ;AACF,YAAQ,QAAQ,gBAAgB,KAAK,OAAO,OAAO,MAAM;AACvD,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,yBAAyB,QAA0B;AACzD,UAAM,QAAkB,CAAC;AAGzB,UAAM,UAAU,KAAK,eAAe,MAAM;AAG1C,UAAM,iBAAiB;AACvB,QAAI;AACJ,YAAQ,QAAQ,eAAe,KAAK,OAAO,OAAO,MAAM;AACtD,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,kBAAkB,KAAK,IAAI,GAAG;AAChC,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAGA,UAAM,aAAuB,CAAC;AAG9B,UAAM,mBAAmB;AACzB,YAAQ,QAAQ,iBAAiB,KAAK,OAAO,OAAO,MAAM;AACxD,iBAAW,KAAK,MAAM,CAAC,CAAC;AAAA,IAC1B;AAGA,UAAM,mBAAmB;AACzB,YAAQ,QAAQ,iBAAiB,KAAK,OAAO,OAAO,MAAM;AACxD,iBAAW,KAAK,MAAM,CAAC,CAAC;AAAA,IAC1B;AAGA,UAAM,gBAAgB;AACtB,YAAQ,QAAQ,cAAc,KAAK,OAAO,OAAO,MAAM;AACrD,iBAAW,KAAK,MAAM,CAAC,CAAC;AAAA,IAC1B;AAGA,eAAW,OAAO,YAAY;AAC5B,UAAI,kBAAkB,KAAK,GAAG,GAAG;AAC/B,cAAM,KAAK,GAAG;AAAA,MAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAAuB;AACzC,UAAM,QAAQ,KAAK,QAAQ;AAG3B,QAAI,KAAK,SAAS,GAAG;AACnB,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,cAAc;AAClD,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,kBAAkB,KAAK,IAAI,GAAG;AACjC,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,eAAe;AACnD,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,KAAK,IAAI,GAAG;AACtB,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,aAAa;AACjD,aAAO;AAAA,IACT;AAGA,QAAI,kDAAkD,KAAK,IAAI,GAAG;AAChE,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,kBAAkB;AACtD,aAAO;AAAA,IACT;AAGA,QAAI,wBAAwB,KAAK,IAAI,GAAG;AACtC,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,cAAc;AAClD,aAAO;AAAA,IACT;AAGA,QACE,6DAA6D;AAAA,MAC3D;AAAA,IACF,GACA;AACA,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,cAAc;AAClD,aAAO;AAAA,IACT;AAEA,QAAI,sCAAsC,KAAK,IAAI,GAAG;AACpD,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,gBAAgB;AACpD,aAAO;AAAA,IACT;AAGA,QAAI,6BAA6B,KAAK,IAAI,GAAG;AAC3C,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,aAAa;AACjD,aAAO;AAAA,IACT;AAEA,QAAI,wBAAwB,KAAK,IAAI,GAAG;AACtC,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,YAAY;AAChD,aAAO;AAAA,IACT;AAGA,QAAI,eAAe,KAAK,IAAI,GAAG;AAC7B,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,aAAa;AACjD,aAAO;AAAA,IACT;AAEA,QAAI,mDAAmD,KAAK,IAAI,GAAG;AACjE,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,YAAY;AAChD,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,KAAK,IAAI,GAAG;AACtB,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,aAAa;AACjD,aAAO;AAAA,IACT;AAEA,QAAI,qBAAqB,KAAK,IAAI,GAAG;AACnC,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,YAAY;AAChD,aAAO;AAAA,IACT;AAGA,QACE,yDAAyD,KAAK,IAAI,GAClE;AACA,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,cAAc;AAClD,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,SAAS,KAAK;AACrB,UAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,cAAc;AAClD,aAAO;AAAA,IACT;AAGA,UAAM,oBACJ,KAAK,MAAM,6CAA6C,KAAK,CAAC,GAC9D;AACF,QAAI,mBAAmB,KAAK,SAAS,KAAK;AACxC,UAAI;AACF,gBAAQ,IAAI,SAAS,IAAI,mBAAmB,gBAAgB,GAAG;AACjE,aAAO;AAAA,IACT;AAEA,QAAI,MAAO,SAAQ,IAAI,SAAS,IAAI,GAAG;AACvC,WAAO;AAAA,EACT;AACF;;;AEtXA,oBAA2B;AAC3B,sBAA4C;AAC5C,wBAAe;AAUR,IAAM,iBAAN,MAAqB;AAAA,EAI1B,YAAY,SAA4B;AAHxC,SAAQ,MAAyB;AAI/B,SAAK,UAAU;AAEf,QAAI,QAAQ,QAAQ;AAClB,WAAK,MAAM,IAAI,yBAAW;AAAA,QACxB,cAAc,QAAQ;AAAA,QACtB,eAAe,EAAE,SAAS,QAAQ,OAAO;AAAA,QACzC,WAAW,QAAQ;AAAA,QACnB,aAAa,QAAQ,eAAe;AAAA,QACpC,WAAW,QAAQ,aAAa;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UACJ,OACA,cACA,cACA,sBAC2C;AAC3C,QAAI,CAAC,KAAK,KAAK;AACb,cAAQ,KAAK,kBAAAC,QAAG,OAAO,sBAAsB,CAAC;AAE9C,YAAMC,WAAU,oBAAI,IAAiC;AACrD,iBAAW,CAAC,MAAM,SAAS,KAAK,OAAO;AACrC,cAAM,UAAU,oBAAI,IAAoB;AACxC,kBAAU,QAAQ,CAAC,SAAS,QAAQ,IAAI,MAAM,IAAI,CAAC;AACnD,QAAAA,SAAQ,IAAI,MAAM,OAAO;AAAA,MAC3B;AACA,aAAOA;AAAA,IACT;AAEA,UAAM,UAAU,oBAAI,IAAiC;AACrD,UAAM,WAAW,MAAM,KAAK,MAAM,OAAO,CAAC,EAAE,KAAK;AACjD,UAAM,cAAc,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAEzC,QAAI,YAAY,WAAW,EAAG,QAAO;AAGrC,UAAM,mBAA6B,CAAC;AACpC,UAAM,qBAAqB,oBAAI,IAAoB;AAEnD,gBAAY,QAAQ,CAAC,SAAS;AAC5B,YAAM,MAAM,KAAK,YAAY,IAAI;AACjC,UAAI,wBAAwB,qBAAqB,GAAG,GAAG;AAErD,2BAAmB,IAAI,MAAM,qBAAqB,GAAG,CAAC;AAAA,MACxD,OAAO;AAEL,yBAAiB,KAAK,IAAI;AAAA,MAC5B;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,kBAAAD,QAAG,KAAK,UAAU,CAAC;AAC/B,YAAQ,IAAI,UAAU,kBAAAA,QAAG,KAAK,YAAY,OAAO,SAAS,CAAC,CAAC,IAAI;AAChE,YAAQ;AAAA,MACN,YAAY,kBAAAA,QAAG,MAAM,mBAAmB,KAAK,SAAS,CAAC,CAAC;AAAA,IAC1D;AACA,YAAQ;AAAA,MACN,aAAa,kBAAAA,QAAG,OAAO,iBAAiB,OAAO,SAAS,CAAC,CAAC;AAAA,IAC5D;AAEA,QAAI;AAEF,UAAI,kBAA4B,CAAC;AACjC,UAAI,iBAAiB,SAAS,GAAG;AAC/B,gBAAQ,IAAI,kBAAAA,QAAG,KAAK;AAAA,eAAkB,CAAC;AACvC,0BAAkB,MAAM,KAAK;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,YAAM,iBAAiB,IAAI,IAAoB,kBAAkB;AACjE,uBAAiB,QAAQ,CAAC,MAAM,UAAU;AACxC,uBAAe,IAAI,MAAM,gBAAgB,KAAK,KAAK,IAAI;AAAA,MACzD,CAAC;AAGD,iBAAW,CAAC,MAAM,SAAS,KAAK,OAAO;AACrC,cAAM,UAAU,oBAAI,IAAoB;AACxC,kBAAU,QAAQ,CAAC,SAAS;AAC1B,kBAAQ,IAAI,MAAM,eAAe,IAAI,IAAI,KAAK,IAAI;AAAA,QACpD,CAAC;AACD,gBAAQ,IAAI,MAAM,OAAO;AAAA,MAC3B;AAEA,aAAO;AAAA,IACT,SAAS,OAAY;AACnB,cAAQ,MAAM,kBAAAA,QAAG,IAAI,SAAS,GAAG,MAAM,OAAO;AAE9C,YAAMC,WAAU,oBAAI,IAAiC;AACrD,iBAAW,CAAC,MAAM,SAAS,KAAK,OAAO;AACrC,cAAM,UAAU,oBAAI,IAAoB;AACxC,kBAAU,QAAQ,CAAC,SAAS,QAAQ,IAAI,MAAM,IAAI,CAAC;AACnD,QAAAA,SAAQ,IAAI,MAAM,OAAO;AAAA,MAC3B;AACA,aAAOA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAAsB;AACxC,UAAM,UAAU,KACb,QAAQ,8BAA8B,GAAG,EACzC,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE,EACpB,YAAY;AAEf,UAAM,YAAY,QAAQ,MAAM,GAAG,EAAE;AAErC,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,OAAO,KAAK,WAAW,IAAI;AACjC,aAAO,QAAQ,IAAI;AAAA,IACrB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,KAAqB;AACtC,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,cAAQ,QAAQ,KAAK,OAAO;AAC5B,aAAO,OAAO;AAAA,IAChB;AACA,WAAO,KAAK,IAAI,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eACZ,OACA,cACA,cACmB;AACnB,UAAM,YAAY;AAClB,UAAM,UAAoB,CAAC;AAE3B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,YAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,SAAS;AAC1C,YAAM,aAAa,MAAM,KAAK;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,cAAQ,KAAK,GAAG,UAAU;AAG1B,YAAM,WAAW,KAAK,IAAI,IAAI,WAAW,MAAM,MAAM;AACrD,cAAQ;AAAA,QACN,kBAAAD,QAAG;AAAA,UACD,cAAc,kBAAAA,QAAG,KAAK,SAAS,SAAS,CAAC,CAAC,IAAI,kBAAAA,QAAG;AAAA,YAC/C,MAAM,OAAO,SAAS;AAAA,UACxB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eACZ,OACA,cACA,cACmB;AACnB,UAAM,aAAa,KAAK,cAAc,YAAY;AAElD,UAAM,eAAe,IAAI;AAAA,MACvB,sBAAsB,KAAK;AAAA,QACzB;AAAA,MACF,CAAC,QAAQ,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQrB;AAEA,UAAM,aAAa,IAAI;AAAA,MACrB,MAAM,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,IACjD;AAEA,UAAM,WAAW,MAAM,KAAK,IAAK,OAAO,CAAC,cAAc,UAAU,CAAC;AAClE,UAAM,UAAU,SAAS,QAAQ,SAAS;AAG1C,UAAM,QAAQ,QAAQ,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;AACxD,UAAM,UAAoB,CAAC;AAE3B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAI,IAAI,MAAM,QAAQ;AAEpB,cAAM,aAAa,MAAM,CAAC,EAAE,QAAQ,aAAa,EAAE,EAAE,KAAK;AAC1D,gBAAQ,KAAK,UAAU;AAAA,MACzB,OAAO;AAEL,gBAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,QAAwB;AAC5C,UAAM,QAAgC;AAAA,MACpC,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AACA,WAAO,MAAM,MAAM,KAAK;AAAA,EAC1B;AACF;;;AChQA,IAAAE,aAAe;AACf,IAAAC,eAAiB;AACjB,IAAAC,qBAAe;AAOR,IAAM,gBAAN,MAAoB;AAAA,EAGzB,YAAY,SAA2B;AACrC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,OAA8B,QAA+B;AAC1E,UAAM,EAAE,WAAW,IAAI,KAAK;AAG5B,QAAI,CAAC,WAAAC,QAAG,WAAW,UAAU,GAAG;AAC9B,iBAAAA,QAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,IAC9C;AAGA,UAAM,WAAW,aAAAC,QAAK,KAAK,YAAY,GAAG,MAAM,OAAO;AACvD,QAAI,WAAmC,CAAC;AAExC,QAAI,WAAAD,QAAG,WAAW,QAAQ,GAAG;AAC3B,UAAI;AACF,mBAAW,KAAK,MAAM,WAAAA,QAAG,aAAa,UAAU,OAAO,CAAC;AAAA,MAC1D,SAAS,GAAG;AACV,gBAAQ,KAAK,mBAAAE,QAAG,OAAO,mBAAmB,QAAQ,EAAE,CAAC;AAAA,MACvD;AAAA,IACF;AAGA,UAAM,eAAuC,EAAE,GAAG,SAAS;AAC3D,QAAI,WAAW;AAEf,eAAW,CAAC,MAAM,SAAS,KAAK,OAAO;AACrC,iBAAW,QAAQ,WAAW;AAC5B,cAAM,MAAM,KAAK,YAAY,IAAI;AACjC,YAAI,CAAC,aAAa,GAAG,GAAG;AACtB,uBAAa,GAAG,IAAI;AACpB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,SAAS,OAAO,KAAK,YAAY,EACpC,KAAK,EACL,OAAO,CAAC,KAAK,QAAQ;AACpB,UAAI,GAAG,IAAI,aAAa,GAAG;AAC3B,aAAO;AAAA,IACT,GAAG,CAAC,CAA2B;AAGjC,eAAAF,QAAG,cAAc,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AAEnE,YAAQ,IAAI,mBAAAE,QAAG,MAAM,SAAS,mBAAAA,QAAG,KAAK,MAAM,CAAC,UAAU,mBAAAA,QAAG,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC3E,QAAI,WAAW,GAAG;AAChB,cAAQ,IAAI,mBAAAA,QAAG,KAAK,SAAS,mBAAAA,QAAG,OAAO,SAAS,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,eACA,cACA,QACe;AACf,UAAM,EAAE,WAAW,IAAI,KAAK;AAG5B,QAAI,CAAC,WAAAF,QAAG,WAAW,UAAU,GAAG;AAC9B,iBAAAA,QAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,IAC9C;AAGA,UAAM,WAAW,aAAAC,QAAK,KAAK,YAAY,GAAG,MAAM,OAAO;AACvD,QAAI,WAAmC,CAAC;AAExC,QAAI,WAAAD,QAAG,WAAW,QAAQ,GAAG;AAC3B,UAAI;AACF,mBAAW,KAAK,MAAM,WAAAA,QAAG,aAAa,UAAU,OAAO,CAAC;AAAA,MAC1D,SAAS,GAAG;AACV,gBAAQ,KAAK,mBAAAE,QAAG,OAAO,mBAAmB,QAAQ,EAAE,CAAC;AAAA,MACvD;AAAA,IACF;AAGA,UAAM,kBAA0C,EAAE,GAAG,SAAS;AAC9D,QAAI,WAAW;AACf,QAAI,eAAe;AAEnB,eAAW,CAAC,MAAM,OAAO,KAAK,cAAc;AAC1C,iBAAW,CAAC,cAAc,cAAc,KAAK,SAAS;AACpD,cAAM,MAAM,KAAK,YAAY,YAAY;AACzC,YAAI,CAAC,gBAAgB,GAAG,GAAG;AACzB,0BAAgB,GAAG,IAAI;AACvB;AAAA,QACF,OAAO;AACL;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,SAAS,OAAO,KAAK,eAAe,EACvC,KAAK,EACL,OAAO,CAAC,KAAK,QAAQ;AACpB,UAAI,GAAG,IAAI,gBAAgB,GAAG;AAC9B,aAAO;AAAA,IACT,GAAG,CAAC,CAA2B;AAGjC,eAAAF,QAAG,cAAc,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AAEnE,YAAQ,IAAI,mBAAAE,QAAG,MAAM,SAAS,mBAAAA,QAAG,KAAK,MAAM,CAAC,UAAU,mBAAAA,QAAG,KAAK,QAAQ,CAAC,EAAE,CAAC;AAC3E,QAAI,WAAW,GAAG;AAChB,cAAQ,IAAI,mBAAAA,QAAG,KAAK,WAAW,mBAAAA,QAAG,OAAO,SAAS,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,IACtE;AACA,QAAI,eAAe,GAAG;AACpB,cAAQ,IAAI,mBAAAA,QAAG,KAAK,aAAa,mBAAAA,QAAG,KAAK,aAAa,SAAS,CAAC,CAAC,QAAQ,CAAC;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,yBAAyB,QAAwC;AAC/D,UAAM,EAAE,WAAW,IAAI,KAAK;AAC5B,UAAM,WAAW,aAAAD,QAAK,KAAK,YAAY,GAAG,MAAM,OAAO;AAEvD,QAAI,WAAAD,QAAG,WAAW,QAAQ,GAAG;AAC3B,UAAI;AACF,eAAO,KAAK,MAAM,WAAAA,QAAG,aAAa,UAAU,OAAO,CAAC;AAAA,MACtD,SAAS,GAAG;AACV,gBAAQ,KAAK,mBAAAE,QAAG,OAAO,mBAAmB,QAAQ,EAAE,CAAC;AAAA,MACvD;AAAA,IACF;AAEA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAAsB;AAGxC,UAAM,UAAU,KACb,QAAQ,8BAA8B,GAAG,EACzC,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE,EACpB,YAAY;AAGf,UAAM,YAAY,QAAQ,MAAM,GAAG,EAAE;AAGrC,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,OAAO,KAAK,WAAW,IAAI;AACjC,aAAO,QAAQ,IAAI;AAAA,IACrB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,KAAqB;AACtC,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,cAAQ,QAAQ,KAAK,OAAO;AAC5B,aAAO,OAAO;AAAA,IAChB;AACA,WAAO,KAAK,IAAI,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AAAA,EAC/C;AACF;;;AJ5JO,SAAS,iBAAiB,UAA6B,CAAC,GAAW;AACxE,QAAM;AAAA,IACJ,SAAS,QAAQ,IAAI,kBAAkB;AAAA,IACvC,SAAS,QAAQ,IAAI,kBAAkB;AAAA,IACvC,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,UAAU,CAAC,gBAAgB,aAAa;AAAA,IACxC,UAAU,CAAC,mBAAmB,SAAS;AAAA,IACvC,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB,CAAC,OAAO;AAAA,IACxB,WAAW;AAAA,IACX,gBAAgB;AAAA,EAClB,IAAI;AAEJ,QAAM,UAAU,IAAI,YAAY,EAAE,SAAS,QAAQ,CAAC;AACpD,QAAM,aAAa,IAAI,eAAe;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,YAAY,IAAI,cAAc,EAAE,YAAY,cAAc,CAAC;AAEjE,MAAI,eAAsC,oBAAI,IAAI;AAElD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,eAAe,QAAQ;AAErB,YAAM,iBAAiB,aAAAC,QAAK,QAAQ,OAAO,MAAM,UAAU;AAG3D,UAAI,CAAC,WAAAC,QAAG,WAAW,cAAc,GAAG;AAClC,mBAAAA,QAAG,UAAU,gBAAgB,EAAE,WAAW,KAAK,CAAC;AAAA,MAClD;AAGA,YAAM,aAAa,CAAC,eAAe,GAAG,aAAa;AACnD,iBAAW,UAAU,YAAY;AAC/B,cAAM,WAAW,aAAAD,QAAK,KAAK,gBAAgB,GAAG,MAAM,OAAO;AAC3D,YAAI,CAAC,WAAAC,QAAG,WAAW,QAAQ,GAAG;AAC5B,qBAAAA,QAAG,cAAc,UAAU,MAAM,OAAO;AACxC,kBAAQ;AAAA,YACN,mBAAAC,QAAG;AAAA,cACD,SAAS,mBAAAA,QAAG,KAAK,MAAM,CAAC,UAAU,mBAAAA,QAAG;AAAA,gBACnC,iBAAiB,MAAM;AAAA,cACzB,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,IAAI,mBAAAA,QAAG,KAAK,qBAAqB,CAAC;AAC1C,cAAQ,IAAI,cAAc,mBAAAA,QAAG,OAAO,UAAU,CAAC,EAAE;AACjD,cAAQ,IAAI,YAAY,mBAAAA,QAAG,KAAK,aAAa,CAAC,EAAE;AAChD,cAAQ,IAAI,YAAY,mBAAAA,QAAG,KAAK,cAAc,KAAK,IAAI,CAAC,CAAC,EAAE;AAC3D,cAAQ,IAAI,YAAY,WAAW,mBAAAA,QAAG,MAAM,GAAG,IAAI,mBAAAA,QAAG,IAAI,GAAG,CAAC,EAAE;AAChE,cAAQ;AAAA,QACN,YAAY,gBAAgB,mBAAAA,QAAG,MAAM,GAAG,IAAI,mBAAAA,QAAG,IAAI,GAAG,CAAC;AAAA,MACzD;AACA,cAAQ;AAAA,QACN,eAAe,SAAS,mBAAAA,QAAG,MAAM,KAAK,IAAI,mBAAAA,QAAG,OAAO,KAAK,CAAC;AAAA;AAAA,MAC5D;AAAA,IACF;AAAA,IAEA,MAAM,aAAa;AACjB,UAAI,CAAC,SAAU;AAEf,cAAQ,IAAI,mBAAAA,QAAG,KAAK,kBAAkB,CAAC;AACvC,qBAAe,MAAM,QAAQ,KAAK;AAElC,YAAM,aAAa,MAAM,KAAK,aAAa,OAAO,CAAC,EAAE,KAAK,EAAE;AAC5D,cAAQ;AAAA,QACN,mBAAAA,QAAG,KAAK,SAAS,mBAAAA,QAAG,OAAO,WAAW,SAAS,CAAC,CAAC;AAAA,CAAW;AAAA,MAC9D;AAEA,UAAI,eAAe,EAAG;AAGtB,YAAM,UAAU,SAAS,cAAc,aAAa;AAGpD,UAAI,iBAAiB,QAAQ;AAC3B,mBAAW,UAAU,eAAe;AAClC,kBAAQ,IAAI,mBAAAA,QAAG,KAAK;AAAA,WAAc,mBAAAA,QAAG,OAAO,MAAM,CAAC,KAAK,CAAC;AAGzD,gBAAM,uBACJ,UAAU,yBAAyB,MAAM;AAE3C,gBAAM,eAAe,MAAM,WAAW;AAAA,YACpC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,gBAAM,UAAU;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,IAAI,mBAAAA,QAAG,MAAM,eAAe,CAAC;AAAA,IACvC;AAAA;AAAA,IAGA,MAAM,gBAAgB,EAAE,MAAM,OAAO,GAAG;AACtC,UAAI,CAAC,SAAU;AACf,UAAI,CAAC,KAAK,MAAM,iBAAiB,EAAG;AACpC,UAAI,KAAK,SAAS,cAAc,KAAK,KAAK,SAAS,UAAU,EAAG;AAEhE,cAAQ,IAAI;AAAA,cAAiB,IAAI,EAAE;AACnC,YAAM,QAAQ,QAAQ,SAAS,IAAI;AAEnC,UAAI,MAAM,SAAS,GAAG;AACpB,gBAAQ,IAAI,SAAS,MAAM,MAAM,OAAO;AACxC,qBAAa,IAAI,MAAM,KAAK;AAG5B,cAAM,UAAU,SAAS,cAAc,aAAa;AAEpD,YAAI,iBAAiB,QAAQ;AAC3B,qBAAW,UAAU,eAAe;AAClC,kBAAM,eAAe,oBAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAG5C,kBAAM,uBACJ,UAAU,yBAAyB,MAAM;AAE3C,kBAAM,eAAe,MAAM,WAAW;AAAA,cACpC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AACA,kBAAM,UAAU;AAAA,cACd;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGA,IAAO,gBAAQ;","names":["import_fs","import_path","import_picocolors","import_fs","import_path","path","fs","fs","path","pc","results","import_fs","import_path","import_picocolors","fs","path","pc","path","fs","pc"]}
|
package/dist/index.mjs
CHANGED
|
@@ -341,8 +341,8 @@ var I18nTranslator = class {
|
|
|
341
341
|
openAIApiKey: options.apiKey,
|
|
342
342
|
configuration: { baseURL: options.apiUrl },
|
|
343
343
|
modelName: options.model,
|
|
344
|
-
temperature: 0.3,
|
|
345
|
-
maxTokens: 4e3
|
|
344
|
+
temperature: options.temperature ?? 0.3,
|
|
345
|
+
maxTokens: options.maxTokens ?? 4e3
|
|
346
346
|
});
|
|
347
347
|
}
|
|
348
348
|
}
|
|
@@ -657,6 +657,8 @@ function vitePluginAII18n(options = {}) {
|
|
|
657
657
|
apiKey = process.env.OPENAI_API_KEY || "",
|
|
658
658
|
apiUrl = process.env.OPENAI_API_URL || "https://api.openai.com/v1",
|
|
659
659
|
model = "gpt-4",
|
|
660
|
+
temperature = 0.3,
|
|
661
|
+
maxTokens = 4e3,
|
|
660
662
|
include = ["src/**/*.vue", "src/**/*.ts"],
|
|
661
663
|
exclude = ["node_modules/**", "dist/**"],
|
|
662
664
|
localesDir = "src/locales",
|
|
@@ -666,7 +668,13 @@ function vitePluginAII18n(options = {}) {
|
|
|
666
668
|
autoTranslate = true
|
|
667
669
|
} = options;
|
|
668
670
|
const scanner = new I18nScanner({ include, exclude });
|
|
669
|
-
const translator = new I18nTranslator({
|
|
671
|
+
const translator = new I18nTranslator({
|
|
672
|
+
apiKey,
|
|
673
|
+
apiUrl,
|
|
674
|
+
model,
|
|
675
|
+
temperature,
|
|
676
|
+
maxTokens
|
|
677
|
+
});
|
|
670
678
|
const generator = new I18nGenerator({ localesDir, defaultLocale });
|
|
671
679
|
let scannedTexts = /* @__PURE__ */ new Map();
|
|
672
680
|
return {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/scanner.ts","../src/utils.ts","../src/translator.ts","../src/generator.ts"],"sourcesContent":["/**\r\n * AI 国际化助手插件入口\r\n *\r\n * 功能:\r\n * - 扫描源码中的中文文本\r\n * - 使用 AI 自动翻译\r\n * - 生成/更新 i18n 语言文件\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport { I18nScanner } from \"./scanner\";\r\nimport { I18nTranslator } from \"./translator\";\r\nimport { I18nGenerator } from \"./generator\";\r\n\r\nexport interface I18nPluginOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n // 扫描配置\r\n include?: string[];\r\n exclude?: string[];\r\n // 输出配置\r\n localesDir?: string;\r\n defaultLocale?: string;\r\n targetLocales?: string[];\r\n // 功能开关\r\n autoScan?: boolean;\r\n autoTranslate?: boolean;\r\n}\r\n\r\nexport function vitePluginAII18n(options: I18nPluginOptions = {}): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = \"gpt-4\",\r\n include = [\"src/**/*.vue\", \"src/**/*.ts\"],\r\n exclude = [\"node_modules/**\", \"dist/**\"],\r\n localesDir = \"src/locales\",\r\n defaultLocale = \"zh-CN\",\r\n targetLocales = [\"en-US\"],\r\n autoScan = true,\r\n autoTranslate = true,\r\n } = options;\r\n\r\n const scanner = new I18nScanner({ include, exclude });\r\n const translator = new I18nTranslator({ apiKey, apiUrl, model });\r\n const generator = new I18nGenerator({ localesDir, defaultLocale });\r\n\r\n let scannedTexts: Map<string, string[]> = new Map();\r\n\r\n return {\r\n name: \"vite-plugin-ai-i18n\",\r\n enforce: \"pre\",\r\n\r\n configResolved(config) {\r\n // 🔥 关键:在最早期就创建空文件,确保 TypeScript 编译时文件存在\r\n const fullLocalesDir = path.resolve(config.root, localesDir);\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(fullLocalesDir)) {\r\n fs.mkdirSync(fullLocalesDir, { recursive: true });\r\n }\r\n\r\n // 为所有语言创建空文件(如果不存在)\r\n const allLocales = [defaultLocale, ...targetLocales];\r\n for (const locale of allLocales) {\r\n const filePath = path.join(fullLocalesDir, `${locale}.json`);\r\n if (!fs.existsSync(filePath)) {\r\n fs.writeFileSync(filePath, \"{}\", \"utf-8\");\r\n console.log(\r\n pc.green(\r\n `✅ 已生成 ${pc.cyan(locale)} 语言文件: ${pc.gray(\r\n `src\\\\locales\\\\${locale}.json`,\r\n )}`,\r\n ),\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.cyan(\"\\n🌍 AI 国际化助手已启动...\"));\r\n console.log(`📂 语言文件目录: ${pc.yellow(localesDir)}`);\r\n console.log(`🔤 默认语言: ${pc.cyan(defaultLocale)}`);\r\n console.log(`🎯 目标语言: ${pc.cyan(targetLocales.join(\", \"))}`);\r\n console.log(`🔍 自动扫描: ${autoScan ? pc.green(\"✅\") : pc.red(\"❌\")}`);\r\n console.log(\r\n `🤖 自动翻译: ${autoTranslate ? pc.green(\"✅\") : pc.red(\"❌\")}`,\r\n );\r\n console.log(\r\n `🔑 API Key: ${apiKey ? pc.green(\"已配置\") : pc.yellow(\"未配置\")}\\n`,\r\n );\r\n },\r\n\r\n async buildStart() {\r\n if (!autoScan) return;\r\n\r\n console.log(pc.cyan(\"🔍 正在扫描中文文本...\\n\"));\r\n scannedTexts = await scanner.scan();\r\n\r\n const totalTexts = Array.from(scannedTexts.values()).flat().length;\r\n console.log(\r\n pc.blue(`📝 发现 ${pc.yellow(totalTexts.toString())} 条待翻译文本\\n`),\r\n );\r\n\r\n if (totalTexts === 0) return;\r\n\r\n // 生成默认语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n // 自动翻译到目标语言\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n console.log(pc.cyan(`\\n🌐 正在翻译到 ${pc.yellow(locale)}...`));\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n scannedTexts,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n scannedTexts,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.green(\"\\n✨ 国际化处理完成\\n\"));\r\n },\r\n\r\n // 监听文件变化,增量更新\r\n async handleHotUpdate({ file, server }) {\r\n if (!autoScan) return;\r\n if (!file.match(/\\.(vue|ts|tsx)$/)) return;\r\n if (file.includes(\"node_modules\") || file.includes(localesDir)) return;\r\n\r\n console.log(`\\n🔄 检测到文件变化: ${file}`);\r\n const texts = scanner.scanFile(file);\r\n\r\n if (texts.length > 0) {\r\n console.log(`📝 发现 ${texts.length} 条新文本`);\r\n scannedTexts.set(file, texts);\r\n\r\n // 更新语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n const fileTextsMap = new Map([[file, texts]]);\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n fileTextsMap,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n fileTextsMap,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAII18n;\r\n","/**\r\n * 中文文本扫描器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { glob } from \"./utils\";\r\n\r\nexport interface ScannerOptions {\r\n include: string[];\r\n exclude: string[];\r\n debug?: boolean; // 调试模式\r\n}\r\n\r\nexport class I18nScanner {\r\n private options: ScannerOptions;\r\n\r\n // 匹配中文字符的正则\r\n private chineseRegex = /[\\u4e00-\\u9fa5]+[^\\n<>{}]*[\\u4e00-\\u9fa5]*/g;\r\n\r\n // 需要忽略的模式\r\n private ignorePatterns = [\r\n /console\\.(log|warn|error|info)/,\r\n /\\/\\/.*$/,\r\n /\\/\\*[\\s\\S]*?\\*\\//,\r\n /<!--[\\s\\S]*?-->/,\r\n ];\r\n\r\n constructor(options: ScannerOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 扫描所有匹配的文件\r\n */\r\n async scan(): Promise<Map<string, string[]>> {\r\n const results = new Map<string, string[]>();\r\n const files = await glob(this.options.include, this.options.exclude);\r\n\r\n for (const file of files) {\r\n const texts = this.scanFile(file);\r\n if (texts.length > 0) {\r\n results.set(file, texts);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 扫描单个文件\r\n */\r\n scanFile(filePath: string): string[] {\r\n if (!fs.existsSync(filePath)) return [];\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n const ext = path.extname(filePath);\r\n\r\n let texts: string[] = [];\r\n\r\n if (ext === \".vue\") {\r\n texts = this.scanVueFile(content);\r\n } else if ([\".ts\", \".tsx\", \".js\", \".jsx\"].includes(ext)) {\r\n texts = this.scanScriptFile(content);\r\n }\r\n\r\n // 使用 Set 去重并过滤(性能优化)\r\n const uniqueTexts = new Set(texts);\r\n return Array.from(uniqueTexts).filter((t) => this.isValidText(t));\r\n }\r\n\r\n /**\r\n * 扫描 Vue 文件\r\n */\r\n private scanVueFile(content: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 扫描 template 部分\r\n const templateMatch = content.match(\r\n /<template[^>]*>([\\s\\S]*?)<\\/template>/\r\n );\r\n if (templateMatch) {\r\n texts.push(...this.extractChineseFromTemplate(templateMatch[1]));\r\n }\r\n\r\n // 扫描 script 部分\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (scriptMatch) {\r\n texts.push(...this.extractChineseFromScript(scriptMatch[1]));\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 扫描脚本文件\r\n */\r\n private scanScriptFile(content: string): string[] {\r\n return this.extractChineseFromScript(content);\r\n }\r\n\r\n /**\r\n * 安全地移除注释(不影响字符串中的内容)\r\n */\r\n private removeComments(code: string): string {\r\n let result = \"\";\r\n let inString = false;\r\n let stringChar = \"\";\r\n let inBlockComment = false;\r\n let inLineComment = false;\r\n\r\n for (let i = 0; i < code.length; i++) {\r\n const char = code[i];\r\n const nextChar = code[i + 1];\r\n const prevChar = code[i - 1];\r\n\r\n // 处理字符串\r\n if (!inBlockComment && !inLineComment) {\r\n if (\r\n (char === '\"' || char === \"'\" || char === \"`\") &&\r\n prevChar !== \"\\\\\"\r\n ) {\r\n if (!inString) {\r\n inString = true;\r\n stringChar = char;\r\n } else if (char === stringChar) {\r\n inString = false;\r\n }\r\n }\r\n }\r\n\r\n // 在字符串中,保留所有字符\r\n if (inString) {\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 处理块注释\r\n if (char === \"/\" && nextChar === \"*\" && !inLineComment) {\r\n inBlockComment = true;\r\n i++; // 跳过 *\r\n continue;\r\n }\r\n if (char === \"*\" && nextChar === \"/\" && inBlockComment) {\r\n inBlockComment = false;\r\n i++; // 跳过 /\r\n continue;\r\n }\r\n\r\n // 处理行注释\r\n if (char === \"/\" && nextChar === \"/\" && !inBlockComment) {\r\n inLineComment = true;\r\n i++; // 跳过第二个 /\r\n continue;\r\n }\r\n if (char === \"\\n\" && inLineComment) {\r\n inLineComment = false;\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 跳过注释内容\r\n if (inBlockComment || inLineComment) {\r\n continue;\r\n }\r\n\r\n result += char;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * 从模板中提取中文\r\n */\r\n private extractChineseFromTemplate(template: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 移除注释\r\n let cleaned = template.replace(/<!--[\\s\\S]*?-->/g, \"\");\r\n\r\n // 1. 提取 t() 或 $t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /(?:\\$t|\\bt)\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取标签内的纯文本(不包含插值表达式)\r\n const tagTextRegex = />([^<{]+)</g;\r\n while ((match = tagTextRegex.exec(cleaned)) !== null) {\r\n const text = match[1].trim();\r\n if (text && /[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 3. 提取插值中的字符串字面量(不包括 t() 调用)\r\n const interpolationRegex = /\\{\\{\\s*[\"']([^\"']+)[\"']\\s*\\}\\}/g;\r\n while ((match = interpolationRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 4. 提取静态属性中的中文\r\n const staticAttrRegex =\r\n /(?:placeholder|title|label|alt|content|aria-label)=[\"']([^\"']*[\\u4e00-\\u9fa5][^\"']*)[\"']/g;\r\n while ((match = staticAttrRegex.exec(cleaned)) !== null) {\r\n texts.push(match[1]);\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 从脚本中提取中文\r\n */\r\n private extractChineseFromScript(script: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 安全地移除注释\r\n const cleaned = this.removeComments(script);\r\n\r\n // 1. 提取 t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /\\bt\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取所有字符串字面量(不在 t() 中的)\r\n const allStrings: string[] = [];\r\n\r\n // 单引号字符串\r\n const singleQuoteRegex = /'([^'\\\\]*(\\\\.[^'\\\\]*)*)'/g;\r\n while ((match = singleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 双引号字符串\r\n const doubleQuoteRegex = /\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"/g;\r\n while ((match = doubleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 模板字符串(简单情况,不包含插值)\r\n const templateRegex = /`([^`$\\\\]*(\\\\.[^`$\\\\]*)*)`/g;\r\n while ((match = templateRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 3. 过滤出包含中文的字符串\r\n for (const str of allStrings) {\r\n if (/[\\u4e00-\\u9fa5]/.test(str)) {\r\n texts.push(str);\r\n }\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 验证文本是否有效\r\n */\r\n private isValidText(text: string): boolean {\r\n const debug = this.options.debug;\r\n\r\n // 1. 基础过滤\r\n if (text.length < 2) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本太短`);\r\n return false;\r\n }\r\n\r\n if (!/[\\u4e00-\\u9fa5]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 不包含中文`);\r\n return false;\r\n }\r\n\r\n if (/^\\s*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯空格`);\r\n return false;\r\n }\r\n\r\n // 2. 过滤 i18n 相关\r\n if (/^\\$t\\(|^t\\(|^i18n\\.|_uni_app$|^[a-z_]+_[a-z_]+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: i18n key`);\r\n return false;\r\n }\r\n\r\n // 3. 过滤系统提示信息\r\n if (/^[⚠️❌✅🔍📝💡🎯🚀🔧📊]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 系统提示`);\r\n return false;\r\n }\r\n\r\n // 4. 过滤技术术语\r\n if (\r\n /\\.(json|js|ts|vue|md|txt|html|css|jsx|tsx)\\s*(文件|不存在|已|错误)/.test(\r\n text\r\n )\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术术语`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9_\\-\\.]+\\s*(文件|不存在|错误|失败)/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术错误信息`);\r\n return false;\r\n }\r\n\r\n // 5. 过滤变量名和路径\r\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 变量名`);\r\n return false;\r\n }\r\n\r\n if (/^\\/[a-zA-Z0-9_\\-\\/]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 路径`);\r\n return false;\r\n }\r\n\r\n // 6. 过滤 URL 和邮箱\r\n if (/^https?:\\/\\//.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: URL`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 邮箱`);\r\n return false;\r\n }\r\n\r\n // 7. 过滤纯数字和日期\r\n if (/^\\d+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯数字`);\r\n return false;\r\n }\r\n\r\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 日期`);\r\n return false;\r\n }\r\n\r\n // 8. 过滤代码片段\r\n if (\r\n /^(const|let|var|function|class|import|export|return)\\s/.test(text)\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 代码片段`);\r\n return false;\r\n }\r\n\r\n // 9. 过滤过长的文本(可能是代码)\r\n if (text.length > 100) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本过长`);\r\n return false;\r\n }\r\n\r\n // 10. 过滤包含特殊字符过多的文本\r\n const specialCharCount = (\r\n text.match(/[^\\u4e00-\\u9fa5a-zA-Z0-9\\s,。!?、;:\"\"''()《》]/g) || []\r\n ).length;\r\n if (specialCharCount > text.length * 0.3) {\r\n if (debug)\r\n console.log(`[过滤] \"${text}\" - 原因: 特殊字符过多 (${specialCharCount})`);\r\n return false;\r\n }\r\n\r\n if (debug) console.log(`[保留] \"${text}\"`);\r\n return true;\r\n }\r\n}\r\n","/**\r\n * i18n 插件工具函数\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\n\r\n/**\r\n * 简单的 glob 实现\r\n */\r\nexport async function glob(\r\n patterns: string[],\r\n excludePatterns: string[]\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n const cwd = process.cwd();\r\n\r\n for (const pattern of patterns) {\r\n const files = await matchPattern(pattern, cwd);\r\n results.push(...files);\r\n }\r\n\r\n // 过滤排除的文件\r\n return results.filter((file) => {\r\n const relativePath = path.relative(cwd, file);\r\n return !excludePatterns.some((p) => matchGlob(relativePath, p));\r\n });\r\n}\r\n\r\n/**\r\n * 匹配单个 glob 模式\r\n */\r\nasync function matchPattern(pattern: string, cwd: string): Promise<string[]> {\r\n const results: string[] = [];\r\n\r\n // 解析模式\r\n const parts = pattern.split(\"/\");\r\n const hasGlobstar = parts.includes(\"**\");\r\n\r\n if (hasGlobstar) {\r\n // 递归搜索\r\n const baseDir = parts.slice(0, parts.indexOf(\"**\")).join(\"/\") || \".\";\r\n const filePattern = parts.slice(parts.indexOf(\"**\") + 1).join(\"/\");\r\n await walkDir(path.join(cwd, baseDir), filePattern, results);\r\n } else {\r\n // 直接匹配\r\n const fullPath = path.join(cwd, pattern);\r\n if (fs.existsSync(fullPath)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * 递归遍历目录\r\n */\r\nasync function walkDir(\r\n dir: string,\r\n pattern: string,\r\n results: string[]\r\n): Promise<void> {\r\n if (!fs.existsSync(dir)) return;\r\n\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n // 跳过 node_modules 和隐藏目录\r\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) {\r\n continue;\r\n }\r\n await walkDir(fullPath, pattern, results);\r\n } else if (entry.isFile()) {\r\n if (matchGlob(entry.name, pattern)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 简单的 glob 匹配\r\n */\r\nfunction matchGlob(str: string, pattern: string): boolean {\r\n // 转换 glob 模式为正则\r\n const regexPattern = pattern\r\n .replace(/\\./g, \"\\\\.\")\r\n .replace(/\\*\\*/g, \".*\")\r\n .replace(/\\*/g, \"[^/]*\")\r\n .replace(/\\?/g, \".\");\r\n\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(str);\r\n}\r\n\r\n/**\r\n * 生成唯一 ID\r\n */\r\nexport function generateId(): string {\r\n return Math.random().toString(36).slice(2, 10);\r\n}\r\n\r\n/**\r\n * 格式化文件大小\r\n */\r\nexport function formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n}\r\n","/**\r\n * AI 翻译器\r\n */\r\n\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface TranslatorOptions {\r\n apiKey: string;\r\n apiUrl: string;\r\n model: string;\r\n}\r\n\r\nexport class I18nTranslator {\r\n private llm: ChatOpenAI | null = null;\r\n private options: TranslatorOptions;\r\n\r\n constructor(options: TranslatorOptions) {\r\n this.options = options;\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: 0.3,\r\n maxTokens: 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 批量翻译文本 - 支持增量翻译(跳过已翻译的文本)\r\n */\r\n async translate(\r\n texts: Map<string, string[]>,\r\n sourceLocale: string,\r\n targetLocale: string,\r\n existingTranslations?: Record<string, string>,\r\n ): Promise<Map<string, Map<string, string>>> {\r\n if (!this.llm) {\r\n console.warn(pc.yellow(\"⚠️ 未配置 API Key,跳过翻译\"));\r\n // 返回原文作为翻译\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n\r\n const results = new Map<string, Map<string, string>>();\r\n const allTexts = Array.from(texts.values()).flat();\r\n const uniqueTexts = [...new Set(allTexts)];\r\n\r\n if (uniqueTexts.length === 0) return results;\r\n\r\n // 过滤出需要翻译的文本(未翻译的)\r\n const textsToTranslate: string[] = [];\r\n const cachedTranslations = new Map<string, string>();\r\n\r\n uniqueTexts.forEach((text) => {\r\n const key = this.generateKey(text);\r\n if (existingTranslations && existingTranslations[key]) {\r\n // 使用已有翻译\r\n cachedTranslations.set(text, existingTranslations[key]);\r\n } else {\r\n // 需要翻译\r\n textsToTranslate.push(text);\r\n }\r\n });\r\n\r\n console.log(pc.blue(\"📊 翻译统计:\"));\r\n console.log(` 总计: ${pc.cyan(uniqueTexts.length.toString())} 条`);\r\n console.log(\r\n ` ✅ 已有: ${pc.green(cachedTranslations.size.toString())} 条`,\r\n );\r\n console.log(\r\n ` 🆕 新增: ${pc.yellow(textsToTranslate.length.toString())} 条`,\r\n );\r\n\r\n try {\r\n // 只翻译新增的文本\r\n let newTranslations: string[] = [];\r\n if (textsToTranslate.length > 0) {\r\n console.log(pc.cyan(`\\n🤖 正在翻译新增文本...`));\r\n newTranslations = await this.batchTranslate(\r\n textsToTranslate,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n }\r\n\r\n // 创建完整的翻译映射(原文 -> 译文)\r\n const translationMap = new Map<string, string>(cachedTranslations);\r\n textsToTranslate.forEach((text, index) => {\r\n translationMap.set(text, newTranslations[index] || text);\r\n });\r\n\r\n // 按文件组织翻译结果\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => {\r\n textMap.set(text, translationMap.get(text) || text);\r\n });\r\n results.set(file, textMap);\r\n }\r\n\r\n return results;\r\n } catch (error: any) {\r\n console.error(pc.red(\"❌ 翻译失败:\"), error.message);\r\n // 返回原文\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n }\r\n\r\n /**\r\n * 生成翻译 key(与 generator 保持一致)\r\n */\r\n private generateKey(text: string): string {\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n\r\n /**\r\n * 批量翻译(分批处理避免超限)\r\n */\r\n private async batchTranslate(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const batchSize = 20;\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i += batchSize) {\r\n const batch = texts.slice(i, i + batchSize);\r\n const translated = await this.translateBatch(\r\n batch,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n results.push(...translated);\r\n\r\n // 显示进度\r\n const progress = Math.min(i + batchSize, texts.length);\r\n console.log(\r\n pc.gray(\r\n ` 📊 翻译进度: ${pc.cyan(progress.toString())}/${pc.cyan(\r\n texts.length.toString(),\r\n )}`,\r\n ),\r\n );\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 翻译一批文本\r\n */\r\n private async translateBatch(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const localeName = this.getLocaleName(targetLocale);\r\n\r\n const systemPrompt = new SystemMessage(\r\n `你是专业的软件本地化翻译专家。请将以下${this.getLocaleName(\r\n sourceLocale,\r\n )}文本翻译成${localeName}。\r\n要求:\r\n1. 保持专业术语的准确性\r\n2. 翻译要自然流畅,符合目标语言习惯\r\n3. 保留原文中的变量占位符(如 {name}、%s 等)\r\n4. 每行一个翻译,与输入顺序严格对应\r\n5. 只输出翻译结果,不要解释\r\n6. 如果原文包含换行,翻译也保持相同的换行`,\r\n );\r\n\r\n const userPrompt = new HumanMessage(\r\n texts.map((t, i) => `${i + 1}. ${t}`).join(\"\\n\"),\r\n );\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n const content = response.content.toString();\r\n\r\n // 解析翻译结果\r\n const lines = content.split(\"\\n\").filter((l) => l.trim());\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i++) {\r\n if (i < lines.length) {\r\n // 移除序号前缀\r\n const translated = lines[i].replace(/^\\d+\\.\\s*/, \"\").trim();\r\n results.push(translated);\r\n } else {\r\n // 如果翻译结果不够,使用原文\r\n results.push(texts[i]);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 获取语言名称\r\n */\r\n private getLocaleName(locale: string): string {\r\n const names: Record<string, string> = {\r\n \"zh-CN\": \"简体中文\",\r\n \"zh-TW\": \"繁体中文\",\r\n \"en-US\": \"英语\",\r\n \"ja-JP\": \"日语\",\r\n \"ko-KR\": \"韩语\",\r\n \"fr-FR\": \"法语\",\r\n \"de-DE\": \"德语\",\r\n \"es-ES\": \"西班牙语\",\r\n \"pt-BR\": \"葡萄牙语\",\r\n \"ru-RU\": \"俄语\",\r\n \"ar-SA\": \"阿拉伯语\",\r\n };\r\n return names[locale] || locale;\r\n }\r\n}\r\n","/**\r\n * i18n 语言文件生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface GeneratorOptions {\r\n localesDir: string;\r\n defaultLocale: string;\r\n}\r\n\r\nexport class I18nGenerator {\r\n private options: GeneratorOptions;\r\n\r\n constructor(options: GeneratorOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 生成语言文件\r\n */\r\n async generate(texts: Map<string, string[]>, locale: string): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象\r\n const translations: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n\r\n for (const [file, fileTexts] of texts) {\r\n for (const text of fileTexts) {\r\n const key = this.generateKey(text);\r\n if (!translations[key]) {\r\n translations[key] = text;\r\n newCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translations)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translations[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 生成语言文件(用于目标语言)- 支持增量翻译\r\n */\r\n async generateTranslated(\r\n originalTexts: Map<string, string[]>,\r\n translations: Map<string, Map<string, string>>,\r\n locale: string,\r\n ): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象(保留已有翻译)\r\n const translationsObj: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n let skippedCount = 0;\r\n\r\n for (const [file, textMap] of translations) {\r\n for (const [originalText, translatedText] of textMap) {\r\n const key = this.generateKey(originalText);\r\n if (!translationsObj[key]) {\r\n translationsObj[key] = translatedText;\r\n newCount++;\r\n } else {\r\n skippedCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translationsObj)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translationsObj[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` ✨ 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n if (skippedCount > 0) {\r\n console.log(pc.gray(` ⏭️ 跳过 ${pc.blue(skippedCount.toString())} 条已有翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 加载已有翻译\r\n */\r\n loadExistingTranslations(locale: string): Record<string, string> {\r\n const { localesDir } = this.options;\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n return {};\r\n }\r\n\r\n /**\r\n * 生成翻译 key\r\n */\r\n private generateKey(text: string): string {\r\n // 使用拼音首字母或简化文本作为 key\r\n // 这里使用简单的哈希方式\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n // 截取前 30 个字符\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n // 如果太短,添加哈希后缀\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n}\r\n"],"mappings":"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","names":["fs","path","pc","fs","path","fs","path","results","fs","path","pc","path","fs","pc"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/scanner.ts","../src/utils.ts","../src/translator.ts","../src/generator.ts"],"sourcesContent":["/**\r\n * AI 国际化助手插件入口\r\n *\r\n * 功能:\r\n * - 扫描源码中的中文文本\r\n * - 使用 AI 自动翻译\r\n * - 生成/更新 i18n 语言文件\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport { I18nScanner } from \"./scanner\";\r\nimport { I18nTranslator } from \"./translator\";\r\nimport { I18nGenerator } from \"./generator\";\r\n\r\nexport interface I18nPluginOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n // 扫描配置\r\n include?: string[];\r\n exclude?: string[];\r\n // 输出配置\r\n localesDir?: string;\r\n defaultLocale?: string;\r\n targetLocales?: string[];\r\n // 功能开关\r\n autoScan?: boolean;\r\n autoTranslate?: boolean;\r\n}\r\n\r\nexport function vitePluginAII18n(options: I18nPluginOptions = {}): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = \"gpt-4\",\r\n temperature = 0.3,\r\n maxTokens = 4000,\r\n include = [\"src/**/*.vue\", \"src/**/*.ts\"],\r\n exclude = [\"node_modules/**\", \"dist/**\"],\r\n localesDir = \"src/locales\",\r\n defaultLocale = \"zh-CN\",\r\n targetLocales = [\"en-US\"],\r\n autoScan = true,\r\n autoTranslate = true,\r\n } = options;\r\n\r\n const scanner = new I18nScanner({ include, exclude });\r\n const translator = new I18nTranslator({\r\n apiKey,\r\n apiUrl,\r\n model,\r\n temperature,\r\n maxTokens,\r\n });\r\n const generator = new I18nGenerator({ localesDir, defaultLocale });\r\n\r\n let scannedTexts: Map<string, string[]> = new Map();\r\n\r\n return {\r\n name: \"vite-plugin-ai-i18n\",\r\n enforce: \"pre\",\r\n\r\n configResolved(config) {\r\n // 🔥 关键:在最早期就创建空文件,确保 TypeScript 编译时文件存在\r\n const fullLocalesDir = path.resolve(config.root, localesDir);\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(fullLocalesDir)) {\r\n fs.mkdirSync(fullLocalesDir, { recursive: true });\r\n }\r\n\r\n // 为所有语言创建空文件(如果不存在)\r\n const allLocales = [defaultLocale, ...targetLocales];\r\n for (const locale of allLocales) {\r\n const filePath = path.join(fullLocalesDir, `${locale}.json`);\r\n if (!fs.existsSync(filePath)) {\r\n fs.writeFileSync(filePath, \"{}\", \"utf-8\");\r\n console.log(\r\n pc.green(\r\n `✅ 已生成 ${pc.cyan(locale)} 语言文件: ${pc.gray(\r\n `src\\\\locales\\\\${locale}.json`,\r\n )}`,\r\n ),\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.cyan(\"\\n🌍 AI 国际化助手已启动...\"));\r\n console.log(`📂 语言文件目录: ${pc.yellow(localesDir)}`);\r\n console.log(`🔤 默认语言: ${pc.cyan(defaultLocale)}`);\r\n console.log(`🎯 目标语言: ${pc.cyan(targetLocales.join(\", \"))}`);\r\n console.log(`🔍 自动扫描: ${autoScan ? pc.green(\"✅\") : pc.red(\"❌\")}`);\r\n console.log(\r\n `🤖 自动翻译: ${autoTranslate ? pc.green(\"✅\") : pc.red(\"❌\")}`,\r\n );\r\n console.log(\r\n `🔑 API Key: ${apiKey ? pc.green(\"已配置\") : pc.yellow(\"未配置\")}\\n`,\r\n );\r\n },\r\n\r\n async buildStart() {\r\n if (!autoScan) return;\r\n\r\n console.log(pc.cyan(\"🔍 正在扫描中文文本...\\n\"));\r\n scannedTexts = await scanner.scan();\r\n\r\n const totalTexts = Array.from(scannedTexts.values()).flat().length;\r\n console.log(\r\n pc.blue(`📝 发现 ${pc.yellow(totalTexts.toString())} 条待翻译文本\\n`),\r\n );\r\n\r\n if (totalTexts === 0) return;\r\n\r\n // 生成默认语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n // 自动翻译到目标语言\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n console.log(pc.cyan(`\\n🌐 正在翻译到 ${pc.yellow(locale)}...`));\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n scannedTexts,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n scannedTexts,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n\r\n console.log(pc.green(\"\\n✨ 国际化处理完成\\n\"));\r\n },\r\n\r\n // 监听文件变化,增量更新\r\n async handleHotUpdate({ file, server }) {\r\n if (!autoScan) return;\r\n if (!file.match(/\\.(vue|ts|tsx)$/)) return;\r\n if (file.includes(\"node_modules\") || file.includes(localesDir)) return;\r\n\r\n console.log(`\\n🔄 检测到文件变化: ${file}`);\r\n const texts = scanner.scanFile(file);\r\n\r\n if (texts.length > 0) {\r\n console.log(`📝 发现 ${texts.length} 条新文本`);\r\n scannedTexts.set(file, texts);\r\n\r\n // 更新语言文件\r\n await generator.generate(scannedTexts, defaultLocale);\r\n\r\n if (autoTranslate && apiKey) {\r\n for (const locale of targetLocales) {\r\n const fileTextsMap = new Map([[file, texts]]);\r\n\r\n // 读取已有翻译\r\n const existingTranslations =\r\n generator.loadExistingTranslations(locale);\r\n\r\n const translations = await translator.translate(\r\n fileTextsMap,\r\n defaultLocale,\r\n locale,\r\n existingTranslations,\r\n );\r\n await generator.generateTranslated(\r\n fileTextsMap,\r\n translations,\r\n locale,\r\n );\r\n }\r\n }\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAII18n;\r\n","/**\r\n * 中文文本扫描器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { glob } from \"./utils\";\r\n\r\nexport interface ScannerOptions {\r\n include: string[];\r\n exclude: string[];\r\n debug?: boolean; // 调试模式\r\n}\r\n\r\nexport class I18nScanner {\r\n private options: ScannerOptions;\r\n\r\n // 匹配中文字符的正则\r\n private chineseRegex = /[\\u4e00-\\u9fa5]+[^\\n<>{}]*[\\u4e00-\\u9fa5]*/g;\r\n\r\n // 需要忽略的模式\r\n private ignorePatterns = [\r\n /console\\.(log|warn|error|info)/,\r\n /\\/\\/.*$/,\r\n /\\/\\*[\\s\\S]*?\\*\\//,\r\n /<!--[\\s\\S]*?-->/,\r\n ];\r\n\r\n constructor(options: ScannerOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 扫描所有匹配的文件\r\n */\r\n async scan(): Promise<Map<string, string[]>> {\r\n const results = new Map<string, string[]>();\r\n const files = await glob(this.options.include, this.options.exclude);\r\n\r\n for (const file of files) {\r\n const texts = this.scanFile(file);\r\n if (texts.length > 0) {\r\n results.set(file, texts);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 扫描单个文件\r\n */\r\n scanFile(filePath: string): string[] {\r\n if (!fs.existsSync(filePath)) return [];\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n const ext = path.extname(filePath);\r\n\r\n let texts: string[] = [];\r\n\r\n if (ext === \".vue\") {\r\n texts = this.scanVueFile(content);\r\n } else if ([\".ts\", \".tsx\", \".js\", \".jsx\"].includes(ext)) {\r\n texts = this.scanScriptFile(content);\r\n }\r\n\r\n // 使用 Set 去重并过滤(性能优化)\r\n const uniqueTexts = new Set(texts);\r\n return Array.from(uniqueTexts).filter((t) => this.isValidText(t));\r\n }\r\n\r\n /**\r\n * 扫描 Vue 文件\r\n */\r\n private scanVueFile(content: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 扫描 template 部分\r\n const templateMatch = content.match(\r\n /<template[^>]*>([\\s\\S]*?)<\\/template>/\r\n );\r\n if (templateMatch) {\r\n texts.push(...this.extractChineseFromTemplate(templateMatch[1]));\r\n }\r\n\r\n // 扫描 script 部分\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (scriptMatch) {\r\n texts.push(...this.extractChineseFromScript(scriptMatch[1]));\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 扫描脚本文件\r\n */\r\n private scanScriptFile(content: string): string[] {\r\n return this.extractChineseFromScript(content);\r\n }\r\n\r\n /**\r\n * 安全地移除注释(不影响字符串中的内容)\r\n */\r\n private removeComments(code: string): string {\r\n let result = \"\";\r\n let inString = false;\r\n let stringChar = \"\";\r\n let inBlockComment = false;\r\n let inLineComment = false;\r\n\r\n for (let i = 0; i < code.length; i++) {\r\n const char = code[i];\r\n const nextChar = code[i + 1];\r\n const prevChar = code[i - 1];\r\n\r\n // 处理字符串\r\n if (!inBlockComment && !inLineComment) {\r\n if (\r\n (char === '\"' || char === \"'\" || char === \"`\") &&\r\n prevChar !== \"\\\\\"\r\n ) {\r\n if (!inString) {\r\n inString = true;\r\n stringChar = char;\r\n } else if (char === stringChar) {\r\n inString = false;\r\n }\r\n }\r\n }\r\n\r\n // 在字符串中,保留所有字符\r\n if (inString) {\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 处理块注释\r\n if (char === \"/\" && nextChar === \"*\" && !inLineComment) {\r\n inBlockComment = true;\r\n i++; // 跳过 *\r\n continue;\r\n }\r\n if (char === \"*\" && nextChar === \"/\" && inBlockComment) {\r\n inBlockComment = false;\r\n i++; // 跳过 /\r\n continue;\r\n }\r\n\r\n // 处理行注释\r\n if (char === \"/\" && nextChar === \"/\" && !inBlockComment) {\r\n inLineComment = true;\r\n i++; // 跳过第二个 /\r\n continue;\r\n }\r\n if (char === \"\\n\" && inLineComment) {\r\n inLineComment = false;\r\n result += char;\r\n continue;\r\n }\r\n\r\n // 跳过注释内容\r\n if (inBlockComment || inLineComment) {\r\n continue;\r\n }\r\n\r\n result += char;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * 从模板中提取中文\r\n */\r\n private extractChineseFromTemplate(template: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 移除注释\r\n let cleaned = template.replace(/<!--[\\s\\S]*?-->/g, \"\");\r\n\r\n // 1. 提取 t() 或 $t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /(?:\\$t|\\bt)\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取标签内的纯文本(不包含插值表达式)\r\n const tagTextRegex = />([^<{]+)</g;\r\n while ((match = tagTextRegex.exec(cleaned)) !== null) {\r\n const text = match[1].trim();\r\n if (text && /[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 3. 提取插值中的字符串字面量(不包括 t() 调用)\r\n const interpolationRegex = /\\{\\{\\s*[\"']([^\"']+)[\"']\\s*\\}\\}/g;\r\n while ((match = interpolationRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 4. 提取静态属性中的中文\r\n const staticAttrRegex =\r\n /(?:placeholder|title|label|alt|content|aria-label)=[\"']([^\"']*[\\u4e00-\\u9fa5][^\"']*)[\"']/g;\r\n while ((match = staticAttrRegex.exec(cleaned)) !== null) {\r\n texts.push(match[1]);\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 从脚本中提取中文\r\n */\r\n private extractChineseFromScript(script: string): string[] {\r\n const texts: string[] = [];\r\n\r\n // 安全地移除注释\r\n const cleaned = this.removeComments(script);\r\n\r\n // 1. 提取 t() 函数中的文本(这些是需要翻译的 key)\r\n const tFunctionRegex = /\\bt\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\r\n let match;\r\n while ((match = tFunctionRegex.exec(cleaned)) !== null) {\r\n const text = match[1];\r\n if (/[\\u4e00-\\u9fa5]/.test(text)) {\r\n texts.push(text);\r\n }\r\n }\r\n\r\n // 2. 提取所有字符串字面量(不在 t() 中的)\r\n const allStrings: string[] = [];\r\n\r\n // 单引号字符串\r\n const singleQuoteRegex = /'([^'\\\\]*(\\\\.[^'\\\\]*)*)'/g;\r\n while ((match = singleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 双引号字符串\r\n const doubleQuoteRegex = /\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"/g;\r\n while ((match = doubleQuoteRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 模板字符串(简单情况,不包含插值)\r\n const templateRegex = /`([^`$\\\\]*(\\\\.[^`$\\\\]*)*)`/g;\r\n while ((match = templateRegex.exec(cleaned)) !== null) {\r\n allStrings.push(match[1]);\r\n }\r\n\r\n // 3. 过滤出包含中文的字符串\r\n for (const str of allStrings) {\r\n if (/[\\u4e00-\\u9fa5]/.test(str)) {\r\n texts.push(str);\r\n }\r\n }\r\n\r\n return texts;\r\n }\r\n\r\n /**\r\n * 验证文本是否有效\r\n */\r\n private isValidText(text: string): boolean {\r\n const debug = this.options.debug;\r\n\r\n // 1. 基础过滤\r\n if (text.length < 2) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本太短`);\r\n return false;\r\n }\r\n\r\n if (!/[\\u4e00-\\u9fa5]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 不包含中文`);\r\n return false;\r\n }\r\n\r\n if (/^\\s*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯空格`);\r\n return false;\r\n }\r\n\r\n // 2. 过滤 i18n 相关\r\n if (/^\\$t\\(|^t\\(|^i18n\\.|_uni_app$|^[a-z_]+_[a-z_]+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: i18n key`);\r\n return false;\r\n }\r\n\r\n // 3. 过滤系统提示信息\r\n if (/^[⚠️❌✅🔍📝💡🎯🚀🔧📊]/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 系统提示`);\r\n return false;\r\n }\r\n\r\n // 4. 过滤技术术语\r\n if (\r\n /\\.(json|js|ts|vue|md|txt|html|css|jsx|tsx)\\s*(文件|不存在|已|错误)/.test(\r\n text\r\n )\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术术语`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9_\\-\\.]+\\s*(文件|不存在|错误|失败)/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 技术错误信息`);\r\n return false;\r\n }\r\n\r\n // 5. 过滤变量名和路径\r\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 变量名`);\r\n return false;\r\n }\r\n\r\n if (/^\\/[a-zA-Z0-9_\\-\\/]*$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 路径`);\r\n return false;\r\n }\r\n\r\n // 6. 过滤 URL 和邮箱\r\n if (/^https?:\\/\\//.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: URL`);\r\n return false;\r\n }\r\n\r\n if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 邮箱`);\r\n return false;\r\n }\r\n\r\n // 7. 过滤纯数字和日期\r\n if (/^\\d+$/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 纯数字`);\r\n return false;\r\n }\r\n\r\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(text)) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 日期`);\r\n return false;\r\n }\r\n\r\n // 8. 过滤代码片段\r\n if (\r\n /^(const|let|var|function|class|import|export|return)\\s/.test(text)\r\n ) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 代码片段`);\r\n return false;\r\n }\r\n\r\n // 9. 过滤过长的文本(可能是代码)\r\n if (text.length > 100) {\r\n if (debug) console.log(`[过滤] \"${text}\" - 原因: 文本过长`);\r\n return false;\r\n }\r\n\r\n // 10. 过滤包含特殊字符过多的文本\r\n const specialCharCount = (\r\n text.match(/[^\\u4e00-\\u9fa5a-zA-Z0-9\\s,。!?、;:\"\"''()《》]/g) || []\r\n ).length;\r\n if (specialCharCount > text.length * 0.3) {\r\n if (debug)\r\n console.log(`[过滤] \"${text}\" - 原因: 特殊字符过多 (${specialCharCount})`);\r\n return false;\r\n }\r\n\r\n if (debug) console.log(`[保留] \"${text}\"`);\r\n return true;\r\n }\r\n}\r\n","/**\r\n * i18n 插件工具函数\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\n\r\n/**\r\n * 简单的 glob 实现\r\n */\r\nexport async function glob(\r\n patterns: string[],\r\n excludePatterns: string[]\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n const cwd = process.cwd();\r\n\r\n for (const pattern of patterns) {\r\n const files = await matchPattern(pattern, cwd);\r\n results.push(...files);\r\n }\r\n\r\n // 过滤排除的文件\r\n return results.filter((file) => {\r\n const relativePath = path.relative(cwd, file);\r\n return !excludePatterns.some((p) => matchGlob(relativePath, p));\r\n });\r\n}\r\n\r\n/**\r\n * 匹配单个 glob 模式\r\n */\r\nasync function matchPattern(pattern: string, cwd: string): Promise<string[]> {\r\n const results: string[] = [];\r\n\r\n // 解析模式\r\n const parts = pattern.split(\"/\");\r\n const hasGlobstar = parts.includes(\"**\");\r\n\r\n if (hasGlobstar) {\r\n // 递归搜索\r\n const baseDir = parts.slice(0, parts.indexOf(\"**\")).join(\"/\") || \".\";\r\n const filePattern = parts.slice(parts.indexOf(\"**\") + 1).join(\"/\");\r\n await walkDir(path.join(cwd, baseDir), filePattern, results);\r\n } else {\r\n // 直接匹配\r\n const fullPath = path.join(cwd, pattern);\r\n if (fs.existsSync(fullPath)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * 递归遍历目录\r\n */\r\nasync function walkDir(\r\n dir: string,\r\n pattern: string,\r\n results: string[]\r\n): Promise<void> {\r\n if (!fs.existsSync(dir)) return;\r\n\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n // 跳过 node_modules 和隐藏目录\r\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) {\r\n continue;\r\n }\r\n await walkDir(fullPath, pattern, results);\r\n } else if (entry.isFile()) {\r\n if (matchGlob(entry.name, pattern)) {\r\n results.push(fullPath);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 简单的 glob 匹配\r\n */\r\nfunction matchGlob(str: string, pattern: string): boolean {\r\n // 转换 glob 模式为正则\r\n const regexPattern = pattern\r\n .replace(/\\./g, \"\\\\.\")\r\n .replace(/\\*\\*/g, \".*\")\r\n .replace(/\\*/g, \"[^/]*\")\r\n .replace(/\\?/g, \".\");\r\n\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(str);\r\n}\r\n\r\n/**\r\n * 生成唯一 ID\r\n */\r\nexport function generateId(): string {\r\n return Math.random().toString(36).slice(2, 10);\r\n}\r\n\r\n/**\r\n * 格式化文件大小\r\n */\r\nexport function formatSize(bytes: number): string {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n}\r\n","/**\r\n * AI 翻译器\r\n */\r\n\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface TranslatorOptions {\r\n apiKey: string;\r\n apiUrl: string;\r\n model: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n}\r\n\r\nexport class I18nTranslator {\r\n private llm: ChatOpenAI | null = null;\r\n private options: TranslatorOptions;\r\n\r\n constructor(options: TranslatorOptions) {\r\n this.options = options;\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: options.temperature ?? 0.3,\r\n maxTokens: options.maxTokens ?? 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 批量翻译文本 - 支持增量翻译(跳过已翻译的文本)\r\n */\r\n async translate(\r\n texts: Map<string, string[]>,\r\n sourceLocale: string,\r\n targetLocale: string,\r\n existingTranslations?: Record<string, string>,\r\n ): Promise<Map<string, Map<string, string>>> {\r\n if (!this.llm) {\r\n console.warn(pc.yellow(\"⚠️ 未配置 API Key,跳过翻译\"));\r\n // 返回原文作为翻译\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n\r\n const results = new Map<string, Map<string, string>>();\r\n const allTexts = Array.from(texts.values()).flat();\r\n const uniqueTexts = [...new Set(allTexts)];\r\n\r\n if (uniqueTexts.length === 0) return results;\r\n\r\n // 过滤出需要翻译的文本(未翻译的)\r\n const textsToTranslate: string[] = [];\r\n const cachedTranslations = new Map<string, string>();\r\n\r\n uniqueTexts.forEach((text) => {\r\n const key = this.generateKey(text);\r\n if (existingTranslations && existingTranslations[key]) {\r\n // 使用已有翻译\r\n cachedTranslations.set(text, existingTranslations[key]);\r\n } else {\r\n // 需要翻译\r\n textsToTranslate.push(text);\r\n }\r\n });\r\n\r\n console.log(pc.blue(\"📊 翻译统计:\"));\r\n console.log(` 总计: ${pc.cyan(uniqueTexts.length.toString())} 条`);\r\n console.log(\r\n ` ✅ 已有: ${pc.green(cachedTranslations.size.toString())} 条`,\r\n );\r\n console.log(\r\n ` 🆕 新增: ${pc.yellow(textsToTranslate.length.toString())} 条`,\r\n );\r\n\r\n try {\r\n // 只翻译新增的文本\r\n let newTranslations: string[] = [];\r\n if (textsToTranslate.length > 0) {\r\n console.log(pc.cyan(`\\n🤖 正在翻译新增文本...`));\r\n newTranslations = await this.batchTranslate(\r\n textsToTranslate,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n }\r\n\r\n // 创建完整的翻译映射(原文 -> 译文)\r\n const translationMap = new Map<string, string>(cachedTranslations);\r\n textsToTranslate.forEach((text, index) => {\r\n translationMap.set(text, newTranslations[index] || text);\r\n });\r\n\r\n // 按文件组织翻译结果\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => {\r\n textMap.set(text, translationMap.get(text) || text);\r\n });\r\n results.set(file, textMap);\r\n }\r\n\r\n return results;\r\n } catch (error: any) {\r\n console.error(pc.red(\"❌ 翻译失败:\"), error.message);\r\n // 返回原文\r\n const results = new Map<string, Map<string, string>>();\r\n for (const [file, fileTexts] of texts) {\r\n const textMap = new Map<string, string>();\r\n fileTexts.forEach((text) => textMap.set(text, text));\r\n results.set(file, textMap);\r\n }\r\n return results;\r\n }\r\n }\r\n\r\n /**\r\n * 生成翻译 key(与 generator 保持一致)\r\n */\r\n private generateKey(text: string): string {\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n\r\n /**\r\n * 批量翻译(分批处理避免超限)\r\n */\r\n private async batchTranslate(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const batchSize = 20;\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i += batchSize) {\r\n const batch = texts.slice(i, i + batchSize);\r\n const translated = await this.translateBatch(\r\n batch,\r\n sourceLocale,\r\n targetLocale,\r\n );\r\n results.push(...translated);\r\n\r\n // 显示进度\r\n const progress = Math.min(i + batchSize, texts.length);\r\n console.log(\r\n pc.gray(\r\n ` 📊 翻译进度: ${pc.cyan(progress.toString())}/${pc.cyan(\r\n texts.length.toString(),\r\n )}`,\r\n ),\r\n );\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 翻译一批文本\r\n */\r\n private async translateBatch(\r\n texts: string[],\r\n sourceLocale: string,\r\n targetLocale: string,\r\n ): Promise<string[]> {\r\n const localeName = this.getLocaleName(targetLocale);\r\n\r\n const systemPrompt = new SystemMessage(\r\n `你是专业的软件本地化翻译专家。请将以下${this.getLocaleName(\r\n sourceLocale,\r\n )}文本翻译成${localeName}。\r\n要求:\r\n1. 保持专业术语的准确性\r\n2. 翻译要自然流畅,符合目标语言习惯\r\n3. 保留原文中的变量占位符(如 {name}、%s 等)\r\n4. 每行一个翻译,与输入顺序严格对应\r\n5. 只输出翻译结果,不要解释\r\n6. 如果原文包含换行,翻译也保持相同的换行`,\r\n );\r\n\r\n const userPrompt = new HumanMessage(\r\n texts.map((t, i) => `${i + 1}. ${t}`).join(\"\\n\"),\r\n );\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n const content = response.content.toString();\r\n\r\n // 解析翻译结果\r\n const lines = content.split(\"\\n\").filter((l) => l.trim());\r\n const results: string[] = [];\r\n\r\n for (let i = 0; i < texts.length; i++) {\r\n if (i < lines.length) {\r\n // 移除序号前缀\r\n const translated = lines[i].replace(/^\\d+\\.\\s*/, \"\").trim();\r\n results.push(translated);\r\n } else {\r\n // 如果翻译结果不够,使用原文\r\n results.push(texts[i]);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * 获取语言名称\r\n */\r\n private getLocaleName(locale: string): string {\r\n const names: Record<string, string> = {\r\n \"zh-CN\": \"简体中文\",\r\n \"zh-TW\": \"繁体中文\",\r\n \"en-US\": \"英语\",\r\n \"ja-JP\": \"日语\",\r\n \"ko-KR\": \"韩语\",\r\n \"fr-FR\": \"法语\",\r\n \"de-DE\": \"德语\",\r\n \"es-ES\": \"西班牙语\",\r\n \"pt-BR\": \"葡萄牙语\",\r\n \"ru-RU\": \"俄语\",\r\n \"ar-SA\": \"阿拉伯语\",\r\n };\r\n return names[locale] || locale;\r\n }\r\n}\r\n","/**\r\n * i18n 语言文件生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\n\r\nexport interface GeneratorOptions {\r\n localesDir: string;\r\n defaultLocale: string;\r\n}\r\n\r\nexport class I18nGenerator {\r\n private options: GeneratorOptions;\r\n\r\n constructor(options: GeneratorOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * 生成语言文件\r\n */\r\n async generate(texts: Map<string, string[]>, locale: string): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象\r\n const translations: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n\r\n for (const [file, fileTexts] of texts) {\r\n for (const text of fileTexts) {\r\n const key = this.generateKey(text);\r\n if (!translations[key]) {\r\n translations[key] = text;\r\n newCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translations)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translations[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 生成语言文件(用于目标语言)- 支持增量翻译\r\n */\r\n async generateTranslated(\r\n originalTexts: Map<string, string[]>,\r\n translations: Map<string, Map<string, string>>,\r\n locale: string,\r\n ): Promise<void> {\r\n const { localesDir } = this.options;\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(localesDir)) {\r\n fs.mkdirSync(localesDir, { recursive: true });\r\n }\r\n\r\n // 读取现有文件\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n let existing: Record<string, string> = {};\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n existing = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n // 生成新的翻译对象(保留已有翻译)\r\n const translationsObj: Record<string, string> = { ...existing };\r\n let newCount = 0;\r\n let skippedCount = 0;\r\n\r\n for (const [file, textMap] of translations) {\r\n for (const [originalText, translatedText] of textMap) {\r\n const key = this.generateKey(originalText);\r\n if (!translationsObj[key]) {\r\n translationsObj[key] = translatedText;\r\n newCount++;\r\n } else {\r\n skippedCount++;\r\n }\r\n }\r\n }\r\n\r\n // 按 key 排序\r\n const sorted = Object.keys(translationsObj)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = translationsObj[key];\r\n return obj;\r\n }, {} as Record<string, string>);\r\n\r\n // 写入文件\r\n fs.writeFileSync(filePath, JSON.stringify(sorted, null, 2), \"utf-8\");\r\n\r\n console.log(pc.green(`✅ 已更新 ${pc.cyan(locale)} 语言文件: ${pc.gray(filePath)}`));\r\n if (newCount > 0) {\r\n console.log(pc.gray(` ✨ 新增 ${pc.yellow(newCount.toString())} 条翻译`));\r\n }\r\n if (skippedCount > 0) {\r\n console.log(pc.gray(` ⏭️ 跳过 ${pc.blue(skippedCount.toString())} 条已有翻译`));\r\n }\r\n }\r\n\r\n /**\r\n * 加载已有翻译\r\n */\r\n loadExistingTranslations(locale: string): Record<string, string> {\r\n const { localesDir } = this.options;\r\n const filePath = path.join(localesDir, `${locale}.json`);\r\n\r\n if (fs.existsSync(filePath)) {\r\n try {\r\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n } catch (e) {\r\n console.warn(pc.yellow(`⚠️ 无法解析现有语言文件: ${filePath}`));\r\n }\r\n }\r\n\r\n return {};\r\n }\r\n\r\n /**\r\n * 生成翻译 key\r\n */\r\n private generateKey(text: string): string {\r\n // 使用拼音首字母或简化文本作为 key\r\n // 这里使用简单的哈希方式\r\n const cleaned = text\r\n .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, \"_\")\r\n .replace(/_+/g, \"_\")\r\n .replace(/^_|_$/g, \"\")\r\n .toLowerCase();\r\n\r\n // 截取前 30 个字符\r\n const truncated = cleaned.slice(0, 30);\r\n\r\n // 如果太短,添加哈希后缀\r\n if (truncated.length < 3) {\r\n const hash = this.simpleHash(text);\r\n return `text_${hash}`;\r\n }\r\n\r\n return truncated;\r\n }\r\n\r\n /**\r\n * 简单哈希函数\r\n */\r\n private simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = (hash << 5) - hash + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(36).slice(0, 6);\r\n }\r\n}\r\n"],"mappings":"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","names":["fs","path","pc","fs","path","fs","path","results","fs","path","pc","path","fs","pc"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-ai-i18n",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AI-powered internationalization plugin for Vite - Automatically scan, translate and manage i18n files",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vite-plugin-ai-i18n",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"vite-plugin-ai-shared": "^1.0.2",
|
|
42
43
|
"picocolors": "^1.1.1",
|
|
43
44
|
"@langchain/core": "^0.3.0",
|
|
44
|
-
"@langchain/openai": "^0.3.0"
|
|
45
|
-
"vite-plugin-ai-shared": "1.0.1"
|
|
45
|
+
"@langchain/openai": "^0.3.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^20.0.0",
|