From 7e4c89b0cb9da47a98a6d1d962d55f6366c91e3e Mon Sep 17 00:00:00 2001 From: IGCrystal Date: Tue, 17 Jun 2025 11:10:21 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=84=20refactor(i18n):=20replace=20manu?= =?UTF-8?q?al=20types=20with=20auto-inference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migrate from manual TypeScript interfaces to automatic type generation from JSON files. Eliminates sync issues and maintenance overhead. --- dashboard/src/i18n/loader.ts | 14 +- dashboard/src/i18n/types.ts | 290 +++-------------------------------- 2 files changed, 35 insertions(+), 269 deletions(-) diff --git a/dashboard/src/i18n/loader.ts b/dashboard/src/i18n/loader.ts index c9632e8cb..a7c1137a3 100644 --- a/dashboard/src/i18n/loader.ts +++ b/dashboard/src/i18n/loader.ts @@ -197,6 +197,7 @@ export class I18nLoader { */ private mergeModules(modules: any[], moduleNames: string[]): any { const result: any = {}; + const pathRegistry = new Map(); modules.forEach((module, index) => { const moduleName = moduleNames[index]; @@ -211,8 +212,19 @@ export class I18nLoader { current = current[nameParts[i]]; } - // 设置最终值 + // 冲突检测:检查最终键是否已存在 const finalKey = nameParts[nameParts.length - 1]; + const fullPath = nameParts.join('.'); + + if (current[finalKey] && pathRegistry.has(fullPath)) { + const existingModule = pathRegistry.get(fullPath); + console.warn(`⚠️ I18n模块路径冲突: "${fullPath}" 已被模块 "${existingModule}" 占用,模块 "${moduleName}" 可能会覆盖部分键值`); + } + + // 记录路径和模块名的映射 + pathRegistry.set(fullPath, moduleName); + + // 设置最终值(保持原有的浅合并行为) current[finalKey] = { ...current[finalKey], ...module }; }); diff --git a/dashboard/src/i18n/types.ts b/dashboard/src/i18n/types.ts index 7bce65a10..4cd553711 100644 --- a/dashboard/src/i18n/types.ts +++ b/dashboard/src/i18n/types.ts @@ -1,279 +1,31 @@ /** - * I18n TypeScript Type Definitions - * 国际化类型定义,确保类型安全 + * I18n TypeScript Type Definitions - Auto-generated from JSON + * 国际化类型定义,从JSON文件自动推断,确保类型安全且自动同步 */ -// 核心模块类型定义 -export interface CoreTranslations { - common: { - save: string; - cancel: string; - close: string; - delete: string; - edit: string; - add: string; - confirm: string; - loading: string; - success: string; - error: string; - warning: string; - info: string; - name: string; - description: string; - author: string; - status: string; - actions: string; - enable: string; - disable: string; - enabled: string; - disabled: string; - reload: string; - configure: string; - install: string; - uninstall: string; - update: string; - language: string; - }; - actions: { - create: string; - read: string; - update: string; - delete: string; - search: string; - filter: string; - sort: string; - export: string; - import: string; - backup: string; - restore: string; - }; - status: { - loading: string; - success: string; - error: string; - warning: string; - info: string; - pending: string; - processing: string; - completed: string; - failed: string; - cancelled: string; - }; - navigation: { - dashboard: string; - platforms: string; - providers: string; - toolUse: string; - config: string; - extension: string; - chat: string; - conversation: string; - console: string; - alkaid: string; - about: string; - settings: string; - documentation: string; - github: string; - drag: string; - }; -} +// 直接导入已经组织好的翻译数据 +import { translations } from './translations'; -// 功能模块类型定义 -export interface FeatureTranslations { - chat: { - title: string; - subtitle: string; - input: { - placeholder: string; - send: string; - clear: string; - }; - message: { - user: string; - assistant: string; - system: string; - }; - voice: { - start: string; - stop: string; - recording: string; - }; - }; - extension: { - title: string; - subtitle: string; - showSystemPlugins: string; - hideSystemPlugins: string; - platformCommandConfig: string; - noPlugins: string; - tryInstallOrShowSystem: string; - configDialog: { - title: string; - noConfig: string; - }; - platformConfig: { - title: string; - description: string; - noPlatforms: string; - addPlatformFirst: string; - goToPlatformManagement: string; - }; - marketplace: { - title: string; - installPlugin: string; - fromGitHub: string; - fromLocal: string; - repoUrl: string; - selectFile: string; - pluginDevelopmentDoc: string; - submitPluginRepo: string; - }; - }; - conversation: { - title: string; - subtitle: string; - table: { - id: string; - platform: string; - user: string; - message: string; - time: string; - actions: string; - }; - filter: { - platform: string; - user: string; - dateRange: string; - }; - export: { - title: string; - format: string; - range: string; - }; - }; - provider: { - title: string; - tabTypes: { - chat_completion: string; - speech_to_text: string; - text_to_speech: string; - embedding: string; - }; - openaiDescription: string; - defaultDescription: string; - }; - platform: { - title: string; - subtitle: string; - adapters: string; - addAdapter: string; - }; - config: { - title: string; - subtitle: string; - sections: { - general: string; - advanced: string; - security: string; - }; - }; - console: { - title: string; - subtitle: string; - clear: string; - download: string; - }; - about: { - title: string; - version: string; - author: string; - license: string; - repository: string; - }; - alkaid: { - comingSoon: string; - knowledgeBase: { - title: string; - subtitle: string; - }; - memory: { - title: string; - subtitle: string; - }; - }; -} +// 导出翻译数据常量,供类型推断使用 +export const translationData = translations; -// 消息模块类型定义 -export interface MessageTranslations { - errors: { - network: { - timeout: string; - connection: string; - server: string; - }; - validation: { - required: string; - invalid: string; - tooLong: string; - tooShort: string; - }; - auth: { - unauthorized: string; - forbidden: string; - tokenExpired: string; - }; - }; - success: { - save: { - completed: string; - config: string; - settings: string; - }; - action: { - created: string; - updated: string; - deleted: string; - }; - }; - validation: { - required: string; - email: string; - url: string; - number: string; - min: string; - max: string; - }; -} +// 从实际的翻译数据推断完整的翻译结构类型 +export type TranslationSchema = typeof translations[keyof typeof translations]; -// 完整的翻译类型 -export interface TranslationSchema extends CoreTranslations, FeatureTranslations, MessageTranslations {} +// TypeScript 助手:递归提取嵌套键路径 +type NestedKeyOf = T extends object + ? { + [K in keyof T & string]: T[K] extends object + ? `${K}` | `${K}.${NestedKeyOf}` + : `${K}` + }[keyof T & string] + : never; -// 翻译键类型 -export type TranslationKey = - // Core keys - | `core.common.${keyof CoreTranslations['common']}` - | `core.actions.${keyof CoreTranslations['actions']}` - | `core.status.${keyof CoreTranslations['status']}` - | `core.navigation.${keyof CoreTranslations['navigation']}` - - // Feature keys - | `features.chat.${keyof FeatureTranslations['chat'] | `input.${keyof FeatureTranslations['chat']['input']}` | `message.${keyof FeatureTranslations['chat']['message']}` | `voice.${keyof FeatureTranslations['chat']['voice']}`}` - | `features.extension.${keyof FeatureTranslations['extension'] | `configDialog.${keyof FeatureTranslations['extension']['configDialog']}` | `platformConfig.${keyof FeatureTranslations['extension']['platformConfig']}` | `marketplace.${keyof FeatureTranslations['extension']['marketplace']}`}` - | `features.conversation.${keyof FeatureTranslations['conversation'] | `table.${keyof FeatureTranslations['conversation']['table']}` | `filter.${keyof FeatureTranslations['conversation']['filter']}` | `export.${keyof FeatureTranslations['conversation']['export']}`}` - | `features.provider.${keyof FeatureTranslations['provider'] | `tabTypes.${keyof FeatureTranslations['provider']['tabTypes']}`}` - | `features.platform.${keyof FeatureTranslations['platform']}` - | `features.config.${keyof FeatureTranslations['config'] | `sections.${keyof FeatureTranslations['config']['sections']}`}` - | `features.console.${keyof FeatureTranslations['console']}` - | `features.about.${keyof FeatureTranslations['about']}` - | `features.alkaid.${keyof FeatureTranslations['alkaid'] | `knowledgeBase.${keyof FeatureTranslations['alkaid']['knowledgeBase']}` | `memory.${keyof FeatureTranslations['alkaid']['memory']}`}` - - // Message keys - | `messages.errors.${keyof MessageTranslations['errors'] | `network.${keyof MessageTranslations['errors']['network']}` | `validation.${keyof MessageTranslations['errors']['validation']}` | `auth.${keyof MessageTranslations['errors']['auth']}`}` - | `messages.success.${keyof MessageTranslations['success'] | `save.${keyof MessageTranslations['success']['save']}` | `action.${keyof MessageTranslations['success']['action']}`}` - | `messages.validation.${keyof MessageTranslations['validation']}`; +// 自动推断的翻译键联合类型 - 包含所有有效的点分隔键路径 +export type TranslationKey = NestedKeyOf; -// 语言环境类型 -export type Locale = 'zh-CN' | 'en-US'; +// 语言环境类型 - 从实际的翻译数据键推断 +export type Locale = keyof typeof translations; // 翻译函数类型 export type TranslationFunction = { @@ -281,6 +33,8 @@ export type TranslationFunction = { (key: TranslationKey, params: Record): string; }; +// 以下是保留的工具类型定义,这些不依赖具体的翻译结构 + // 模块加载状态 export interface ModuleLoadingState { core: boolean;