diff --git a/wpf/QuotaFloat.Wpf.csproj b/wpf/QuotaFloat.Wpf.csproj index 450f72a..c25af3a 100644 --- a/wpf/QuotaFloat.Wpf.csproj +++ b/wpf/QuotaFloat.Wpf.csproj @@ -8,10 +8,10 @@ QuotaFloat.Wpf QuotaFloat.Wpf Assets\QuoteFloat.ico - 1.1.1 - 1.1.1.0 - 1.1.1.0 - 1.1.1 + 1.1.2 + 1.1.2.0 + 1.1.2.0 + 1.1.2 app.manifest embedded true diff --git a/wpf/Resources/UiText.cs b/wpf/Resources/UiText.cs index 7924c29..a353f19 100644 --- a/wpf/Resources/UiText.cs +++ b/wpf/Resources/UiText.cs @@ -15,6 +15,8 @@ public sealed record WidgetText( string Synced, string Refresh, string Settings, + string TrayOpenSettings, + string TrayExit, string SyncAutomationName, string RefreshAutomationName, string SettingsAutomationName, @@ -47,6 +49,8 @@ public static WidgetText For(string language) => string.Equals(language, "en-US" Synced: "已同步", Refresh: "刷新", Settings: "设置", + TrayOpenSettings: "打开设置", + TrayExit: "退出 Quote Float", SyncAutomationName: "已同步", RefreshAutomationName: "刷新", SettingsAutomationName: "设置", @@ -75,6 +79,8 @@ public static WidgetText For(string language) => string.Equals(language, "en-US" Synced: "Synced", Refresh: "Refresh", Settings: "Settings", + TrayOpenSettings: "Open Settings", + TrayExit: "Exit Quote Float", SyncAutomationName: "Synced", RefreshAutomationName: "Refresh", SettingsAutomationName: "Settings", diff --git a/wpf/Services/TrayIconService.cs b/wpf/Services/TrayIconService.cs index 3d86b56..90c8501 100644 --- a/wpf/Services/TrayIconService.cs +++ b/wpf/Services/TrayIconService.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; +using QuotaFloat.Wpf.Resources; namespace QuotaFloat.Wpf.Services; @@ -28,16 +29,18 @@ public sealed class TrayIconService : IDisposable private readonly Action openSettings; private readonly Action exitApplication; + private readonly Func textProvider; private readonly HwndSource hwndSource; private readonly uint iconId = 0x5146; private IntPtr productIcon; private bool disposed; private bool iconAdded; - public TrayIconService(Window owner, Action openSettings, Action exitApplication) + public TrayIconService(Window owner, Action openSettings, Action exitApplication, Func textProvider) { this.openSettings = openSettings; this.exitApplication = exitApplication; + this.textProvider = textProvider; var hwnd = new WindowInteropHelper(owner).Handle; hwndSource = HwndSource.FromHwnd(hwnd) @@ -153,8 +156,9 @@ private void ShowContextMenu(IntPtr hwnd) try { - AppendMenu(menu, MfString | MfEnabled, OpenSettingsCommandId, "打开设置"); - AppendMenu(menu, MfString | MfEnabled, ExitCommandId, "退出 Quote Float"); + var text = textProvider(); + AppendMenu(menu, MfString | MfEnabled, OpenSettingsCommandId, text.TrayOpenSettings); + AppendMenu(menu, MfString | MfEnabled, ExitCommandId, text.TrayExit); SetForegroundWindow(hwnd); if (!GetCursorPos(out var point)) { diff --git a/wpf/Windows/MainWindow.xaml.cs b/wpf/Windows/MainWindow.xaml.cs index 15da08d..685481d 100644 --- a/wpf/Windows/MainWindow.xaml.cs +++ b/wpf/Windows/MainWindow.xaml.cs @@ -84,7 +84,8 @@ private void MainWindow_OnSourceInitialized(object? sender, EventArgs e) var hwnd = new WindowInteropHelper(this).Handle; hwndSource = HwndSource.FromHwnd(hwnd); hwndSource?.AddHook(MainWindowWndProc); - trayIconService = new TrayIconService(this, ShowSettings, ExitApplication); + trayIconService = new TrayIconService(this, ShowSettings, ExitApplication, + () => WidgetText.For(preferenceStore.Current.Language)); widgetController.ApplyState(widgetController.State); } diff --git a/wpf/app.manifest b/wpf/app.manifest index 11a20cd..cb514aa 100644 --- a/wpf/app.manifest +++ b/wpf/app.manifest @@ -1,6 +1,6 @@ - + diff --git a/wpf/tests/Program.cs b/wpf/tests/Program.cs index 36a1711..6cbc580 100644 --- a/wpf/tests/Program.cs +++ b/wpf/tests/Program.cs @@ -45,6 +45,16 @@ void Check(bool condition, string label) Check(QuotaDisplayState.LoadingState.Status == QuotaUiStatus.Loading && QuotaDisplayState.LoadingState.ActiveRequests == 1, "loading state is explicit"); Check(WidgetText.For("en-US").Status(QuotaUiStatus.Stale, true) == "Refreshing" && WidgetText.For("zh-Hans").Status(QuotaUiStatus.Stale, true) == "刷新中", "refreshing copy is localized"); +var trayLanguage = "zh-Hans"; +Func trayText = () => WidgetText.For(trayLanguage); +Check(trayText().TrayOpenSettings == "打开设置" && trayText().TrayExit == "退出 Quote Float", + "Chinese tray labels are exact"); +trayLanguage = "en-US"; +Check(trayText().TrayOpenSettings == "Open Settings" && trayText().TrayExit == "Exit Quote Float", + "English tray labels are exact"); +trayLanguage = "zh-Hans"; +Check(trayText().TrayOpenSettings == "打开设置" && trayText().TrayExit == "退出 Quote Float", + "tray labels refresh dynamically from current language"); var refreshingSnapshot = new QuotaSnapshot("Plus", new QuotaWindow(72, DateTimeOffset.UtcNow.AddHours(1), 18000, "5h"), new QuotaWindow(38, DateTimeOffset.UtcNow.AddDays(2), 604800, "Weekly"), 2, Array.Empty(), DateTimeOffset.UtcNow); using var refreshingSource = new GateAfterFirstSource(refreshingSnapshot); @@ -292,7 +302,7 @@ void Check(bool condition, string label) } Console.WriteLine("PASS: QF-WPF-009 focused fixtures and orchestration checks"); -Console.WriteLine($"RESULTS: fixtures=35; checks={checkCount}; activeRequests={source.MaxActive}; refreshCalls={source.CallCount}; backoffCalls={backoffSource.CallCount}; privacy=normalized-values-only"); +Console.WriteLine($"RESULTS: fixtures=36; checks={checkCount}; activeRequests={source.MaxActive}; refreshCalls={source.CallCount}; backoffCalls={backoffSource.CallCount}; privacy=normalized-values-only"); return 0; sealed class ManualTimeProvider : TimeProvider