feat: 前端 UI 国际化,支持中/英/日三语切换

之前语言设置只影响后端 AI 行为,前端 UI 文字始终为中文。
新增轻量 i18n 模块(React Context + 翻译字典),切换语言后所有 UI 文字即时刷新。

- 新增 i18n 核心模块和 zh-CN/en-US/ja-JP 翻译文件(约 72 个 key)
- App.tsx 拆分为 App(Provider)+ AppContent(业务),确保 Hook 可访问 Context
- 所有组件通过 useI18n().t() 获取翻译文本
- errors.ts 改为接受翻译函数参数
- storage.ts saveConfig 时派发事件通知 locale 变化
This commit is contained in:
2026-06-14 14:34:30 +08:00
parent d8bbdb1744
commit e95b1603c1
14 changed files with 418 additions and 71 deletions

View File

@@ -3,6 +3,7 @@
// 职责主题切换、TTS 开关、detail level 切换、语言选择
// ============================================================
import { useI18n } from "../../lib/i18n";
import type { SessionConfig, Theme } from "../../types";
interface ConfigPanelProps {
@@ -14,40 +15,42 @@ interface ConfigPanelProps {
}
export function ConfigPanel({ config, theme, onUpdate, onThemeChange, onClose }: ConfigPanelProps) {
const { t } = useI18n();
return (
<div className="drawer-overlay" onClick={onClose}>
<div className="drawer" onClick={(e) => e.stopPropagation()}>
<div className="drawer__header">
<span className="drawer__title"></span>
<span className="drawer__title">{t("settings.title")}</span>
<button className="drawer__close" onClick={onClose}></button>
</div>
<div className="drawer__body">
<div className="config-group">
<div className="config-group__title"></div>
<div className="config-group__title">{t("settings.appearance")}</div>
<label className="config-row">
<div className="config-row__info">
<span className="config-row__label"></span>
<span className="config-row__desc"></span>
<span className="config-row__label">{t("settings.theme")}</span>
<span className="config-row__desc">{t("settings.theme.desc")}</span>
</div>
<select
value={theme}
onChange={(e) => onThemeChange(e.target.value as Theme)}
>
<option value="dark"></option>
<option value="light"></option>
<option value="dark">{t("settings.theme.dark")}</option>
<option value="light">{t("settings.theme.light")}</option>
</select>
</label>
</div>
<div className="config-group">
<div className="config-group__title"></div>
<div className="config-group__title">{t("settings.session")}</div>
<label className="config-row">
<div className="config-row__info">
<span className="config-row__label"></span>
<span className="config-row__desc">AI </span>
<span className="config-row__label">{t("settings.tts")}</span>
<span className="config-row__desc">{t("settings.tts.desc")}</span>
</div>
<input
type="checkbox"
@@ -58,22 +61,22 @@ export function ConfigPanel({ config, theme, onUpdate, onThemeChange, onClose }:
<label className="config-row">
<div className="config-row__info">
<span className="config-row__label"></span>
<span className="config-row__desc"></span>
<span className="config-row__label">{t("settings.detail")}</span>
<span className="config-row__desc">{t("settings.detail.desc")}</span>
</div>
<select
value={config.detailLevel}
onChange={(e) => onUpdate({ detailLevel: e.target.value as "low" | "high" })}
>
<option value="low"></option>
<option value="high"></option>
<option value="low">{t("settings.detail.low")}</option>
<option value="high">{t("settings.detail.high")}</option>
</select>
</label>
<label className="config-row">
<div className="config-row__info">
<span className="config-row__label"></span>
<span className="config-row__desc"></span>
<span className="config-row__label">{t("settings.language")}</span>
<span className="config-row__desc">{t("settings.language.desc")}</span>
</div>
<select
value={config.language}