diff --git a/apps/dokploy/public/locales/zh-Hans/common.json b/apps/dokploy/public/locales/zh-Hans/common.json index 67797618..d8faad7d 100644 --- a/apps/dokploy/public/locales/zh-Hans/common.json +++ b/apps/dokploy/public/locales/zh-Hans/common.json @@ -13,7 +13,6 @@ "dashboard.images": "镜像", "dashboard.volumes": "卷", "dashboard.networks": "网络", - "button.create": "创建", "button.edit": "编辑", "button.delete": "删除", @@ -23,71 +22,57 @@ "button.back": "返回", "button.next": "下一步", "button.finish": "完成", - "status.running": "运行中", "status.stopped": "已停止", "status.error": "错误", "status.pending": "等待中", "status.success": "成功", "status.failed": "失败", - "form.required": "必填", "form.invalid": "无效", "form.submit": "提交", "form.reset": "重置", - "notification.success": "操作成功", "notification.error": "操作失败", "notification.warning": "警告", "notification.info": "信息", - "time.now": "刚刚", "time.minutes": "分钟前", "time.hours": "小时前", "time.days": "天前", - "filter.all": "全部", "filter.active": "活跃", "filter.inactive": "不活跃", - "sort.asc": "升序", "sort.desc": "降序", - "search.placeholder": "搜索...", "search.noResults": "无结果", - "pagination.prev": "上一页", "pagination.next": "下一页", "pagination.of": "共 {0} 页", - "error.notFound": "未找到", "error.serverError": "服务器错误", "error.unauthorized": "未授权", "error.forbidden": "禁止访问", - "loading": "加载中...", "empty": "暂无数据", "more": "更多", "less": "收起", - "project.create": "创建项目", "project.edit": "编辑项目", "project.delete": "删除项目", "project.name": "项目名称", "project.description": "项目描述", - "service.create": "创建服务", "service.edit": "编辑服务", "service.delete": "删除服务", "service.name": "服务名称", "service.type": "服务类型", - "domain.add": "添加域名", "domain.remove": "移除域名", - "environment.variables": "环境变量", "environment.add": "添加环境变量", "environment.edit": "编辑环境变量", "environment.name": "变量名", "environment.value": "变量值" -} +} \ No newline at end of file diff --git a/apps/dokploy/public/locales/zh-Hans/settings.json b/apps/dokploy/public/locales/zh-Hans/settings.json index f7e8a38a..06033238 100644 --- a/apps/dokploy/public/locales/zh-Hans/settings.json +++ b/apps/dokploy/public/locales/zh-Hans/settings.json @@ -1,58 +1,67 @@ { - "settings.common.save": "保存", - "settings.common.enterTerminal": "终端", - "settings.server.domain.title": "服务器域名", - "settings.server.domain.description": "为您的服务器应用添加域名。", - "settings.server.domain.form.domain": "域名", - "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 邮箱", - "settings.server.domain.form.certificate.label": "证书提供商", - "settings.server.domain.form.certificate.placeholder": "选择证书", - "settings.server.domain.form.certificateOptions.none": "无", - "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt", - - "settings.server.webServer.title": "Web 服务器", - "settings.server.webServer.description": "重载或清理 Web 服务器。", - "settings.server.webServer.actions": "操作", - "settings.server.webServer.reload": "重新加载", - "settings.server.webServer.watchLogs": "查看日志", - "settings.server.webServer.updateServerIp": "更新服务器 IP", - "settings.server.webServer.server.label": "服务器", - "settings.server.webServer.traefik.label": "Traefik", - "settings.server.webServer.traefik.modifyEnv": "修改环境变量", - "settings.server.webServer.traefik.managePorts": "额外端口映射", - "settings.server.webServer.traefik.managePortsDescription": "为 Traefik 添加或删除额外端口", - "settings.server.webServer.traefik.targetPort": "目标端口", - "settings.server.webServer.traefik.publishedPort": "发布端口", - "settings.server.webServer.traefik.addPort": "添加端口", - "settings.server.webServer.traefik.portsUpdated": "端口更新成功", - "settings.server.webServer.traefik.portsUpdateError": "端口更新失败", - "settings.server.webServer.traefik.publishMode": "发布模式", - "settings.server.webServer.storage.label": "存储空间", - "settings.server.webServer.storage.cleanUnusedImages": "清理未使用的镜像", - "settings.server.webServer.storage.cleanUnusedVolumes": "清理未使用的卷", - "settings.server.webServer.storage.cleanStoppedContainers": "清理已停止的容器", - "settings.server.webServer.storage.cleanDockerBuilder": "清理 Docker Builder 和系统", - "settings.server.webServer.storage.cleanMonitoring": "清理监控数据", - "settings.server.webServer.storage.cleanAll": "清理所有内容", - - "settings.profile.title": "账户", - "settings.profile.description": "在此更改您的个人资料详情。", - "settings.profile.email": "邮箱", - "settings.profile.password": "密码", - "settings.profile.avatar": "头像", - - "settings.appearance.title": "外观", - "settings.appearance.description": "自定义您的仪表盘主题。", - "settings.appearance.theme": "主题", - "settings.appearance.themeDescription": "为您的仪表盘选择主题", - "settings.appearance.themes.light": "明亮", - "settings.appearance.themes.dark": "暗黑", - "settings.appearance.themes.system": "跟随系统", - "settings.appearance.language": "语言", - "settings.appearance.languageDescription": "为您的仪表盘选择语言", - - "settings.terminal.connectionSettings": "连接设置", - "settings.terminal.ipAddress": "IP 地址", - "settings.terminal.port": "端口", - "settings.terminal.username": "用户名" -} + "settings.common.save": "保存", + "settings.common.enterTerminal": "终端", + "settings.server.domain.title": "服务器域名", + "settings.server.domain.description": "为您的服务器应用添加域名。", + "settings.server.domain.form.domain": "域名", + "settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 邮箱", + "settings.server.domain.form.certificate.label": "证书提供商", + "settings.server.domain.form.certificate.placeholder": "选择证书", + "settings.server.domain.form.certificateOptions.none": "无", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt", + "settings.server.webServer.title": "Web 服务器", + "settings.server.webServer.description": "重载或清理 Web 服务器。", + "settings.server.webServer.actions": "操作", + "settings.server.webServer.reload": "重新加载", + "settings.server.webServer.watchLogs": "查看日志", + "settings.server.webServer.updateServerIp": "更新服务器 IP", + "settings.server.webServer.server.label": "服务器", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "修改环境变量", + "settings.server.webServer.traefik.managePorts": "额外端口映射", + "settings.server.webServer.traefik.managePortsDescription": "为 Traefik 添加或删除额外端口", + "settings.server.webServer.traefik.targetPort": "目标端口", + "settings.server.webServer.traefik.publishedPort": "发布端口", + "settings.server.webServer.traefik.addPort": "添加端口", + "settings.server.webServer.traefik.portsUpdated": "端口更新成功", + "settings.server.webServer.traefik.portsUpdateError": "端口更新失败", + "settings.server.webServer.traefik.publishMode": "发布模式", + "settings.server.webServer.storage.label": "存储空间", + "settings.server.webServer.storage.cleanUnusedImages": "清理未使用的镜像", + "settings.server.webServer.storage.cleanUnusedVolumes": "清理未使用的卷", + "settings.server.webServer.storage.cleanStoppedContainers": "清理已停止的容器", + "settings.server.webServer.storage.cleanDockerBuilder": "清理 Docker Builder 和系统", + "settings.server.webServer.storage.cleanMonitoring": "清理监控数据", + "settings.server.webServer.storage.cleanAll": "清理所有内容", + "settings.profile.title": "账户", + "settings.profile.description": "在此更改您的个人资料详情。", + "settings.profile.email": "邮箱", + "settings.profile.password": "密码", + "settings.profile.avatar": "头像", + "settings.appearance.title": "外观", + "settings.appearance.description": "自定义您的仪表盘主题。", + "settings.appearance.theme": "主题", + "settings.appearance.themeDescription": "为您的仪表盘选择主题", + "settings.appearance.themes.light": "明亮", + "settings.appearance.themes.dark": "暗黑", + "settings.appearance.themes.system": "跟随系统", + "settings.appearance.language": "语言", + "settings.appearance.languageDescription": "为您的仪表盘选择语言", + "settings.terminal.connectionSettings": "连接设置", + "settings.terminal.ipAddress": "IP 地址", + "settings.terminal.port": "端口", + "settings.terminal.username": "用户名", + "settings.settings": "设置", + "settings.general": "通用设置", + "settings.security": "安全", + "settings.users": "用户管理", + "settings.roles": "角色管理", + "settings.permissions": "权限", + "settings.api": "API设置", + "settings.certificates": "证书管理", + "settings.ssh": "SSH密钥", + "settings.backups": "备份", + "settings.logs": "日志", + "settings.updates": "更新", + "settings.network": "网络" +} \ No newline at end of file diff --git a/extract.js b/extract.js new file mode 100644 index 00000000..61d5d2d7 --- /dev/null +++ b/extract.js @@ -0,0 +1,270 @@ +console.log('Creating translation extractor script...'); + +const fs = require('fs'); +const path = require('path'); + +// 存储找到的所有翻译键 +const translationKeys = { + common: new Set(), + settings: new Set() +}; + +// 匹配更多格式的翻译函数调用 +// 支持 t('common.xxx')、t("common.xxx")、t(`common.xxx`) +const translationPatterns = [ + /t\(\s*['"]([a-zA-Z0-9._-]+)['"]?\s*[,)]/g, // t('key') 或 t("key") + /t\(\s*`([a-zA-Z0-9._-]+)`\s*[,)]/g, // t(`key`) + /useTranslation\(\s*["']([a-zA-Z0-9._-]+)["']\s*\)/g, // useTranslation('namespace') + /serverSideTranslations\([^)]*["']([a-zA-Z0-9._-]+)["']/g // serverSideTranslations(..., ['namespace']) +]; + +const namespaceRegex = /^(common|settings)\./; + +// 递归扫描目录下的所有 JS 和 TS 文件 +function scanDirectory(directory) { + try { + const entries = fs.readdirSync(directory, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(directory, entry.name); + + if (entry.isDirectory() && !fullPath.includes('node_modules') && !fullPath.includes('.next')) { + scanDirectory(fullPath); + } else if (entry.isFile() && /\.(js|jsx|ts|tsx)$/.test(entry.name)) { + try { + const content = fs.readFileSync(fullPath, 'utf8'); + + // 检查文件中是否使用了翻译 + let usesTranslation = false; + if (content.includes('useTranslation') || content.includes('t(') || content.includes('serverSideTranslations')) { + usesTranslation = true; + } + + if (usesTranslation) { + // 使用所有模式匹配翻译键 + for (const pattern of translationPatterns) { + let match; + while ((match = pattern.exec(content)) !== null) { + const key = match[1]; + + // 检查是否有命名空间 + const namespaceMatch = key.match(namespaceRegex); + if (namespaceMatch) { + const namespace = namespaceMatch[1]; + if (namespace === 'common' || namespace === 'settings') { + translationKeys[namespace].add(key); + } + } + + // 如果文件中导入了特定命名空间,所有的 t(key) 都属于该命名空间 + if (content.includes(`useTranslation('common')`) || content.includes(`useTranslation("common")`)) { + if (!key.includes('.')) { + translationKeys.common.add(`common.${key}`); + } + } + + if (content.includes(`useTranslation('settings')`) || content.includes(`useTranslation("settings")`)) { + if (!key.includes('.')) { + translationKeys.settings.add(`settings.${key}`); + } + } + } + } + + // 控制台输出被处理的文件及其找到的翻译键 + if (usesTranslation) { + console.log(`检查文件: ${fullPath}`); + } + } + } catch (error) { + console.error(`Error reading file ${fullPath}:`, error); + } + } + } + } catch (error) { + console.error(`Error scanning directory ${directory}:`, error); + } +} + +// 手动添加一些常见的翻译键(基于直接观察和常见用法) +function addCommonTranslationKeys() { + const commonKeys = [ + 'dashboard.title', 'dashboard.overview', 'dashboard.projects', 'dashboard.servers', + 'dashboard.docker', 'dashboard.monitoring', 'dashboard.settings', 'dashboard.logout', + 'dashboard.profile', 'dashboard.terminal', 'dashboard.containers', 'dashboard.images', + 'dashboard.volumes', 'dashboard.networks', + + 'button.create', 'button.edit', 'button.delete', 'button.cancel', + 'button.save', 'button.confirm', 'button.back', 'button.next', 'button.finish', + + 'status.running', 'status.stopped', 'status.error', 'status.pending', + 'status.success', 'status.failed', + + 'form.required', 'form.invalid', 'form.submit', 'form.reset', + + 'notification.success', 'notification.error', 'notification.warning', 'notification.info', + + 'time.now', 'time.minutes', 'time.hours', 'time.days', + + 'filter.all', 'filter.active', 'filter.inactive', + + 'sort.asc', 'sort.desc', + + 'search.placeholder', 'search.noResults', + + 'pagination.prev', 'pagination.next', 'pagination.of', + + 'error.notFound', 'error.serverError', 'error.unauthorized', 'error.forbidden', + + 'loading', 'empty', 'more', 'less', + + 'project.create', 'project.edit', 'project.delete', 'project.name', 'project.description', + + 'service.create', 'service.edit', 'service.delete', 'service.name', 'service.type', + + 'domain.add', 'domain.remove', + + 'environment.variables', 'environment.add', 'environment.edit', + 'environment.name', 'environment.value' + ]; + + commonKeys.forEach(key => { + translationKeys.common.add(`common.${key}`); + }); +} + +// 读取现有翻译文件 +function readTranslationFile(filePath) { + try { + if (fs.existsSync(filePath)) { + const content = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(content); + } + } catch (error) { + console.error(`Error reading translation file ${filePath}:`, error); + } + return {}; +} + +// 主函数 +function extractTranslations() { + const appsDir = path.join(__dirname, 'apps', 'dokploy'); + + // 扫描代码库 + scanDirectory(appsDir); + + // 手动添加常见的翻译键 + addCommonTranslationKeys(); + + // 读取现有翻译文件 + const zhHansCommonPath = path.join(appsDir, 'public', 'locales', 'zh-Hans', 'common.json'); + const zhHansSettingsPath = path.join(appsDir, 'public', 'locales', 'zh-Hans', 'settings.json'); + + const existingCommon = readTranslationFile(zhHansCommonPath); + const existingSettings = readTranslationFile(zhHansSettingsPath); + + // 准备新的翻译文件 + const newCommon = {}; + const newSettings = {}; + + // 处理 common 命名空间 + for (const key of translationKeys.common) { + const shortKey = key.replace('common.', ''); + newCommon[key] = existingCommon[key] || `[需要翻译] ${shortKey}`; + } + + // 处理 settings 命名空间 + for (const key of translationKeys.settings) { + const shortKey = key.replace('settings.', ''); + newSettings[key] = existingSettings[key] || `[需要翻译] ${shortKey}`; + } + + // 输出结果 + console.log('=== 提取的 common 翻译键 ==='); + console.log(Array.from(translationKeys.common).sort().join('\n')); + console.log(`\n共找到 ${translationKeys.common.size} 个 common 翻译键`); + + console.log('\n=== 提取的 settings 翻译键 ==='); + console.log(Array.from(translationKeys.settings).sort().join('\n')); + console.log(`\n共找到 ${translationKeys.settings.size} 个 settings 翻译键`); + + // 创建包含缺失翻译的新文件 + const missingCommonTranslations = {}; + const missingSettingsTranslations = {}; + + for (const key of translationKeys.common) { + if (!existingCommon[key]) { + const shortKey = key.replace('common.', ''); + missingCommonTranslations[key] = `[需要翻译] ${shortKey}`; + } + } + + for (const key of translationKeys.settings) { + if (!existingSettings[key]) { + const shortKey = key.replace('settings.', ''); + missingSettingsTranslations[key] = `[需要翻译] ${shortKey}`; + } + } + + // 输出缺失的翻译 + console.log('\n=== 缺失的 common 翻译 ==='); + console.log(JSON.stringify(missingCommonTranslations, null, 2)); + console.log(`\n共缺失 ${Object.keys(missingCommonTranslations).length} 个 common 翻译`); + + console.log('\n=== 缺失的 settings 翻译 ==='); + console.log(JSON.stringify(missingSettingsTranslations, null, 2)); + console.log(`\n共缺失 ${Object.keys(missingSettingsTranslations).length} 个 settings 翻译`); + + // 输出可以直接复制到文件中的完整翻译对象 + console.log('\n=== 完整的 common.json 内容 ==='); + const fullCommon = { ...existingCommon }; + translationKeys.common.forEach(key => { + if (!fullCommon[key]) { + const shortKey = key.replace('common.', ''); + fullCommon[key] = `[翻译] ${shortKey}`; + } + }); + console.log(JSON.stringify(fullCommon, null, 2)); + + console.log('\n=== 完整的 settings.json 内容 ==='); + const fullSettings = { ...existingSettings }; + translationKeys.settings.forEach(key => { + if (!fullSettings[key]) { + const shortKey = key.replace('settings.', ''); + fullSettings[key] = `[翻译] ${shortKey}`; + } + }); + console.log(JSON.stringify(fullSettings, null, 2)); + + // 优化生成的翻译文件格式:移除命名空间前缀 + const optimizedCommon = {}; + Object.keys(fullCommon).forEach(key => { + const shortKey = key.replace('common.', ''); + optimizedCommon[shortKey] = fullCommon[key]; + }); + + const optimizedSettings = {}; + Object.keys(fullSettings).forEach(key => { + const shortKey = key.replace('settings.', ''); + optimizedSettings[shortKey] = fullSettings[key]; + }); + + // 写入文件 + fs.writeFileSync('missing-common-translations.json', JSON.stringify(missingCommonTranslations, null, 2), 'utf8'); + fs.writeFileSync('missing-settings-translations.json', JSON.stringify(missingSettingsTranslations, null, 2), 'utf8'); + fs.writeFileSync('full-common-translations.json', JSON.stringify(fullCommon, null, 2), 'utf8'); + fs.writeFileSync('full-settings-translations.json', JSON.stringify(fullSettings, null, 2), 'utf8'); + fs.writeFileSync('optimized-common-translations.json', JSON.stringify(optimizedCommon, null, 2), 'utf8'); + fs.writeFileSync('optimized-settings-translations.json', JSON.stringify(optimizedSettings, null, 2), 'utf8'); + + console.log('\n翻译提取完成!'); + console.log('文件已保存:'); + console.log('- missing-common-translations.json: 缺失的 common 翻译'); + console.log('- missing-settings-translations.json: 缺失的 settings 翻译'); + console.log('- full-common-translations.json: 完整的 common 翻译(包含命名空间)'); + console.log('- full-settings-translations.json: 完整的 settings 翻译(包含命名空间)'); + console.log('- optimized-common-translations.json: 优化格式的 common 翻译(不含命名空间)'); + console.log('- optimized-settings-translations.json: 优化格式的 settings 翻译(不含命名空间)'); +} + +extractTranslations(); diff --git a/merge-translations.js b/merge-translations.js new file mode 100644 index 00000000..441ebed4 --- /dev/null +++ b/merge-translations.js @@ -0,0 +1,252 @@ +// 读取已创建的翻译文件 +const fs = require('fs'); +const path = require('path'); + +try { + const basePath = process.cwd(); + const commonPart1Path = path.join(basePath, 'common-zh-Hans.json'); + const commonPart2Path = path.join(basePath, 'common-zh-Hans-buttons.json'); + const fullCommonPath = path.join(basePath, 'optimized-common-translations.json'); + const fullSettingsPath = path.join(basePath, 'optimized-settings-translations.json'); + + const commonPart1 = JSON.parse(fs.readFileSync(commonPart1Path, 'utf8')); + const commonPart2 = JSON.parse(fs.readFileSync(commonPart2Path, 'utf8')); + const fullCommon = JSON.parse(fs.readFileSync(fullCommonPath, 'utf8')); + const fullSettings = JSON.parse(fs.readFileSync(fullSettingsPath, 'utf8')); + + // 创建一个全新的翻译对象 + const mergedCommon = {}; + const mergedSettings = {}; + + // 添加通用组件翻译 + mergedCommon["dashboard.title"] = "仪表盘"; + mergedCommon["dashboard.overview"] = "概览"; + mergedCommon["dashboard.projects"] = "项目"; + mergedCommon["dashboard.servers"] = "服务器"; + mergedCommon["dashboard.docker"] = "Docker"; + mergedCommon["dashboard.monitoring"] = "监控"; + mergedCommon["dashboard.settings"] = "设置"; + mergedCommon["dashboard.logout"] = "退出登录"; + mergedCommon["dashboard.profile"] = "个人资料"; + mergedCommon["dashboard.terminal"] = "终端"; + mergedCommon["dashboard.containers"] = "容器"; + mergedCommon["dashboard.images"] = "镜像"; + mergedCommon["dashboard.volumes"] = "卷"; + mergedCommon["dashboard.networks"] = "网络"; + + // 按钮翻译 + mergedCommon["button.create"] = "创建"; + mergedCommon["button.edit"] = "编辑"; + mergedCommon["button.delete"] = "删除"; + mergedCommon["button.cancel"] = "取消"; + mergedCommon["button.save"] = "保存"; + mergedCommon["button.confirm"] = "确认"; + mergedCommon["button.back"] = "返回"; + mergedCommon["button.next"] = "下一步"; + mergedCommon["button.finish"] = "完成"; + + // 状态翻译 + mergedCommon["status.running"] = "运行中"; + mergedCommon["status.stopped"] = "已停止"; + mergedCommon["status.error"] = "错误"; + mergedCommon["status.pending"] = "等待中"; + mergedCommon["status.success"] = "成功"; + mergedCommon["status.failed"] = "失败"; + + // 表单翻译 + mergedCommon["form.required"] = "必填"; + mergedCommon["form.invalid"] = "无效"; + mergedCommon["form.submit"] = "提交"; + mergedCommon["form.reset"] = "重置"; + + // 通知翻译 + mergedCommon["notification.success"] = "操作成功"; + mergedCommon["notification.error"] = "操作失败"; + mergedCommon["notification.warning"] = "警告"; + mergedCommon["notification.info"] = "信息"; + + // 时间翻译 + mergedCommon["time.now"] = "刚刚"; + mergedCommon["time.minutes"] = "分钟前"; + mergedCommon["time.hours"] = "小时前"; + mergedCommon["time.days"] = "天前"; + + // 过滤翻译 + mergedCommon["filter.all"] = "全部"; + mergedCommon["filter.active"] = "活跃"; + mergedCommon["filter.inactive"] = "不活跃"; + + // 排序翻译 + mergedCommon["sort.asc"] = "升序"; + mergedCommon["sort.desc"] = "降序"; + + // 搜索翻译 + mergedCommon["search.placeholder"] = "搜索..."; + mergedCommon["search.noResults"] = "无结果"; + + // 分页翻译 + mergedCommon["pagination.prev"] = "上一页"; + mergedCommon["pagination.next"] = "下一页"; + mergedCommon["pagination.of"] = "共 {0} 页"; + + // 错误翻译 + mergedCommon["error.notFound"] = "未找到"; + mergedCommon["error.serverError"] = "服务器错误"; + mergedCommon["error.unauthorized"] = "未授权"; + mergedCommon["error.forbidden"] = "禁止访问"; + + // 通用状态翻译 + mergedCommon["loading"] = "加载中..."; + mergedCommon["empty"] = "暂无数据"; + mergedCommon["more"] = "更多"; + mergedCommon["less"] = "收起"; + + // 项目翻译 + mergedCommon["project.create"] = "创建项目"; + mergedCommon["project.edit"] = "编辑项目"; + mergedCommon["project.delete"] = "删除项目"; + mergedCommon["project.name"] = "项目名称"; + mergedCommon["project.description"] = "项目描述"; + + // 服务翻译 + mergedCommon["service.create"] = "创建服务"; + mergedCommon["service.edit"] = "编辑服务"; + mergedCommon["service.delete"] = "删除服务"; + mergedCommon["service.name"] = "服务名称"; + mergedCommon["service.type"] = "服务类型"; + + // 域名翻译 + mergedCommon["domain.add"] = "添加域名"; + mergedCommon["domain.remove"] = "移除域名"; + + // 环境变量翻译 + mergedCommon["environment.variables"] = "环境变量"; + mergedCommon["environment.add"] = "添加环境变量"; + mergedCommon["environment.edit"] = "编辑环境变量"; + mergedCommon["environment.name"] = "变量名"; + mergedCommon["environment.value"] = "变量值"; + + // 设置页面的通用翻译 + mergedSettings["common.save"] = "保存"; + mergedSettings["common.enterTerminal"] = "终端"; + + // 服务器域名设置 + mergedSettings["server.domain.title"] = "服务器域名"; + mergedSettings["server.domain.description"] = "为您的服务器应用添加域名。"; + mergedSettings["server.domain.form.domain"] = "域名"; + mergedSettings["server.domain.form.letsEncryptEmail"] = "Let's Encrypt 邮箱"; + mergedSettings["server.domain.form.certificate.label"] = "证书提供商"; + mergedSettings["server.domain.form.certificate.placeholder"] = "选择证书"; + mergedSettings["server.domain.form.certificateOptions.none"] = "无"; + mergedSettings["server.domain.form.certificateOptions.letsencrypt"] = "Let's Encrypt"; + + // Web服务器设置 + mergedSettings["server.webServer.title"] = "Web 服务器"; + mergedSettings["server.webServer.description"] = "重载或清理 Web 服务器。"; + mergedSettings["server.webServer.actions"] = "操作"; + mergedSettings["server.webServer.reload"] = "重新加载"; + mergedSettings["server.webServer.watchLogs"] = "查看日志"; + mergedSettings["server.webServer.updateServerIp"] = "更新服务器 IP"; + mergedSettings["server.webServer.server.label"] = "服务器"; + + // Traefik设置 + mergedSettings["server.webServer.traefik.label"] = "Traefik"; + mergedSettings["server.webServer.traefik.modifyEnv"] = "修改环境变量"; + mergedSettings["server.webServer.traefik.managePorts"] = "额外端口映射"; + mergedSettings["server.webServer.traefik.managePortsDescription"] = "为 Traefik 添加或删除额外端口"; + mergedSettings["server.webServer.traefik.targetPort"] = "目标端口"; + mergedSettings["server.webServer.traefik.publishedPort"] = "发布端口"; + mergedSettings["server.webServer.traefik.addPort"] = "添加端口"; + mergedSettings["server.webServer.traefik.portsUpdated"] = "端口更新成功"; + mergedSettings["server.webServer.traefik.portsUpdateError"] = "端口更新失败"; + mergedSettings["server.webServer.traefik.publishMode"] = "发布模式"; + + // 存储空间设置 + mergedSettings["server.webServer.storage.label"] = "存储空间"; + mergedSettings["server.webServer.storage.cleanUnusedImages"] = "清理未使用的镜像"; + mergedSettings["server.webServer.storage.cleanUnusedVolumes"] = "清理未使用的卷"; + mergedSettings["server.webServer.storage.cleanStoppedContainers"] = "清理已停止的容器"; + mergedSettings["server.webServer.storage.cleanDockerBuilder"] = "清理 Docker Builder 和系统"; + mergedSettings["server.webServer.storage.cleanMonitoring"] = "清理监控数据"; + mergedSettings["server.webServer.storage.cleanAll"] = "清理所有内容"; + + // 个人资料设置 + mergedSettings["profile.title"] = "账户"; + mergedSettings["profile.description"] = "在此更改您的个人资料详情。"; + mergedSettings["profile.email"] = "邮箱"; + mergedSettings["profile.password"] = "密码"; + mergedSettings["profile.avatar"] = "头像"; + + // 外观设置 + mergedSettings["appearance.title"] = "外观"; + mergedSettings["appearance.description"] = "自定义您的仪表盘主题。"; + mergedSettings["appearance.theme"] = "主题"; + mergedSettings["appearance.themeDescription"] = "为您的仪表盘选择主题"; + mergedSettings["appearance.themes.light"] = "明亮"; + mergedSettings["appearance.themes.dark"] = "暗黑"; + mergedSettings["appearance.themes.system"] = "跟随系统"; + mergedSettings["appearance.language"] = "语言"; + mergedSettings["appearance.languageDescription"] = "为您的仪表盘选择语言"; + + // 终端设置 + mergedSettings["terminal.connectionSettings"] = "连接设置"; + mergedSettings["terminal.ipAddress"] = "IP 地址"; + mergedSettings["terminal.port"] = "端口"; + mergedSettings["terminal.username"] = "用户名"; + + // 其他设置 + mergedSettings["settings"] = "设置"; + mergedSettings["general"] = "通用设置"; + mergedSettings["security"] = "安全"; + mergedSettings["users"] = "用户管理"; + mergedSettings["roles"] = "角色管理"; + mergedSettings["permissions"] = "权限"; + mergedSettings["api"] = "API设置"; + mergedSettings["certificates"] = "证书管理"; + mergedSettings["ssh"] = "SSH密钥"; + mergedSettings["backups"] = "备份"; + mergedSettings["logs"] = "日志"; + mergedSettings["updates"] = "更新"; + mergedSettings["network"] = "网络"; + + // 输出合并后的文件内容 + console.log('Common translations total:', Object.keys(mergedCommon).length); + console.log('Settings translations total:', Object.keys(mergedSettings).length); + + // 保存为最终的翻译文件 + fs.writeFileSync(path.join(basePath, 'final-zh-Hans-common.json'), JSON.stringify(mergedCommon, null, 2)); + fs.writeFileSync(path.join(basePath, 'final-zh-Hans-settings.json'), JSON.stringify(mergedSettings, null, 2)); + + // 输出翻译完成的统计 + const commonKeys = Object.keys(mergedCommon); + const settingsKeys = Object.keys(mergedSettings); + console.log('最终翻译文件已保存:'); + console.log(`- 通用翻译 (${commonKeys.length} 个词条)`); + console.log(`- 设置翻译 (${settingsKeys.length} 个词条)`); + + // 创建最终放入项目中的文件(按项目结构) + const projectCommonPath = path.join(basePath, 'apps', 'dokploy', 'public', 'locales', 'zh-Hans'); + + // 确保目录存在 + if (!fs.existsSync(projectCommonPath)) { + fs.mkdirSync(projectCommonPath, { recursive: true }); + console.log(`创建目录: ${projectCommonPath}`); + } + + // 写入到项目中的目标位置 + const projectCommonFilePath = path.join(projectCommonPath, 'common.json'); + const projectSettingsFilePath = path.join(projectCommonPath, 'settings.json'); + + console.log(`尝试写入到:\n- ${projectCommonFilePath}\n- ${projectSettingsFilePath}`); + + try { + fs.writeFileSync(projectCommonFilePath, JSON.stringify(mergedCommon, null, 2)); + fs.writeFileSync(projectSettingsFilePath, JSON.stringify(mergedSettings, null, 2)); + console.log('已成功写入到项目文件夹中!'); + } catch (error) { + console.error('写入到项目文件夹失败:', error.message); + console.log('请手动将文件复制到目标位置。'); + } +} catch (error) { + console.error('错误:', error); +}