feat(i18n): improve language switch functionality and update UI elements for better consistency

This commit is contained in:
HouYunFei
2026-08-05 15:08:38 +08:00
parent 8e5073ccc2
commit e48f5df9ca
5 changed files with 12 additions and 23 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
## Unreleased
+ [新增] 文档站支持英文默认与简体中文切换,正文、导航、搜索及 LLM 文档接口按语言独立展示。
+ [新增] 前端搭建中英文国际化框架,支持从右上角持久化切换全局导航、首页、配置中心、Ant Design 和日期组件语言。
+ [新增] 前端搭建中英文国际化框架,支持从右上角一键持久化切换全局导航、首页、配置中心、Ant Design 和日期组件语言。
+ [修复] 统一 Ant Design 下拉菜单与选择器在浅色、深色主题下的弹层及交互状态颜色。
+ [新增] Agent 支持查看、创建、编辑、删除、启停和显式调用本地 Codex Skill,并同步多标签页变更。
+ [新增] Agent 支持从当前对话或当前画布生成可编辑的 Skill 草稿,并在用户确认后保存。
@@ -7,7 +7,7 @@ description: 当前版本已实现但仍需人工验证的变更项
- 文档站国际化:访问文档站首页及 `/docs` 应默认显示英文,切换简体中文后 URL 应带 `/zh-CN` 且首页、顶部导航、侧边栏、正文、搜索框和页面操作文案均显示中文;刷新、站内跳转和再次切回 English 后语言应保持正确,搜索结果与 Markdown/LLM 文档内容不得混入另一种语言。
- 国际化基础框架:从页面右上角切换简体中文或 English 后,首页、顶部及移动端导航、版本弹窗、配置主面板、渠道编辑、模型选择、页面标题、页面描述和 Ant Design 内置文案应立即切换,刷新页面后保持所选语言;偏好设置中不应再显示语言选项,WebDAV 同步错误、提示词来源及其他尚未接入翻译的业务模块暂时仍可能显示中文。
- 全局弹层主题:右上角语言菜单及全站 Dropdown、Menu、Select、Cascader、TreeSelect 等弹层在浅色主题下应使用白色背景、轻灰悬停和选中态,在深色主题下应使用深色背景与低对比度浅色交互态,不应再出现默认黑灰色块、文字对比不足或单个页面配色不一致。
- 全局弹层主题:全站 Dropdown、Menu、Select、Cascader、TreeSelect 等弹层在浅色主题下应使用白色背景、轻灰悬停和选中态,在深色主题下应使用深色背景与低对比度浅色交互态,不应再出现默认黑灰色块、文字对比不足或单个页面配色不一致。
- 画布节点缩放稳定性:选中任意节点并反复拖动四角缩放时,节点工具条应在缩放期间隐藏、松开后恢复,页面不应再出现 `Maximum update depth exceeded`。
- Agent MCP 初始化状态:首次进入空白对话、点击「新对话」或恢复历史对话后,无需先发送消息,对话区应立即显示 Codex 会话准备,并列出 `codex_apps`、`infinite-canvas`、`node_repl` 等完整 MCP 服务状态;全部服务进入终态前可输入并保留草稿,但发送按钮、上传和回车提交应禁用;可选 MCP 失败时应提示警告但允许发送,`infinite-canvas` 缺失或失败时应阻止发送并显示明确原因;首轮及后续任务运行期间再次收到的 MCP 状态只进入诊断日志,模型开始处理及回复完成后对话区不应残留初始化卡片。
- Agent 模型设置:连接 Canvas Agent 后,输入框左下方应显示当前 Codex 模型与推理强度;模型列表应来自当前账号实际可用模型且不显示内部审查模型或重复项,切换模型后强度选项随模型能力更新且无空白选项,刷新页面后保留选择,发送任务时实际使用所选模型和强度;本地控制台与右侧「日志」应记录本轮使用的模型和推理强度。
@@ -1,6 +1,5 @@
import type { CSSProperties } from "react";
import { BookOpen, Globe2, Keyboard, Puzzle, Settings2 } from "lucide-react";
import { Dropdown } from "antd";
import { BookOpen, Keyboard, Puzzle, Settings2 } from "lucide-react";
import { useTranslation } from "react-i18next";
import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
@@ -26,11 +25,14 @@ export function UserStatusActions({ showConfig = true, variant = "default", onOp
const setTheme = useThemeStore((state) => state.setTheme);
const openConfigDialog = useConfigStore((state) => state.openConfigDialog);
const canvasTheme = canvasThemes[theme];
const naturalIconClass = "inline-flex size-7 shrink-0 items-center justify-center text-stone-600 transition hover:text-stone-950 dark:text-stone-300 dark:hover:text-white [&_svg]:size-4";
const naturalIconClass = "inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-stone-600 transition-colors hover:bg-black/5 hover:text-stone-950 dark:text-stone-300 dark:hover:bg-white/10 dark:hover:text-white [&_svg]:size-4";
const iconStyle: CSSProperties | undefined = variant === "canvas" ? { color: canvasTheme.node.text } : undefined;
const versionStyle = iconStyle;
const gitHubClassName = "size-7 text-base";
const gitHubStyle = iconStyle;
const locale = i18n.resolvedLanguage as AppLocale;
const nextLocale = locale === "zh-CN" ? "en-US" : "zh-CN";
const languageLabel = t("topNav.switchLanguage", { language: t(nextLocale === "zh-CN" ? "locale.zhCN" : "locale.enUS") });
return (
<div className="inline-flex shrink-0 items-center gap-1">
@@ -47,22 +49,9 @@ export function UserStatusActions({ showConfig = true, variant = "default", onOp
<Settings2 className="size-4" />
</button>
) : null}
<Dropdown
trigger={["click"]}
placement="bottomRight"
menu={{
selectedKeys: [i18n.resolvedLanguage],
items: [
{ key: "zh-CN", label: t("locale.zhCN") },
{ key: "en-US", label: t("locale.enUS") },
],
onClick: ({ key }) => void changeAppLocale(key as AppLocale),
}}
>
<button type="button" className={naturalIconClass} style={iconStyle} aria-label={t("topNav.language")} title={t("topNav.language")}>
<Globe2 className="size-4" />
</button>
</Dropdown>
<button type="button" className={`${naturalIconClass} text-[11px] font-semibold tracking-tight`} style={iconStyle} onClick={() => void changeAppLocale(nextLocale)} aria-label={languageLabel} title={languageLabel}>
{locale === "zh-CN" ? "中" : "EN"}
</button>
<AnimatedThemeToggler theme={theme} onThemeChange={setTheme} className={naturalIconClass} style={iconStyle} aria-label={t(theme === "dark" ? "topNav.lightTheme" : "topNav.darkTheme")} title={t(theme === "dark" ? "topNav.lightTheme" : "topNav.darkTheme")} />
<VersionReleaseModal style={versionStyle} />
<GitHubLink className={cn("bg-transparent hover:bg-transparent dark:hover:bg-transparent", gitHubClassName)} style={gitHubStyle} />
+1 -1
View File
@@ -323,7 +323,7 @@ export default {
closeAgent: "Close Agent",
plugins: "Node plugins",
docs: "Documentation",
language: "Change language",
switchLanguage: "Switch to {{language}}",
shortcuts: "Keyboard shortcuts",
lightTheme: "Switch to light theme",
darkTheme: "Switch to dark theme",
+1 -1
View File
@@ -323,7 +323,7 @@ export default {
closeAgent: "收起 Agent",
plugins: "节点插件",
docs: "文档",
language: "切换语言",
switchLanguage: "切换到 {{language}}",
shortcuts: "快捷键",
lightTheme: "切换到浅色主题",
darkTheme: "切换到深色主题",