feat(i18n): implement internationalization framework and update UI components for language support

This commit is contained in:
HouYunFei
2026-08-05 14:08:03 +08:00
parent 9bccd0ff1a
commit 0f6809251a
116 changed files with 3269 additions and 1870 deletions
+20 -17
View File
@@ -1,14 +1,16 @@
import { type ReactNode, useState } from "react";
import { ConfigProvider, Switch } from "antd";
import { useTranslation } from "react-i18next";
import i18n from "@/i18n";
import { type CanvasTheme } from "@/lib/canvas-theme";
import type { AiConfig } from "@/stores/use-config-store";
const qualityOptions = [
{ value: "auto", label: "自动" },
{ value: "high", label: "高" },
{ value: "medium", label: "中" },
{ value: "low", label: "低" },
{ value: "auto", labelKey: "auto" },
{ value: "high", labelKey: "high" },
{ value: "medium", labelKey: "medium" },
{ value: "low", labelKey: "low" },
];
const DIMENSION_STEP = 16;
@@ -28,7 +30,7 @@ const aspectOptions = [
{ value: "auto", label: "auto", width: 0, height: 0, icon: "auto" },
];
export const imageQualityOptions = qualityOptions.map((item) => ({ value: item.value, label: item.label }));
export const imageQualityOptions = qualityOptions.map((item) => ({ value: item.value, get label() { return i18n.t(`settingsPanels.common.${item.labelKey}`); } }));
export const imageAspectOptions = aspectOptions.map((item) => ({ value: item.size || item.value, label: item.label }));
type ImageSettingsPanelProps = {
@@ -42,6 +44,7 @@ type ImageSettingsPanelProps = {
};
export function ImageSettingsPanel({ config, onConfigChange, theme, showTitle = true, className = "w-[320px] space-y-4 rounded-2xl px-1 py-0.5", maxCount = 15, quickCount = 10 }: ImageSettingsPanelProps) {
const { t } = useTranslation();
const [snapDimensionToStep, setSnapDimensionToStep] = useState(true);
const quality = config.quality || "auto";
const count = Math.max(1, Math.min(maxCount, Math.floor(Math.abs(Number(config.count)) || 1)));
@@ -71,25 +74,25 @@ export function ImageSettingsPanel({ config, onConfigChange, theme, showTitle =
if (document.activeElement instanceof HTMLInputElement && event.currentTarget.contains(document.activeElement)) document.activeElement.blur();
}}
>
{showTitle ? <div className="text-lg font-semibold">图像设置</div> : null}
{showTitle ? <div className="text-lg font-semibold">{t("settingsPanels.image.title")}</div> : null}
<div className="space-y-2.5">
<SettingTitle color={theme.node.muted}>质量</SettingTitle>
<SettingTitle color={theme.node.muted}>{t("settingsPanels.image.quality")}</SettingTitle>
<div className="grid grid-cols-4 gap-2.5">
{qualityOptions.map((item) => (
<OptionPill key={item.value} selected={quality === item.value} theme={theme} onClick={() => onConfigChange("quality", item.value)}>
{item.label}
{t(`settingsPanels.common.${item.labelKey}`)}
</OptionPill>
))}
</div>
</div>
<div className="space-y-2.5">
<div className="flex items-center justify-between gap-3">
<SettingTitle color={theme.node.muted}>尺寸</SettingTitle>
<SettingTitle color={theme.node.muted}>{t("settingsPanels.image.size")}</SettingTitle>
<div className="flex items-center gap-2">
<span className="text-xs font-medium" style={{ color: theme.node.muted }}>
16倍数对齐
{t("settingsPanels.image.align16")}
</span>
<span title="输入完成后自动向上补成 16 的倍数" onMouseDown={(event) => event.stopPropagation()}>
<span title={t("settingsPanels.image.align16Hint")} onMouseDown={(event) => event.stopPropagation()}>
<Switch size="small" checked={snapDimensionToStep} onChange={setSnapDimensionToStep} />
</span>
</div>
@@ -101,7 +104,7 @@ export function ImageSettingsPanel({ config, onConfigChange, theme, showTitle =
</div>
</div>
<div className="space-y-2.5">
<SettingTitle color={theme.node.muted}>宽高比</SettingTitle>
<SettingTitle color={theme.node.muted}>{t("settingsPanels.image.aspectRatio")}</SettingTitle>
<div className="grid grid-cols-4 gap-2.5">
{aspectOptions.map((item) => (
<button
@@ -120,9 +123,9 @@ export function ImageSettingsPanel({ config, onConfigChange, theme, showTitle =
</div>
<div className="flex items-center justify-between gap-3">
<div className="space-y-0.5">
<SettingTitle color={theme.node.muted}>透明背景</SettingTitle>
<SettingTitle color={theme.node.muted}>{t("settingsPanels.image.transparent")}</SettingTitle>
<div className="text-xs" style={{ color: theme.node.muted, opacity: 0.75 }}>
开启后生成无背景的透明图像(仅部分模型可用)
{t("settingsPanels.image.transparentHint")}
</div>
</div>
<span onMouseDown={(event) => event.stopPropagation()}>
@@ -130,11 +133,11 @@ export function ImageSettingsPanel({ config, onConfigChange, theme, showTitle =
</span>
</div>
<div className="space-y-2.5">
<SettingTitle color={theme.node.muted}>生成张数</SettingTitle>
<SettingTitle color={theme.node.muted}>{t("settingsPanels.image.count")}</SettingTitle>
<div className="grid grid-cols-4 gap-2.5">
{Array.from({ length: quickCount }, (_, index) => index + 1).map((value) => (
<OptionPill key={value} selected={count === value} theme={theme} onClick={() => onConfigChange("count", String(value))}>
{value} 张
{t("settingsPanels.image.images", { count: value })}
</OptionPill>
))}
<CountInput value={count} max={maxCount} theme={theme} onChange={(value) => onConfigChange("count", String(value || 1))} />
@@ -159,7 +162,7 @@ export function ImageSettingsTheme({ theme, children }: { theme: CanvasTheme; ch
}
export function imageQualityLabel(value: string) {
return ({ auto: "自动", high: "高", medium: "中", low: "低" } as Record<string, string>)[value] || value;
return (["auto", "high", "medium", "low"].includes(value) ? i18n.t(`settingsPanels.common.${value}`) : value);
}
export function imageSizeLabel(size: string) {