diff --git a/apps/desktop-tauri/src/components/MenuCard.test.tsx b/apps/desktop-tauri/src/components/MenuCard.test.tsx index 35dd82826e..15e3eadc5a 100644 --- a/apps/desktop-tauri/src/components/MenuCard.test.tsx +++ b/apps/desktop-tauri/src/components/MenuCard.test.tsx @@ -375,6 +375,31 @@ describe("MenuCard", () => { expect(screen.queryByText("Session")).not.toBeInTheDocument(); }); + it("uses explicit hourly quota labels in Simplified Chinese", async () => { + tauriMocks.getLocaleStrings.mockResolvedValue(buildBundle({}, "chinese")); + const snapshot = provider(null, 31); + snapshot.primary = rateWindow(31, { windowMinutes: 3 * 60 }); + snapshot.selectedMetric = snapshot.primary; + + renderCard(snapshot); + + expect(await screen.findByText("3 小时")).toBeInTheDocument(); + expect(screen.queryByText("Session")).not.toBeInTheDocument(); + }); + + it("maps a Simplified Chinese session-labelled weekly window to Weekly", async () => { + tauriMocks.getLocaleStrings.mockResolvedValue( + buildBundle({ ProviderWeeklyLabel: "本周" }, "chinese"), + ); + const snapshot = provider(null, 31); + snapshot.primary = rateWindow(31, { windowMinutes: 7 * 24 * 60 }); + snapshot.selectedMetric = snapshot.primary; + + renderCard(snapshot); + + expect(await screen.findByText("本周")).toBeInTheDocument(); + }); + it("notifies the tray panel after async local usage data loads", async () => { const onLayoutChange = vi.fn(); diff --git a/apps/desktop-tauri/src/components/MenuCard.tsx b/apps/desktop-tauri/src/components/MenuCard.tsx index d9e099f34a..e879e8fff5 100644 --- a/apps/desktop-tauri/src/components/MenuCard.tsx +++ b/apps/desktop-tauri/src/components/MenuCard.tsx @@ -72,8 +72,18 @@ export function maskEmail(email: string): string { function localizeWindowLabel( raw: string | undefined, t: (key: LocaleKey) => string, + language?: string, + windowMinutes?: number | null, ): string { const normalized = raw?.trim().toLowerCase(); + // Upstream 0.55.0 #3070: quota windows in Simplified Chinese use their + // actual duration instead of the conversational Session wording. + if (language === "chinese" && normalized === "session" && windowMinutes != null) { + if (windowMinutes === 7 * 24 * 60) return t("ProviderWeeklyLabel"); + if (windowMinutes >= 60 && windowMinutes <= 12 * 60 && windowMinutes % 60 === 0) { + return `${windowMinutes / 60} 小时`; + } + } if (normalized === "weekly") { return t("ProviderWeeklyLabel"); } @@ -127,7 +137,7 @@ export default function MenuCard({ compactMetrics = false, costSummaryDisplayStyle, } = display; - const { t } = useLocale(); + const { t, language } = useLocale(); const [chartData, setChartData] = useState(null); const [pricingStatus, setPricingStatus] = useState(null); @@ -179,7 +189,7 @@ export default function MenuCard({ : [ { id: "primary", - label: provider.primaryLabel ?? t("DetailWindowPrimary"), + label: localizeWindowLabel(provider.primaryLabel, t, language, provider.primary.windowMinutes) || t("DetailWindowPrimary"), snap: provider.primary, }, ]),