-
-
Notifications
You must be signed in to change notification settings - Fork 672
✨ feat(WebUI): complete dashboard internationalization system refactor #1822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
- 在 KnowledgeBase.vue 中修正无效的 v-class 指令为 class 属性的问题 - 在 ConsoleDisplayer.vue 中修正 historyNum 属性类型不匹配的问题 - 解决控制台中的 Vue 警告信息 - 在访问 status 前对 err.response 进行空值检查 - 防止“无法读取未定义对象的属性”错误 - 提高 catch 块中错误处理的健壮性 - 对 API 响应数据进行空值检查 - 在处理之前确保数组类型验证 - 修复“无法读取 null 对象的属性”错误 - 改进 beforeUnmount 生命周期中的 D3.js 清理工作 - 对图形数据处理添加防御性编程
✨ 核心特性: - 实现模块化i18n架构,支持22个功能模块 - 完成中英双语翻译文件(44个翻译文件) - 新增懒加载翻译模块,提升性能 - 类型安全的翻译键值验证系统 🌐 国际化覆盖: - 所有主要页面(15+)完成国际化 - 导航侧边栏、顶栏、共享组件全部支持 - 仪表板统计组件完整国际化 - 登录页面及认证流程完整国际化 🎨 UI/UX 优化: - 统一顶栏按钮样式(语言切换+主题切换) - 移动端登录页采用全屏设计 - Logo组件智能换行支持中英文 - 响应式语言切换组件 📱 移动端适配: - 登录卡片移动端全屏布局 - 悬浮工具栏底部固定定位 - 触摸友好的交互设计 - 多设备响应式支持 🔧 技术改进: - 模块化翻译文件结构 (core/*, features/*) - 懒加载机制减少初始包体积 - TypeScript类型定义完整 - 翻译键值自动验证
## 审查者指南
此PR通过引入动态加载器、验证器和类型安全的翻译支持,将仪表板全面改造为完全模块化的国际化系统,然后重构几乎所有UI组件以使用翻译键而不是硬编码字符串,添加全局LanguageSwitcher,并更新应用程序引导程序以在挂载之前异步初始化i18n。
#### 带有i18n的应用程序初始化顺序图
```mermaid
sequenceDiagram
participant App
participant i18nInitializer as "setupI18n()"
participant VueI18nInstance
participant TranslationLoader
participant TranslationCache
participant Filesystem as "Translation Files (JSON)"
App->>i18nInitializer: Start Initialization (async)
activate i18nInitializer
i18nInitializer->>VueI18nInstance: Create/Configure vue-i18n
activate VueI18nInstance
i18nInitializer->>TranslationLoader: Load Initial/Default Translations
activate TranslationLoader
TranslationLoader->>Filesystem: Read JSON files (e.g., core, default lang)
Filesystem-->>TranslationLoader: JSON data
TranslationLoader->>TranslationCache: Store translations
activate TranslationCache
TranslationCache-->>TranslationLoader: Stored
deactivate TranslationCache
TranslationLoader-->>i18nInitializer: Translations Ready
deactivate TranslationLoader
VueI18nInstance-->>i18nInitializer: Instance Ready
deactivate VueI18nInstance
i18nInitializer-->>App: Initialization Complete
deactivate i18nInitializer
App->>App: Mount Vue Components 类图:i18n ComposablesclassDiagram
namespace i18nComposables {
class useI18n {
+t(key: String, ...args): String
+locale: Ref<String>
+setLocale(newLocale: String): void
+availableLocales: Ref<String[]>
}
class useModuleI18n {
+tm(key: String, ...args): String
+locale: Ref<String>
}
}
useI18n ..> VueI18nInstance : interacts with
useModuleI18n ..> VueI18nInstance : interacts with
文件级别更改
提示和命令与Sourcery互动
自定义您的体验访问您的 dashboard 以:
获得帮助
Original review guide in EnglishReviewer's GuideThis PR overhauls the dashboard into a fully modular internationalization system by introducing dynamic loaders, validators and type-safe translation support, then refactors nearly every UI component to consume translation keys instead of hard-coded strings, adds a global LanguageSwitcher, and updates the app bootstrap to asynchronously initialize i18n before mounting. Sequence Diagram for App Initialization with i18nsequenceDiagram
participant App
participant i18nInitializer as "setupI18n()"
participant VueI18nInstance
participant TranslationLoader
participant TranslationCache
participant Filesystem as "Translation Files (JSON)"
App->>i18nInitializer: Start Initialization (async)
activate i18nInitializer
i18nInitializer->>VueI18nInstance: Create/Configure vue-i18n
activate VueI18nInstance
i18nInitializer->>TranslationLoader: Load Initial/Default Translations
activate TranslationLoader
TranslationLoader->>Filesystem: Read JSON files (e.g., core, default lang)
Filesystem-->>TranslationLoader: JSON data
TranslationLoader->>TranslationCache: Store translations
activate TranslationCache
TranslationCache-->>TranslationLoader: Stored
deactivate TranslationCache
TranslationLoader-->>i18nInitializer: Translations Ready
deactivate TranslationLoader
VueI18nInstance-->>i18nInitializer: Instance Ready
deactivate VueI18nInstance
i18nInitializer-->>App: Initialization Complete
deactivate i18nInitializer
App->>App: Mount Vue Components
Class Diagram: i18n ComposablesclassDiagram
namespace i18nComposables {
class useI18n {
+t(key: String, ...args): String
+locale: Ref<String>
+setLocale(newLocale: String): void
+availableLocales: Ref<String[]>
}
class useModuleI18n {
+tm(key: String, ...args): String
+locale: Ref<String>
}
}
useI18n ..> VueI18nInstance : interacts with
useModuleI18n ..> VueI18nInstance : interacts with
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
还未处理的组件:WaitingForRestart.vue - 重启时显示 ReadmeDialog.vue - 插件文档查看 ListConfigItem.vue - 列表配置 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
嘿 @IGCrystal - 我已经审查了你的更改 - 这里有一些反馈:
阻塞问题:
- 检测到一个通用 API 密钥,可能会暴露对各种服务和敏感操作的访问。(link)
一般评论:
- 最好将加载器从原始 fetch 切换到动态
import()
,以便 Vite/Rollup 可以正确地进行代码拆分,并将 locale JSON 包含在构建中,而不是依赖于绝对/src/...
fetch 路径,这些路径在生产环境中可能会中断。 - 在每个
<script setup>
中,导入和初始化useI18n
/useModuleI18n
存在大量重复的样板代码——考虑创建一个共享的 mixin 或 composable 来减少视图中的重复。 - 仔细检查你的加载器注册表中的模块命名(例如
features/tooluse
vsfeatures/tool-use
)是否与文件路径完全匹配,否则某些模块可能永远不会正确加载或缓存。
AI 代理的提示
请解决此代码审查中的评论:
## 总体评论
- 最好将加载器从原始 fetch 切换到动态 `import()`,以便 Vite/Rollup 可以正确地进行代码拆分,并将 locale JSON 包含在构建中,而不是依赖于绝对 `/src/...` fetch 路径,这些路径在生产环境中可能会中断。
- 在每个 `<script setup>` 中,导入和初始化 `useI18n`/`useModuleI18n` 存在大量重复的样板代码——考虑创建一个共享的 mixin 或 composable 来减少视图中的重复。
- 仔细检查你的加载器注册表中的模块命名(例如 `features/tooluse` vs `features/tool-use`)是否与文件路径完全匹配,否则某些模块可能永远不会正确加载或缓存。
## 单独评论
### 评论 1
<location> `dashboard/src/views/ConversationPage.vue:495` </location>
<code_context>
this.fetchConversations();
},
- _methods: {
+ methods: {
// Monaco编辑器挂载后的回调
</code_context>
<issue_to_address>
冗余的 _methods 属性和 getter/setter 应该被移除。
直接在 'methods' 属性下定义方法是标准的 Vue 方法。移除冗余的 _methods 属性将提高代码的清晰度和可维护性。
建议的实现方式:
```
methods: {
// Monaco编辑器挂载后的回调
```
如果在文件的其他地方有对 `this._methods` 的引用,应该更新为直接使用 `this` 或 `this.methods`(尽管在 Vue 中,方法是作为 `this.methodName` 访问的)。此外,如果存在任何针对 `_methods` 的 getter/setter 定义,则应将其删除。
</issue_to_address>
### 评论 2
<location> `dashboard/src/views/ConversationPage.vue:351` </location>
<code_context>
- { title: '更新时间', key: 'updated_at', sortable: true, width: '180px' },
- { title: '操作', key: 'actions', sortable: false, align: 'center', width: '240px' }
- ],
+ headers: [],
// 筛选条件
</code_context>
<issue_to_address>
headers 属性现在未使用,可以删除。
data() 中的 headers 数组现在可以删除,因为使用了 tableHeaders。
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
search: '',
headers: [],
// 筛选条件
platformFilter: [],
=======
search: '',
// 筛选条件
platformFilter: [],
>>>>>>> REPLACE
</suggested_fix>
### 评论 3
<location> `dashboard/src/i18n/loader.ts:89` </location>
<code_context>
+
+ try {
+ // 使用fetch方式加载JSON文件
+ const modulePath = `/src/i18n/locales/${locale}/${moduleInfo.path}`;
+ const response = await fetch(modulePath);
+
</code_context>
<issue_to_address>
硬编码路径在生产环境或不同的构建设置中可能会中断。
考虑用动态导入或 Vite 的资源处理替换硬编码路径,以确保它在不同的环境和构建输出中都能工作。
</issue_to_address>
### 评论 4
<location> `dashboard/src/i18n/loader.ts:212` </location>
<code_context>
+ const moduleName = moduleNames[index];
+ const nameParts = moduleName.split('/');
+
+ // 构建嵌套对象结构(对所有模块统一处理)
+ let current = result;
+ for (let i = 0; i < nameParts.length - 1; i++) {
</code_context>
<issue_to_address>
如果模块共享相同的名称部分,合并模块可能会覆盖键。
如果两个模块共享相同的路径,除了最后一部分,它们的键在合并期间可能会被覆盖。建议添加对此类冲突的检查或警告,以防止细微的错误。
</issue_to_address>
### 评论 5
<location> `dashboard/src/i18n/composables.ts:65` </location>
<code_context>
+ }
+ }
+
+ if (typeof value !== 'string') {
+ console.warn(`Translation value is not string: ${key}`);
+ return key;
</code_context>
<issue_to_address>
如果翻译值不是字符串,翻译函数会返回键,这可能会隐藏缺失的翻译。
考虑使用更明显的指示器,例如将键包裹在括号中,以便在开发过程中更容易检测到缺失的翻译。
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
console.warn(`Translation value is not string: ${key}`);
return key;
=======
console.warn(`Translation value is not string: ${key}`);
return `[${key}]`;
>>>>>>> REPLACE
</suggested_fix>
### 评论 6
<location> `dashboard/src/i18n/validator.ts:307` </location>
<code_context>
+ */
+ validateKeyNaming(localeData: Record<string, any>): ValidationError[] {
+ const errors: ValidationError[] = [];
+ const keyNamingPattern = /^[a-z][a-zA-Z0-9]*$/;
+
+ for (const [locale, data] of Object.entries(localeData)) {
</code_context>
<issue_to_address>
键命名模式对于嵌套键可能过于严格。
一些翻译键可能需要下划线或不同的约定,例如对于首字母缩略词。请检查此模式是否适合所有情况。
</issue_to_address>
### 评论 7
<location> `dashboard/src/i18n/validator.ts:388` </location>
<code_context>
+ }
+
+ // 生成汇总报告
+ const totalKeys = results.length * 100; // 估算的总键数
+ const missingKeys = results.reduce((sum, r) => sum + r.missingKeys.length, 0);
+
</code_context>
<issue_to_address>
总键数估计是硬编码的,可能无法反映实际的键数。
使用硬编码值作为总键数可能导致不准确的完整性指标。请动态计算键的总数。
</issue_to_address>
### 评论 8
<location> `dashboard/src/i18n/validator.ts:8` </location>
<code_context>
+
+import type { ValidationResult, ValidationError, UsageReport, TranslationStats } from './types';
+
+export class I18nValidator {
+ private baseLocale: string = 'zh-CN';
+ private supportedLocales: string[] = ['zh-CN', 'en-US'];
</code_context>
<issue_to_address>
考虑将 I18nValidator 类拆分为单独的模块,每个模块负责一个验证关注点,并共享一个通用的 traverse 辅助函数,以简化代码结构。
这里有一种方法可以在不改变任何行为的情况下驯服这个类——将每个“章节”(完整性、值检查、插值、命名、统计、使用情况、报告)拉到它自己的模块中,并共享一个 `traverse` 辅助函数。顶级的 `I18nValidator` 然后只进行协调。
---
### 1) 提取一个通用的 `traverse` 辅助函数
```ts
// utils/traverse.ts
export function traverse(
obj: any,
fn: (path: string, value: any) => void,
prefix: string = ''
): void {
for (const [k, v] of Object.entries(obj)) {
const full = prefix ? `${prefix}.${k}` : k;
fn(full, v);
if (v && typeof v === 'object') {
traverse(v, fn, full);
}
}
}
```
---
### 2) 将每个关注点拆分为它自己的文件
```ts
// validators/completeness.ts
import { traverse } from '../utils/traverse';
import type { ValidationResult, ValidationError } from '../types';
export function validateCompleteness(
baseLocale: string,
supported: string[],
localeData: Record<string, any>
): ValidationResult {
const errors: ValidationError[] = [];
/* … your existing completeness logic,
replacing getAllKeys with traverse where appropriate … */
return { /* same shape */ };
}
```
```ts
// validators/value.ts
import { traverse } from '../utils/traverse';
import type { ValidationError } from '../types';
export function validateValues(localeData: Record<string, any>): ValidationError[] {
const errors: ValidationError[] = [];
traverse(localeData, (path, v) => {
if (typeof v === 'string') {
if (!v.trim()) { /* push empty_value */ }
const bad = (v.match(/\{[^}]+\}/g) || []).filter(ph => !/^{[A-Za-z_]\w*}$/.test(ph));
bad.forEach(ph =>
errors.push({ type: 'type_mismatch', key: path, message: `…` })
);
} else if (v != null && typeof v !== 'object') {
errors.push({ type: 'type_mismatch', key: path, message: `…` });
}
});
return errors;
}
```
…以及类似的 `interpolation.ts`、`naming.ts`、`usage.ts`、`stats.ts`。
---
### 3) 在你的编排器中连接它们
```ts
// I18nValidator.ts
import { validateCompleteness } from './validators/completeness';
import { validateValues } from './validators/value';
import { validateInterpolation }from './validators/interpolation';
import { validateKeyNaming } from './validators/naming';
import { validateUsage } from './validators/usage';
import { generateStats } from './validators/stats';
import type {
ValidationResult, ValidationError, UsageReport, TranslationStats
} from './types';
export class I18nValidator {
constructor(
private baseLocale = 'zh-CN',
private supportedLocales = ['zh-CN','en-US']
) {}
generateReport(
localeData: Record<string, any>,
usedKeys: string[] = []
): {
completeness: ValidationResult;
values: ValidationError[];
interpolation: ValidationError[];
naming: ValidationError[];
usage: UsageReport | null;
stats: TranslationStats;
} {
const completeness = validateCompleteness(
this.baseLocale, this.supportedLocales, localeData
);
const values = validateValues(localeData);
const interpolation = validateInterpolation(
this.baseLocale, this.supportedLocales, localeData
);
const naming = validateKeyNaming(localeData);
const stats = generateStats(localeData);
const usage =
usedKeys.length > 0
? validateUsage(this.baseLocale, localeData, usedKeys)
: null;
return { completeness, values, interpolation, naming, usage, stats };
}
}
```
**优点:**
- 每个文件大约 50 行,而不是 >1000 行。
- 你可以单独对每个模块进行单元测试。
- “大局”编排器变得微不足道。
</issue_to_address>
### 评论 9
<location> `dashboard/src/i18n/loader.ts:115` </location>
<code_context>
+ /**
+ * 加载核心模块(最高优先级)
+ */
+ async loadCoreModules(locale: string): Promise<any> {
+ const coreModules = [
+ 'core/common',
</code_context>
<issue_to_address>
考虑将三个模块加载方法重构为一个通用的加载器,该加载器使用注册表和可选的覆盖来减少重复。
```markdown
你可以将所有三个“load*Modules”方法折叠为一个通用的加载器,该加载器从注册表(或覆盖列表)中提取名称,从而消除手动数组和重复的循环。例如:
```ts
// 新增:通用模块加载器
private async loadModules(
locale: string,
prefix: string,
overrideList: string[] = []
): Promise<any> {
// 取 overrideList 或者 registry 中所有符合前缀的模块名
const moduleNames = overrideList.length
? overrideList
: Array.from(this.moduleRegistry.keys()).filter((k) => k.startsWith(prefix));
const datas = await Promise.all(
moduleNames.map((name) => this.loadModule(locale, name))
);
return this.mergeModules(datas, moduleNames);
}
```
然后把原来的三个方法改为:
```ts
async loadCoreModules(locale: string): Promise<any> {
return this.loadModules(locale, 'core');
}
async loadFeatureModules(
locale: string,
features?: string[]
): Promise<any> {
return this.loadModules(locale, 'features', features || []);
}
async loadMessageModules(locale: string): Promise<any> {
return this.loadModules(locale, 'messages');
}
```
这样你就不需要在三个地方手动维护数组,也去掉了重复的 `Promise.all … mergeModules` 逻辑保留所有功能且更易维护。
</issue_to_address>
### 评论 10
<location> `dashboard/src/i18n/types.ts:7` </location>
<code_context>
+ */
+
+// 核心模块类型定义
+export interface CoreTranslations {
+ common: {
+ save: string;
</code_context>
<issue_to_address>
考虑使用 TypeScript 助手直接从你的 JSON 文件中推断翻译类型和键联合,而不是手动定义大型接口块。
```markdown
你可以通过直接从你的 JSON 文件中推断模式和有效的键字符串来消除 300 多行的手动接口和联合。这保持了完整的类型安全,并在你添加新键时自动更新。
1) 将你的 locale JSON 导入为 `const`
2) 推断一个单一的 `TranslationSchema`
3) 使用一个小的 `NestedKey<T>` 助手来派生每个有效的 `"a" | "a.b" | "a.b.c"` 键
```ts
// src/i18n/types.ts
import enUS from '../locales/en-US.json';
import zhCN from '../locales/zh-CN.json';
export const translations = {
'en-US': enUS,
'zh-CN': zhCN,
} as const;
// full schema for a single locale
export type TranslationSchema = typeof translations[keyof typeof translations];
// helper to extract nested key paths ("foo", "foo.bar", "foo.bar.baz", …)
type NestedKey<T> = T extends object
? { [K in keyof T & string]:
T[K] extends object
? `${K}` | `${K}.${NestedKey<T[K]>}`
: `${K}`
}[keyof T & string]
: never;
// the union of all valid translation keys
export type TranslationKey = NestedKey<TranslationSchema>;
// simplified t() signature
export function t(
key: TranslationKey,
params?: Record<string, string | number>
): string {
// …implement lookup + interpolation
}
```
优点:
- **零手动重复**——添加到你的 JSON 中的任何键都会立即反映在 `TranslationKey` 中。
- **单一事实来源**——没有单独的 `interface` 块需要维护。
- **完全类型安全**——无效的键或拼写错误会在编译时被捕获。
```
</issue_to_address>
## 安全问题
### 问题 1
<location> `dashboard/src/views/ConfigPage.vue:105` </location>
<issue_to_address>
**security (generic-api-key):** 检测到通用 API 密钥,可能会暴露对各种服务和敏感操作的访问。
*Source: gitleaks*
</issue_to_address>
帮助我变得更有用!请在每条评论上点击 👍 或 👎,我将使用这些反馈来改进你的评论。
Original comment in English
Hey @IGCrystal - I've reviewed your changes - here's some feedback:
Blocking issues:
- Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)
General comments:
- It’s better to switch the loader from raw fetch to dynamic
import()
so that Vite/Rollup can properly code‐split and include locale JSON in the build rather than relying on absolute/src/...
fetch paths that may break in production. - There’s a lot of repeated boilerplate in each
<script setup>
for importing and initializinguseI18n
/useModuleI18n
—consider creating a shared mixin or composable to reduce duplication across views. - Double-check that the module naming in your loader registry (e.g.
features/tooluse
vsfeatures/tool-use
) exactly matches the file paths, otherwise some modules may never load or cache correctly.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- It’s better to switch the loader from raw fetch to dynamic `import()` so that Vite/Rollup can properly code‐split and include locale JSON in the build rather than relying on absolute `/src/...` fetch paths that may break in production.
- There’s a lot of repeated boilerplate in each `<script setup>` for importing and initializing `useI18n`/`useModuleI18n`—consider creating a shared mixin or composable to reduce duplication across views.
- Double-check that the module naming in your loader registry (e.g. `features/tooluse` vs `features/tool-use`) exactly matches the file paths, otherwise some modules may never load or cache correctly.
## Individual Comments
### Comment 1
<location> `dashboard/src/views/ConversationPage.vue:495` </location>
<code_context>
this.fetchConversations();
},
- _methods: {
+ methods: {
// Monaco编辑器挂载后的回调
</code_context>
<issue_to_address>
Redundant _methods property and getter/setter should be removed.
Defining methods directly under the 'methods' property is the standard Vue approach. Removing the redundant _methods property will improve clarity and maintainability.
Suggested implementation:
```
methods: {
// Monaco编辑器挂载后的回调
```
If there are any references to `this._methods` elsewhere in the file, they should be updated to use `this` directly or `this.methods` if needed (though in Vue, methods are accessed as `this.methodName`). Also, if there are any getter/setter definitions for `_methods`, they should be removed.
</issue_to_address>
### Comment 2
<location> `dashboard/src/views/ConversationPage.vue:351` </location>
<code_context>
- { title: '更新时间', key: 'updated_at', sortable: true, width: '180px' },
- { title: '操作', key: 'actions', sortable: false, align: 'center', width: '240px' }
- ],
+ headers: [],
// 筛选条件
</code_context>
<issue_to_address>
The headers property is now unused and can be removed.
The headers array in data() can be removed now that tableHeaders is used.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
search: '',
headers: [],
// 筛选条件
platformFilter: [],
=======
search: '',
// 筛选条件
platformFilter: [],
>>>>>>> REPLACE
</suggested_fix>
### Comment 3
<location> `dashboard/src/i18n/loader.ts:89` </location>
<code_context>
+
+ try {
+ // 使用fetch方式加载JSON文件
+ const modulePath = `/src/i18n/locales/${locale}/${moduleInfo.path}`;
+ const response = await fetch(modulePath);
+
</code_context>
<issue_to_address>
Hardcoded path may break in production or with different build setups.
Consider replacing the hardcoded path with a dynamic import or Vite's asset handling to ensure it works across different environments and build outputs.
</issue_to_address>
### Comment 4
<location> `dashboard/src/i18n/loader.ts:212` </location>
<code_context>
+ const moduleName = moduleNames[index];
+ const nameParts = moduleName.split('/');
+
+ // 构建嵌套对象结构(对所有模块统一处理)
+ let current = result;
+ for (let i = 0; i < nameParts.length - 1; i++) {
</code_context>
<issue_to_address>
Merging modules may overwrite keys if modules share the same name parts.
If two modules share the same path except for the last part, their keys may be overwritten during merging. Adding a check or warning for such collisions is recommended to prevent subtle bugs.
</issue_to_address>
### Comment 5
<location> `dashboard/src/i18n/composables.ts:65` </location>
<code_context>
+ }
+ }
+
+ if (typeof value !== 'string') {
+ console.warn(`Translation value is not string: ${key}`);
+ return key;
</code_context>
<issue_to_address>
Translation function returns key if value is not a string, which may hide missing translations.
Consider using a more visible indicator, like wrapping the key in brackets, to make missing translations easier to detect during development.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
console.warn(`Translation value is not string: ${key}`);
return key;
=======
console.warn(`Translation value is not string: ${key}`);
return `[${key}]`;
>>>>>>> REPLACE
</suggested_fix>
### Comment 6
<location> `dashboard/src/i18n/validator.ts:307` </location>
<code_context>
+ */
+ validateKeyNaming(localeData: Record<string, any>): ValidationError[] {
+ const errors: ValidationError[] = [];
+ const keyNamingPattern = /^[a-z][a-zA-Z0-9]*$/;
+
+ for (const [locale, data] of Object.entries(localeData)) {
</code_context>
<issue_to_address>
Key naming pattern may be too restrictive for nested keys.
Some translation keys may need underscores or different conventions, such as for acronyms. Please review if this pattern fits all scenarios.
</issue_to_address>
### Comment 7
<location> `dashboard/src/i18n/validator.ts:388` </location>
<code_context>
+ }
+
+ // 生成汇总报告
+ const totalKeys = results.length * 100; // 估算的总键数
+ const missingKeys = results.reduce((sum, r) => sum + r.missingKeys.length, 0);
+
</code_context>
<issue_to_address>
Total keys estimation is hardcoded and may not reflect actual key count.
Using a hardcoded value for total keys can result in inaccurate completeness metrics. Please calculate the total number of keys dynamically.
</issue_to_address>
### Comment 8
<location> `dashboard/src/i18n/validator.ts:8` </location>
<code_context>
+
+import type { ValidationResult, ValidationError, UsageReport, TranslationStats } from './types';
+
+export class I18nValidator {
+ private baseLocale: string = 'zh-CN';
+ private supportedLocales: string[] = ['zh-CN', 'en-US'];
</code_context>
<issue_to_address>
Consider splitting the I18nValidator class into separate modules for each validation concern and sharing a generic traverse helper to simplify the code structure.
Here’s one way to tame this class without changing any behavior—pull each “chapter” (completeness, value‐checks, interpolation, naming, stats, usage, report) into its own module and share a single `traverse` helper. The top‐level `I18nValidator` then just coordinates.
---
### 1) Extract a generic `traverse` helper
```ts
// utils/traverse.ts
export function traverse(
obj: any,
fn: (path: string, value: any) => void,
prefix: string = ''
): void {
for (const [k, v] of Object.entries(obj)) {
const full = prefix ? `${prefix}.${k}` : k;
fn(full, v);
if (v && typeof v === 'object') {
traverse(v, fn, full);
}
}
}
```
---
### 2) Split each concern into its own file
```ts
// validators/completeness.ts
import { traverse } from '../utils/traverse';
import type { ValidationResult, ValidationError } from '../types';
export function validateCompleteness(
baseLocale: string,
supported: string[],
localeData: Record<string, any>
): ValidationResult {
const errors: ValidationError[] = [];
/* … your existing completeness logic,
replacing getAllKeys with traverse where appropriate … */
return { /* same shape */ };
}
```
```ts
// validators/value.ts
import { traverse } from '../utils/traverse';
import type { ValidationError } from '../types';
export function validateValues(localeData: Record<string, any>): ValidationError[] {
const errors: ValidationError[] = [];
traverse(localeData, (path, v) => {
if (typeof v === 'string') {
if (!v.trim()) { /* push empty_value */ }
const bad = (v.match(/\{[^}]+\}/g) || []).filter(ph => !/^{[A-Za-z_]\w*}$/.test(ph));
bad.forEach(ph =>
errors.push({ type: 'type_mismatch', key: path, message: `…` })
);
} else if (v != null && typeof v !== 'object') {
errors.push({ type: 'type_mismatch', key: path, message: `…` });
}
});
return errors;
}
```
…and similarly for `interpolation.ts`, `naming.ts`, `usage.ts`, `stats.ts`.
---
### 3) Wire them up in your orchestrator
```ts
// I18nValidator.ts
import { validateCompleteness } from './validators/completeness';
import { validateValues } from './validators/value';
import { validateInterpolation }from './validators/interpolation';
import { validateKeyNaming } from './validators/naming';
import { validateUsage } from './validators/usage';
import { generateStats } from './validators/stats';
import type {
ValidationResult, ValidationError, UsageReport, TranslationStats
} from './types';
export class I18nValidator {
constructor(
private baseLocale = 'zh-CN',
private supportedLocales = ['zh-CN','en-US']
) {}
generateReport(
localeData: Record<string, any>,
usedKeys: string[] = []
): {
completeness: ValidationResult;
values: ValidationError[];
interpolation: ValidationError[];
naming: ValidationError[];
usage: UsageReport | null;
stats: TranslationStats;
} {
const completeness = validateCompleteness(
this.baseLocale, this.supportedLocales, localeData
);
const values = validateValues(localeData);
const interpolation = validateInterpolation(
this.baseLocale, this.supportedLocales, localeData
);
const naming = validateKeyNaming(localeData);
const stats = generateStats(localeData);
const usage =
usedKeys.length > 0
? validateUsage(this.baseLocale, localeData, usedKeys)
: null;
return { completeness, values, interpolation, naming, usage, stats };
}
}
```
**Benefits:**
- Each file is ~50 lines instead of >1 000.
- You can unit‐test each module in isolation.
- The “big picture” orchestrator becomes trivial.
</issue_to_address>
### Comment 9
<location> `dashboard/src/i18n/loader.ts:115` </location>
<code_context>
+ /**
+ * 加载核心模块(最高优先级)
+ */
+ async loadCoreModules(locale: string): Promise<any> {
+ const coreModules = [
+ 'core/common',
</code_context>
<issue_to_address>
Consider refactoring the three module-loading methods into a single generic loader that uses the registry and optional overrides to reduce duplication.
```markdown
You can collapse all three “load*Modules” methods into one generic loader that pulls names from the registry (or an override list), eliminating manual arrays and duplicated loops. For example:
```ts
// 新增:通用模块加载器
private async loadModules(
locale: string,
prefix: string,
overrideList: string[] = []
): Promise<any> {
// 取 overrideList 或者 registry 中所有符合前缀的模块名
const moduleNames = overrideList.length
? overrideList
: Array.from(this.moduleRegistry.keys()).filter((k) => k.startsWith(prefix));
const datas = await Promise.all(
moduleNames.map((name) => this.loadModule(locale, name))
);
return this.mergeModules(datas, moduleNames);
}
```
然后把原来的三个方法改为:
```ts
async loadCoreModules(locale: string): Promise<any> {
return this.loadModules(locale, 'core');
}
async loadFeatureModules(
locale: string,
features?: string[]
): Promise<any> {
return this.loadModules(locale, 'features', features || []);
}
async loadMessageModules(locale: string): Promise<any> {
return this.loadModules(locale, 'messages');
}
```
这样你就不需要在三个地方手动维护数组,也去掉了重复的 `Promise.all … mergeModules` 逻辑保留所有功能且更易维护。
</issue_to_address>
### Comment 10
<location> `dashboard/src/i18n/types.ts:7` </location>
<code_context>
+ */
+
+// 核心模块类型定义
+export interface CoreTranslations {
+ common: {
+ save: string;
</code_context>
<issue_to_address>
Consider inferring translation types and key unions directly from your JSON files using TypeScript helpers instead of manually defining large interface blocks.
```markdown
You can eliminate the 300+ lines of manual interfaces & unions by inferring both the schema and the valid key‐strings directly from your JSON files. This keeps full type‐safety and auto-updates whenever you add new keys.
1) Import your locale JSONs as `const`
2) Infer a single `TranslationSchema`
3) Use a small `NestedKey<T>` helper to derive every valid `"a" | "a.b" | "a.b.c"` key
```ts
// src/i18n/types.ts
import enUS from '../locales/en-US.json';
import zhCN from '../locales/zh-CN.json';
export const translations = {
'en-US': enUS,
'zh-CN': zhCN,
} as const;
// full schema for a single locale
export type TranslationSchema = typeof translations[keyof typeof translations];
// helper to extract nested key paths ("foo", "foo.bar", "foo.bar.baz", …)
type NestedKey<T> = T extends object
? { [K in keyof T & string]:
T[K] extends object
? `${K}` | `${K}.${NestedKey<T[K]>}`
: `${K}`
}[keyof T & string]
: never;
// the union of all valid translation keys
export type TranslationKey = NestedKey<TranslationSchema>;
// simplified t() signature
export function t(
key: TranslationKey,
params?: Record<string, string | number>
): string {
// …implement lookup + interpolation
}
```
Advantages:
- **Zero manual repetition**—any key added to your JSONs is immediately reflected in `TranslationKey`.
- **Single source of truth**—no separate `interface` block to maintain.
- **Full type-safety**—invalid keys or typos are caught at compile time.
```
</issue_to_address>
## Security Issues
### Issue 1
<location> `dashboard/src/views/ConfigPage.vue:105` </location>
<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
*Source: gitleaks*
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
this.messageType = 'error'; | ||
this.showMessage = true; | ||
} | ||
}, | ||
get methods() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: 冗余的 _methods 属性和 getter/setter 应该被移除。
直接在 'methods' 属性下定义方法是标准的 Vue 方法。移除冗余的 _methods 属性将提高代码的清晰度和可维护性。
建议的实现方式:
methods: {
// Monaco编辑器挂载后的回调
如果在文件的其他地方有对 this._methods
的引用,应该更新为直接使用 this
或 this.methods
(尽管在 Vue 中,方法是作为 this.methodName
访问的)。此外,如果存在任何针对 _methods
的 getter/setter 定义,则应将其删除。
Original comment in English
suggestion: Redundant _methods property and getter/setter should be removed.
Defining methods directly under the 'methods' property is the standard Vue approach. Removing the redundant _methods property will improve clarity and maintainability.
Suggested implementation:
methods: {
// Monaco编辑器挂载后的回调
If there are any references to this._methods
elsewhere in the file, they should be updated to use this
directly or this.methods
if needed (though in Vue, methods are accessed as this.methodName
). Also, if there are any getter/setter definitions for _methods
, they should be removed.
search: '', | ||
headers: [ | ||
{ title: '对话标题', key: 'title', sortable: true }, | ||
{ title: '平台', key: 'platform', sortable: true, width: '120px' }, | ||
{ title: '类型', key: 'messageType', sortable: true, width: '100px' }, | ||
{ title: 'ID', key: 'sessionId', sortable: true, width: '100px' }, | ||
{ title: '创建时间', key: 'created_at', sortable: true, width: '180px' }, | ||
{ title: '更新时间', key: 'updated_at', sortable: true, width: '180px' }, | ||
{ title: '操作', key: 'actions', sortable: false, align: 'center', width: '240px' } | ||
], | ||
headers: [], | ||
|
||
// 筛选条件 | ||
platformFilter: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: headers 属性现在未使用,可以删除。
data() 中的 headers 数组现在可以删除,因为使用了 tableHeaders。
search: '', | |
headers: [ | |
{ title: '对话标题', key: 'title', sortable: true }, | |
{ title: '平台', key: 'platform', sortable: true, width: '120px' }, | |
{ title: '类型', key: 'messageType', sortable: true, width: '100px' }, | |
{ title: 'ID', key: 'sessionId', sortable: true, width: '100px' }, | |
{ title: '创建时间', key: 'created_at', sortable: true, width: '180px' }, | |
{ title: '更新时间', key: 'updated_at', sortable: true, width: '180px' }, | |
{ title: '操作', key: 'actions', sortable: false, align: 'center', width: '240px' } | |
], | |
headers: [], | |
// 筛选条件 | |
platformFilter: [], | |
search: '', | |
// 筛选条件 | |
platformFilter: [], |
Original comment in English
suggestion: The headers property is now unused and can be removed.
The headers array in data() can be removed now that tableHeaders is used.
search: '', | |
headers: [ | |
{ title: '对话标题', key: 'title', sortable: true }, | |
{ title: '平台', key: 'platform', sortable: true, width: '120px' }, | |
{ title: '类型', key: 'messageType', sortable: true, width: '100px' }, | |
{ title: 'ID', key: 'sessionId', sortable: true, width: '100px' }, | |
{ title: '创建时间', key: 'created_at', sortable: true, width: '180px' }, | |
{ title: '更新时间', key: 'updated_at', sortable: true, width: '180px' }, | |
{ title: '操作', key: 'actions', sortable: false, align: 'center', width: '240px' } | |
], | |
headers: [], | |
// 筛选条件 | |
platformFilter: [], | |
search: '', | |
// 筛选条件 | |
platformFilter: [], |
dashboard/src/i18n/loader.ts
Outdated
|
||
try { | ||
// 使用fetch方式加载JSON文件 | ||
const modulePath = `/src/i18n/locales/${locale}/${moduleInfo.path}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): 硬编码路径在生产环境或不同的构建设置中可能会中断。
考虑用动态导入或 Vite 的资源处理替换硬编码路径,以确保它在不同的环境和构建输出中都能工作。
Original comment in English
issue (bug_risk): Hardcoded path may break in production or with different build setups.
Consider replacing the hardcoded path with a dynamic import or Vite's asset handling to ensure it works across different environments and build outputs.
const moduleName = moduleNames[index]; | ||
const nameParts = moduleName.split('/'); | ||
|
||
// 构建嵌套对象结构(对所有模块统一处理) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): 如果模块共享相同的名称部分,合并模块可能会覆盖键。
如果两个模块共享相同的路径,除了最后一部分,它们的键在合并期间可能会被覆盖。建议添加对此类冲突的检查或警告,以防止细微的错误。
Original comment in English
issue (bug_risk): Merging modules may overwrite keys if modules share the same name parts.
If two modules share the same path except for the last part, their keys may be overwritten during merging. Adding a check or warning for such collisions is recommended to prevent subtle bugs.
dashboard/src/i18n/composables.ts
Outdated
console.warn(`Translation value is not string: ${key}`); | ||
return key; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: 如果翻译值不是字符串,翻译函数会返回键,这可能会隐藏缺失的翻译。
考虑使用更明显的指示器,例如将键包裹在括号中,以便在开发过程中更容易检测到缺失的翻译。
console.warn(`Translation value is not string: ${key}`); | |
return key; | |
console.warn(`Translation value is not string: ${key}`); | |
return `[${key}]`; |
Original comment in English
suggestion: Translation function returns key if value is not a string, which may hide missing translations.
Consider using a more visible indicator, like wrapping the key in brackets, to make missing translations easier to detect during development.
console.warn(`Translation value is not string: ${key}`); | |
return key; | |
console.warn(`Translation value is not string: ${key}`); | |
return `[${key}]`; |
dashboard/src/i18n/loader.ts
Outdated
/** | ||
* 加载核心模块(最高优先级) | ||
*/ | ||
async loadCoreModules(locale: string): Promise<any> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): 考虑将三个模块加载方法重构为一个通用的加载器,该加载器使用注册表和可选的覆盖来减少重复。
你可以将所有三个“load*Modules”方法折叠为一个通用的加载器,该加载器从注册表(或覆盖列表)中提取名称,从而消除手动数组和重复的循环。例如:
```ts
// 新增:通用模块加载器
private async loadModules(
locale: string,
prefix: string,
overrideList: string[] = []
): Promise<any> {
// 取 overrideList 或者 registry 中所有符合前缀的模块名
const moduleNames = overrideList.length
? overrideList
: Array.from(this.moduleRegistry.keys()).filter((k) => k.startsWith(prefix));
const datas = await Promise.all(
moduleNames.map((name) => this.loadModule(locale, name))
);
return this.mergeModules(datas, moduleNames);
}
然后把原来的三个方法改为:
async loadCoreModules(locale: string): Promise<any> {
return this.loadModules(locale, 'core');
}
async loadFeatureModules(
locale: string,
features?: string[]
): Promise<any> {
return this.loadModules(locale, 'features', features || []);
}
async loadMessageModules(locale: string): Promise<any> {
return this.loadModules(locale, 'messages');
}
这样你就不需要在三个地方手动维护数组,也去掉了重复的 Promise.all … mergeModules
逻辑保留所有功能且更易维护。
Original comment in English
issue (complexity): Consider refactoring the three module-loading methods into a single generic loader that uses the registry and optional overrides to reduce duplication.
You can collapse all three “load*Modules” methods into one generic loader that pulls names from the registry (or an override list), eliminating manual arrays and duplicated loops. For example:
```ts
// 新增:通用模块加载器
private async loadModules(
locale: string,
prefix: string,
overrideList: string[] = []
): Promise<any> {
// 取 overrideList 或者 registry 中所有符合前缀的模块名
const moduleNames = overrideList.length
? overrideList
: Array.from(this.moduleRegistry.keys()).filter((k) => k.startsWith(prefix));
const datas = await Promise.all(
moduleNames.map((name) => this.loadModule(locale, name))
);
return this.mergeModules(datas, moduleNames);
}
然后把原来的三个方法改为:
async loadCoreModules(locale: string): Promise<any> {
return this.loadModules(locale, 'core');
}
async loadFeatureModules(
locale: string,
features?: string[]
): Promise<any> {
return this.loadModules(locale, 'features', features || []);
}
async loadMessageModules(locale: string): Promise<any> {
return this.loadModules(locale, 'messages');
}
这样你就不需要在三个地方手动维护数组,也去掉了重复的 Promise.all … mergeModules
逻辑保留所有功能且更易维护。
dashboard/src/i18n/types.ts
Outdated
*/ | ||
|
||
// 核心模块类型定义 | ||
export interface CoreTranslations { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): 考虑使用 TypeScript 助手直接从你的 JSON 文件中推断翻译类型和键联合,而不是手动定义大型接口块。
你可以通过直接从你的 JSON 文件中推断模式和有效的键字符串来消除 300 多行的手动接口和联合。这保持了完整的类型安全,并在你添加新键时自动更新。
1) 将你的 locale JSON 导入为 `const`
2) 推断一个单一的 `TranslationSchema`
3) 使用一个小的 `NestedKey<T>` 助手来派生每个有效的 `"a" | "a.b" | "a.b.c"` 键
```ts
// src/i18n/types.ts
import enUS from '../locales/en-US.json';
import zhCN from '../locales/zh-CN.json';
export const translations = {
'en-US': enUS,
'zh-CN': zhCN,
} as const;
// full schema for a single locale
export type TranslationSchema = typeof translations[keyof typeof translations];
// helper to extract nested key paths ("foo", "foo.bar", "foo.bar.baz", …)
type NestedKey<T> = T extends object
? { [K in keyof T & string]:
T[K] extends object
? `${K}` | `${K}.${NestedKey<T[K]>}`
: `${K}`
}[keyof T & string]
: never;
// the union of all valid translation keys
export type TranslationKey = NestedKey<TranslationSchema>;
// simplified t() signature
export function t(
key: TranslationKey,
params?: Record<string, string | number>
): string {
// …implement lookup + interpolation
}
优点:
- 零手动重复——添加到你的 JSON 中的任何键都会立即反映在
TranslationKey
中。 - 单一事实来源——没有单独的
interface
块需要维护。 - 完全类型安全——无效的键或拼写错误会在编译时被捕获。
<details>
<summary>Original comment in English</summary>
**issue (complexity):** Consider inferring translation types and key unions directly from your JSON files using TypeScript helpers instead of manually defining large interface blocks.
```markdown
You can eliminate the 300+ lines of manual interfaces & unions by inferring both the schema and the valid key‐strings directly from your JSON files. This keeps full type‐safety and auto-updates whenever you add new keys.
1) Import your locale JSONs as `const`
2) Infer a single `TranslationSchema`
3) Use a small `NestedKey<T>` helper to derive every valid `"a" | "a.b" | "a.b.c"` key
```ts
// src/i18n/types.ts
import enUS from '../locales/en-US.json';
import zhCN from '../locales/zh-CN.json';
export const translations = {
'en-US': enUS,
'zh-CN': zhCN,
} as const;
// full schema for a single locale
export type TranslationSchema = typeof translations[keyof typeof translations];
// helper to extract nested key paths ("foo", "foo.bar", "foo.bar.baz", …)
type NestedKey<T> = T extends object
? { [K in keyof T & string]:
T[K] extends object
? `${K}` | `${K}.${NestedKey<T[K]>}`
: `${K}`
}[keyof T & string]
: never;
// the union of all valid translation keys
export type TranslationKey = NestedKey<TranslationSchema>;
// simplified t() signature
export function t(
key: TranslationKey,
params?: Record<string, string | number>
): string {
// …implement lookup + interpolation
}
Advantages:
- Zero manual repetition—any key added to your JSONs is immediately reflected in
TranslationKey
. - Single source of truth—no separate
interface
block to maintain. - Full type-safety—invalid keys or typos are caught at compile time.
</details>
<small>{{ tm('help.helpPrefix') }} | ||
<a href="https://astrbot.app/" target="_blank">{{ tm('help.documentation') }}</a> | ||
{{ tm('help.helpMiddle') }} | ||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=EYGsuUTfe00_iOu9JTXS7_TEpMkXOvwv&jump_from=webapi&authKey=uUEMKCROfsseS+8IzqPjzV3y1tzy4AkykwTib2jNkOFdzezF9s9XknqnIaf3CDft" target="_blank">{{ tm('help.support') }}</a>{{ tm('help.helpSuffix') }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
security (generic-api-key): 检测到通用 API 密钥,可能会暴露对各种服务和敏感操作的访问。
Source: gitleaks
Original comment in English
security (generic-api-key): Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
Source: gitleaks
function traverse(source: any, target: any, path: string = '') { | ||
for (const key in source) { | ||
const currentPath = path ? `${path}.${key}` : key; | ||
|
||
if (typeof source[key] === 'object' && source[key] !== null) { | ||
if (!target[key]) { | ||
missing.push(currentPath); | ||
} else { | ||
traverse(source[key], target[key], currentPath); | ||
} | ||
} else { | ||
if (!(key in target)) { | ||
missing.push(currentPath); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (code-quality): 避免在块中使用函数声明,而倾向于使用函数赋值表达式。(avoid-function-declarations-in-blocks
)
Explanation
函数声明可能会在 Javascript 中被提升,但这种行为在不同浏览器之间是不一致的。 提升通常会令人困惑,应该避免。与其在块中使用函数声明,不如使用函数表达式,它会在作用域内创建函数。Original comment in English
issue (code-quality): Avoid function declarations, favouring function assignment expressions, inside blocks. (avoid-function-declarations-in-blocks
)
Explanation
Function declarations may be hoisted in Javascript, but the behaviour is inconsistent between browsers. Hoisting is generally confusing and should be avoided. Rather than using function declarations inside blocks, you should use function expressions, which create functions in-scope.if (typeof source[key] === 'object' && source[key] !== null) { | ||
if (!target[key]) { | ||
missing.push(currentPath); | ||
} else { | ||
traverse(source[key], target[key], currentPath); | ||
} | ||
} else { | ||
if (!(key in target)) { | ||
missing.push(currentPath); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): 将 else 子句的嵌套 if 语句合并到 else if
中 (merge-else-if
)
if (typeof source[key] === 'object' && source[key] !== null) { | |
if (!target[key]) { | |
missing.push(currentPath); | |
} else { | |
traverse(source[key], target[key], currentPath); | |
} | |
} else { | |
if (!(key in target)) { | |
missing.push(currentPath); | |
} | |
} | |
if (typeof source[key] === 'object' && source[key] !== null) { | |
if (!target[key]) { | |
missing.push(currentPath); | |
} else { | |
traverse(source[key], target[key], currentPath); | |
} | |
} | |
else if (!(key in target)) { | |
missing.push(currentPath); | |
} | |
Explanation
展平嵌套在 else 子句中的 if 语句会生成更易于阅读和扩展的代码。Original comment in English
suggestion (code-quality): Merge else clause's nested if statement into else if
(merge-else-if
)
if (typeof source[key] === 'object' && source[key] !== null) { | |
if (!target[key]) { | |
missing.push(currentPath); | |
} else { | |
traverse(source[key], target[key], currentPath); | |
} | |
} else { | |
if (!(key in target)) { | |
missing.push(currentPath); | |
} | |
} | |
if (typeof source[key] === 'object' && source[key] !== null) { | |
if (!target[key]) { | |
missing.push(currentPath); | |
} else { | |
traverse(source[key], target[key], currentPath); | |
} | |
} | |
else if (!(key in target)) { | |
missing.push(currentPath); | |
} | |
Explanation
Flattening if statements nested within else clauses generates code that iseasier to read and expand upon.
- Enhance i18n error handling and code quality - Fix SSE data processing in chat page - Improve responsive design for extension page - Add better debugging tools for development"
ExtensionCard.vue - 插件卡片组件 WaitingForRestart.vue - 重启等待组件 ReadmeDialog.vue - README对话框组件 AstrBotConfig.vue - 配置编辑器组件 ListConfigItem.vue - 列表配置项组件 ItemCardGrid.vue - 卡片网格组件 ChatPage.vue - 聊天页面的录音提示文本 ConfigPage.vue - 配置页面的状态消息 ExtensionPage.vue - 插件页面的加载和状态文本 OnlineTime.vue - 仪表板运行时间组件
- AlkaidPage_sigma.vue - PlatformPage.vue - LongTermMemory.vue - KnowledgeBase.vue
- Migrate from manual TypeScript interfaces to automatic type generation from JSON files. Eliminates sync issues and maintenance overhead.
国际化已经基本修改完毕,我这里测试没有问题,请求其他成员进行测试。 |
处理的是分隔线的样式
修复chat与chatbox之间切换后sse断开连接导致无法实时显示消息
修改键位为Ctrl + A ,以及还加入SSE断连提示
解决了 #XYZ
Motivation
Modifications
✨ 核心特性:
🌐 国际化覆盖:
🎨 UI/UX 优化:
📱 移动端适配:
🔧 技术改进:
Check
requirements.txt
和pyproject.toml
文件相应位置。好的,这是翻译成中文的 pull request 总结:
Sourcery 总结
重构 WebUI 为完全模块化的国际化架构,将所有硬编码文本替换为翻译键,提供双语翻译文件,支持延迟加载和类型安全验证,并添加全局语言切换器,同时更新应用程序引导程序以进行异步 i18n 初始化。
新特性:
增强功能:
useI18n
/useModuleI18n
composables,并将硬编码字符串替换为翻译键LanguageSwitcher
组件,用于在标题和登录页面中进行运行时区域设置切换构建:
vue-i18n
依赖项并集成动态翻译加载器和缓存逻辑杂项:
Original summary in English
好的,这是将拉取请求摘要翻译成中文的结果:
Sourcery 总结
通过将硬编码的 UI 文本替换为翻译键、动态加载双语翻译文件、强制执行类型安全的键验证以及添加全局语言切换器,将仪表板重构为完整的国际化系统。
新功能:
增强功能:
useI18n
/useModuleI18n
composables 而不是硬编码的字符串LanguageSwitcher
组件并集成全局语言切换构建:
vue-i18n
依赖项并集成动态翻译加载器杂项:
src/i18n
下引入 TypeScript 翻译模式和加载器/验证器实用程序Original summary in English
Summary by Sourcery
Refactor the dashboard to a full internationalization system by replacing hardcoded UI text with translation keys, loading bilingual translation files dynamically, enforcing type-safe key validation, and adding a global language switcher.
New Features:
Enhancements:
useI18n
/useModuleI18n
composables instead of hardcoded stringsLanguageSwitcher
component and integrate global language switchingBuild:
vue-i18n
dependency and integrate dynamic translation loaderChores:
src/i18n