i18n: complete internationalization for settings panel
- Add missing translation keys for all hint texts and descriptions - Remove all hardcoded defaultValue parameters from components - Add translations for window behavior, directory settings, and theme settings - Add translations for provider-related UI elements - Improve consistency across Chinese and English translations Translation additions: - common.toggleTheme - settings.windowBehaviorHint, claudeConfigDirDescription, codexConfigDirDescription - provider.* (12 new keys) - providerForm.* (15 new keys) - providerPreset.* (4 new keys) Modified files: 10 Lines changed: +132 -74
This commit is contained in:
@@ -22,7 +22,7 @@ export function ModeToggle() {
|
||||
<Sun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span className="sr-only">
|
||||
{t("common.toggleTheme", { defaultValue: "切换主题" })}
|
||||
{t("common.toggleTheme")}
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -75,9 +75,7 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
} catch (error) {
|
||||
console.error("[AboutSection] Failed to open release notes", error);
|
||||
toast.error(
|
||||
t("settings.openReleaseNotesFailed", {
|
||||
defaultValue: "打开更新日志失败",
|
||||
}),
|
||||
t("settings.openReleaseNotesFailed"),
|
||||
);
|
||||
}
|
||||
}, [t, updateInfo?.availableVersion, version]);
|
||||
@@ -101,9 +99,7 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
} catch (error) {
|
||||
console.error("[AboutSection] Update failed", error);
|
||||
toast.error(
|
||||
t("settings.updateFailed", {
|
||||
defaultValue: "更新安装失败,已尝试打开下载页面。",
|
||||
}),
|
||||
t("settings.updateFailed"),
|
||||
);
|
||||
try {
|
||||
await settingsApi.checkUpdates();
|
||||
@@ -122,29 +118,25 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
try {
|
||||
const available = await checkUpdate();
|
||||
if (!available) {
|
||||
toast.success(t("settings.upToDate", { defaultValue: "已是最新版本" }));
|
||||
toast.success(t("settings.upToDate"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[AboutSection] Check update failed", error);
|
||||
toast.error(
|
||||
t("settings.checkUpdateFailed", {
|
||||
defaultValue: "检查更新失败,请稍后重试。",
|
||||
}),
|
||||
t("settings.checkUpdateFailed"),
|
||||
);
|
||||
}
|
||||
}, [checkUpdate, hasUpdate, isPortable, resetDismiss, t, updateHandle]);
|
||||
|
||||
const displayVersion =
|
||||
version ?? t("common.unknown", { defaultValue: "未知" });
|
||||
version ?? t("common.unknown");
|
||||
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-sm font-medium">{t("common.about")}</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.aboutHint", {
|
||||
defaultValue: "查看版本信息与更新状态。",
|
||||
})}
|
||||
{t("settings.aboutHint")}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@@ -163,9 +155,7 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
{isPortable ? (
|
||||
<p className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<Info className="h-3 w-3" />
|
||||
{t("settings.portableMode", {
|
||||
defaultValue: "当前为便携版,更新需手动下载。",
|
||||
})}
|
||||
{t("settings.portableMode")}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -190,20 +180,19 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
{isDownloading ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
{t("settings.updating", { defaultValue: "安装更新..." })}
|
||||
{t("settings.updating")}
|
||||
</span>
|
||||
) : hasUpdate ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Download className="h-4 w-4" />
|
||||
{t("settings.updateTo", {
|
||||
defaultValue: "更新到 {{version}}",
|
||||
version: updateInfo?.availableVersion ?? "",
|
||||
})}
|
||||
</span>
|
||||
) : isChecking ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<RefreshCw className="h-4 w-4 animate-spin" />
|
||||
{t("settings.checking", { defaultValue: "检查中..." })}
|
||||
{t("settings.checking")}
|
||||
</span>
|
||||
) : (
|
||||
t("settings.checkForUpdates")
|
||||
@@ -216,7 +205,6 @@ export function AboutSection({ isPortable }: AboutSectionProps) {
|
||||
<div className="rounded-md bg-muted/40 px-3 py-2 text-xs text-muted-foreground">
|
||||
<p>
|
||||
{t("settings.updateAvailable", {
|
||||
defaultValue: "检测到新版本:{{version}}",
|
||||
version: updateInfo.availableVersion,
|
||||
})}
|
||||
</p>
|
||||
|
||||
@@ -57,9 +57,7 @@ export function DirectorySettings({
|
||||
|
||||
<DirectoryInput
|
||||
label={t("settings.claudeConfigDir")}
|
||||
description={t("settings.claudeConfigDirDescription", {
|
||||
defaultValue: "覆盖 Claude 配置目录 (settings.json)。",
|
||||
})}
|
||||
description={t("settings.claudeConfigDirDescription")}
|
||||
value={claudeDir}
|
||||
resolvedValue={resolvedDirs.claude}
|
||||
placeholder={t("settings.browsePlaceholderClaude")}
|
||||
@@ -70,9 +68,7 @@ export function DirectorySettings({
|
||||
|
||||
<DirectoryInput
|
||||
label={t("settings.codexConfigDir")}
|
||||
description={t("settings.codexConfigDirDescription", {
|
||||
defaultValue: "覆盖 Codex 配置目录。",
|
||||
})}
|
||||
description={t("settings.codexConfigDirDescription")}
|
||||
value={codexDir}
|
||||
resolvedValue={resolvedDirs.codex}
|
||||
placeholder={t("settings.browsePlaceholderCodex")}
|
||||
|
||||
@@ -47,9 +47,7 @@ export function ImportExportSection({
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-sm font-medium">{t("settings.importExport")}</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.importExportHint", {
|
||||
defaultValue: "导入导出 cc-switch 配置,便于备份或迁移。",
|
||||
})}
|
||||
{t("settings.importExportHint")}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@@ -92,7 +90,7 @@ export function ImportExportSection({
|
||||
{selectedFile ? (
|
||||
<Button type="button" variant="ghost" onClick={onClear}>
|
||||
<XCircle className="mr-2 h-4 w-4" />
|
||||
{t("common.clear", { defaultValue: "清除" })}
|
||||
{t("common.clear")}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -103,9 +101,7 @@ export function ImportExportSection({
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.noFileSelected", {
|
||||
defaultValue: "尚未选择配置文件。",
|
||||
})}
|
||||
{t("settings.noFileSelected")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -147,7 +143,7 @@ function ImportStatusMessage({
|
||||
<div>
|
||||
<p className="font-medium">{t("settings.importing")}</p>
|
||||
<p className="text-muted-foreground">
|
||||
{t("common.loading", { defaultValue: "正在处理..." })}
|
||||
{t("common.loading")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,10 +160,10 @@ function ImportStatusMessage({
|
||||
<p className="font-medium">{t("settings.importSuccess")}</p>
|
||||
{backupId ? (
|
||||
<p className="text-xs">
|
||||
{t("settings.backupId", { defaultValue: "备份 ID" })}: {backupId}
|
||||
{t("settings.backupId")}: {backupId}
|
||||
</p>
|
||||
) : null}
|
||||
<p>{t("settings.autoReload", { defaultValue: "即将刷新列表。" })}</p>
|
||||
<p>{t("settings.autoReload")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -175,14 +171,14 @@ function ImportStatusMessage({
|
||||
|
||||
const message =
|
||||
errorMessage ||
|
||||
t("settings.importFailed", { defaultValue: "导入失败,请重试。" });
|
||||
t("settings.importFailed");
|
||||
|
||||
return (
|
||||
<div className={`${baseClass} border-red-200 bg-red-100/70 text-red-600`}>
|
||||
<AlertCircle className="mt-0.5 h-4 w-4" />
|
||||
<div className="space-y-1">
|
||||
<p className="font-medium">
|
||||
{t("settings.importFailed", { defaultValue: "导入失败" })}
|
||||
{t("settings.importFailed")}
|
||||
</p>
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
|
||||
@@ -15,9 +15,7 @@ export function LanguageSettings({ value, onChange }: LanguageSettingsProps) {
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-sm font-medium">{t("settings.language")}</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.languageHint", {
|
||||
defaultValue: "切换后立即预览界面语言,保存后永久生效。",
|
||||
})}
|
||||
{t("settings.languageHint")}
|
||||
</p>
|
||||
</header>
|
||||
<div className="inline-flex gap-1 rounded-md border border-border bg-background p-1">
|
||||
|
||||
@@ -143,9 +143,7 @@ export function SettingsDialog({
|
||||
setShowRestartPrompt(false);
|
||||
if (import.meta.env.DEV) {
|
||||
toast.success(
|
||||
t("settings.devModeRestartHint", {
|
||||
defaultValue: "开发模式下不支持自动重启,请手动重新启动应用。",
|
||||
}),
|
||||
t("settings.devModeRestartHint"),
|
||||
);
|
||||
closeAfterSave();
|
||||
return;
|
||||
@@ -156,9 +154,7 @@ export function SettingsDialog({
|
||||
} catch (error) {
|
||||
console.error("[SettingsDialog] Failed to restart app", error);
|
||||
toast.error(
|
||||
t("settings.restartFailed", {
|
||||
defaultValue: "应用重启失败,请手动关闭后重新打开。",
|
||||
}),
|
||||
t("settings.restartFailed"),
|
||||
);
|
||||
} finally {
|
||||
closeAfterSave();
|
||||
@@ -187,10 +183,10 @@ export function SettingsDialog({
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="general">
|
||||
{t("settings.tabGeneral", { defaultValue: "通用" })}
|
||||
{t("settings.tabGeneral")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="advanced">
|
||||
{t("settings.tabAdvanced", { defaultValue: "高级" })}
|
||||
{t("settings.tabAdvanced")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="about">{t("common.about")}</TabsTrigger>
|
||||
</TabsList>
|
||||
@@ -262,7 +258,7 @@ export function SettingsDialog({
|
||||
{isSaving ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
{t("settings.saving", { defaultValue: "正在保存..." })}
|
||||
{t("settings.saving")}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
@@ -284,9 +280,7 @@ export function SettingsDialog({
|
||||
</DialogHeader>
|
||||
<div className="px-6">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("settings.restartRequiredMessage", {
|
||||
defaultValue: "配置目录已变更,需要重启应用生效。",
|
||||
})}
|
||||
{t("settings.restartRequiredMessage")}
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
|
||||
@@ -13,9 +13,7 @@ export function ThemeSettings() {
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-sm font-medium">{t("settings.theme")}</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.themeHint", {
|
||||
defaultValue: "选择应用的外观主题,立即生效。",
|
||||
})}
|
||||
{t("settings.themeHint")}
|
||||
</p>
|
||||
</header>
|
||||
<div className="inline-flex gap-1 rounded-md border border-border bg-background p-1">
|
||||
@@ -24,21 +22,21 @@ export function ThemeSettings() {
|
||||
onClick={() => setTheme("light")}
|
||||
icon={Sun}
|
||||
>
|
||||
{t("settings.themeLight", { defaultValue: "浅色" })}
|
||||
{t("settings.themeLight")}
|
||||
</ThemeButton>
|
||||
<ThemeButton
|
||||
active={theme === "dark"}
|
||||
onClick={() => setTheme("dark")}
|
||||
icon={Moon}
|
||||
>
|
||||
{t("settings.themeDark", { defaultValue: "深色" })}
|
||||
{t("settings.themeDark")}
|
||||
</ThemeButton>
|
||||
<ThemeButton
|
||||
active={theme === "system"}
|
||||
onClick={() => setTheme("system")}
|
||||
icon={Monitor}
|
||||
>
|
||||
{t("settings.themeSystem", { defaultValue: "跟随系统" })}
|
||||
{t("settings.themeSystem")}
|
||||
</ThemeButton>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -15,9 +15,7 @@ export function WindowSettings({ settings, onChange }: WindowSettingsProps) {
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-sm font-medium">{t("settings.windowBehavior")}</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.windowBehaviorHint", {
|
||||
defaultValue: "配置窗口最小化与 Claude 插件联动策略。",
|
||||
})}
|
||||
{t("settings.windowBehaviorHint")}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user