feat(i18n): Windows app bilingual support (zh-Hans / English)#109
Merged
debugtheworldbot merged 24 commits intomainfrom Apr 29, 2026
Merged
feat(i18n): Windows app bilingual support (zh-Hans / English)#109debugtheworldbot merged 24 commits intomainfrom
debugtheworldbot merged 24 commits intomainfrom
Conversation
Bilingual zh-Hans/en support for KeyStats Windows: auto-detect from system locale at startup with manual override in Settings (restart to apply). Uses .resx + strongly-typed Strings class; reuses macOS translations where overlapping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17 tasks covering resource bootstrap, LocalizationManager, AppSettings field, App startup wiring with mutex retry, Settings Language card with restart flow, and per-window/control/VM/service migration. Each task has explicit code, .resx key tables, and verification gates (build + manual smoke). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…remove 16 orphan keys Task 17 verification cleanup: - Migrate hardcoded "KeyStats is already running." dialog in App.xaml.cs to Strings.Error_AppAlreadyRunning + Strings.App_Name (LocalizationManager is applied earlier in OnStartup, so the prior English-only fallback was no longer necessary). - Remove orphan keys defined in resx but never referenced by code or XAML: Common_Ok, Common_Close, Common_Retry (pre-staged but unused); Stats_PeakKpsLabel, Stats_PeakCpsLabel (superseded by *_TooltipLabel); Calibration_Instruction, Calibration_StartTitle, Calibration_StartMessage, Calibration_FinishTitle, Calibration_FinishMessage, Calibration_SuccessTitle, Calibration_SuccessMessageFormat, Calibration_FailureTitle, Calibration_FailureMessage, Calibration_StatusWaiting, Calibration_StatusMovingFormat (planned for macOS-style dialog flow but Windows UI uses inline status text instead). - Final tally: 158 keys across Strings.resx, Strings.zh-Hans.resx, and Strings.cs; en/zh key sets identical; cs/resx names aligned; zero orphans. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds bilingual UI localization (zh-Hans / English) to the KeyStats Windows app, with startup language detection and a Settings-based manual override (restart-to-apply), using .resx resources and a strongly-typed Strings accessor.
Changes:
- Introduces
.resx-based localization infrastructure (Strings.resx,Strings.zh-Hans.resx,Strings.cs) plusLocalizationManagerand a persistedLanguagePreference. - Migrates multiple WPF windows/controls/view-models/services from hardcoded Chinese strings to
Strings.<Key>lookups, and converts remaining Chinese console logs/comments to English. - Updates startup flow to apply
CurrentUICultureearly and adds a single-instance mutex retry to support “restart after language switch”.
Reviewed changes
Copilot reviewed 31 out of 32 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/superpowers/specs/2026-04-29-windows-i18n-design.md | i18n design spec for Windows implementation |
| docs/superpowers/plans/2026-04-29-windows-i18n.md | Step-by-step implementation plan and verification gates |
| KeyStats.Windows/KeyStats/App.xaml.cs | Apply UI culture at startup; mutex retry; localize tray menu/toasts/dialog titles/errors |
| KeyStats.Windows/KeyStats/Helpers/LocalizationManager.cs | Culture resolution (system/zh-Hans/en) and startup application |
| KeyStats.Windows/KeyStats/Models/AppSettings.cs | Persist languagePreference setting with default "system" |
| KeyStats.Windows/KeyStats/KeyStats.csproj | Explicit manifest resource names for resx embedding/satellite build |
| KeyStats.Windows/KeyStats/Properties/Strings.resx | English (neutral) resource set |
| KeyStats.Windows/KeyStats/Properties/Strings.zh-Hans.resx | Simplified Chinese resource set |
| KeyStats.Windows/KeyStats/Properties/Strings.cs | Hand-written strongly-typed resource accessor |
| KeyStats.Windows/KeyStats/Views/SettingsWindow.xaml | Localize Settings UI and add Language selection card |
| KeyStats.Windows/KeyStats/Views/SettingsWindow.xaml.cs | Handle language selection + restart; localize version and GitHub error |
| KeyStats.Windows/KeyStats/Views/StatsPopupWindow.xaml | Localize stats popup XAML labels/buttons |
| KeyStats.Windows/KeyStats/Views/StatsPopupWindow.xaml.cs | Translate comments (no functional behavior change in shown diff) |
| KeyStats.Windows/KeyStats/Views/NotificationSettingsWindow.xaml | Localize notification settings UI strings |
| KeyStats.Windows/KeyStats/Views/MouseCalibrationWindow.xaml | Localize calibration window XAML |
| KeyStats.Windows/KeyStats/Views/MouseCalibrationWindow.xaml.cs | Localize status/labels and formatting strings |
| KeyStats.Windows/KeyStats/Views/KeyboardHeatmapWindow.xaml | Localize heatmap window XAML |
| KeyStats.Windows/KeyStats/Views/KeyboardHeatmapWindow.xaml.cs | Localize heatmap UI, summary, and date display formatting |
| KeyStats.Windows/KeyStats/Views/KeyHistoryWindow.xaml | Localize key history window UI strings |
| KeyStats.Windows/KeyStats/Views/ImportModeDialog.xaml | Localize import-mode dialog UI strings |
| KeyStats.Windows/KeyStats/Views/ConfirmDialog.xaml | Localize default confirm dialog title/message/buttons |
| KeyStats.Windows/KeyStats/Views/ConfirmDialog.xaml.cs | Localize default confirm dialog fallbacks in Show(...) |
| KeyStats.Windows/KeyStats/Views/AppStatsWindow.xaml | Localize AppStats window UI strings |
| KeyStats.Windows/KeyStats/Views/Controls/KeyBreakdownControl.xaml | Localize empty-state text |
| KeyStats.Windows/KeyStats/Views/Controls/StatsChartControl.xaml.cs | Translate internal comments (no functional behavior change in shown diff) |
| KeyStats.Windows/KeyStats/Views/Controls/KeyDistributionPieChartControl.xaml.cs | Localize pie chart center/empty-state strings |
| KeyStats.Windows/KeyStats/ViewModels/StatsPopupViewModel.cs | Localize history summary string formatting |
| KeyStats.Windows/KeyStats/ViewModels/KeyHistoryViewModel.cs | Localize empty/summary strings |
| KeyStats.Windows/KeyStats/ViewModels/AppStatsViewModel.cs | Localize headers, summary, empty text, and unknown-app label |
| KeyStats.Windows/KeyStats/Services/StatsManager.cs | Localize import exception messages; translate comments |
| KeyStats.Windows/KeyStats/Services/NotificationService.cs | Localize toast title/body for threshold notifications |
| KeyStats.Windows/KeyStats/Services/InputMonitorService.cs | Translate internal comments (no functional behavior change in shown diff) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
SaveSettings() is debounced 2s, so RestartApp launched the new process before the new LanguagePreference hit disk — the relaunched app read the old value and stayed in the previous language. Force a synchronous flush before spawning the new process. Also silence CS8601 with newPref! since the early IsNullOrEmpty return already guarantees non-null. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ead of localized resx keys Agent-Logs-Url: https://github.com/debugtheworldbot/keyStats/sessions/110e43ee-722c-4aca-91d3-294dbf249636 Co-authored-by: debugtheworldbot <62830430+debugtheworldbot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/debugtheworldbot/keyStats/sessions/77b4ab04-6b8e-4d41-9753-b46885d2247a Co-authored-by: debugtheworldbot <62830430+debugtheworldbot@users.noreply.github.com>
- Merge NotifSettings_{EveryPrefix,TimesSuffix} into single
NotifSettings_EveryFormat so translators maintain a coherent sentence
instead of a brittle prefix/suffix pair (word order differs across
languages). XAML now binds two named TextBlocks that the code-behind
fills by splitting the format string around {0}.
- Drop AppStats_SortIndicator resource — the ↓ glyph is identical in both
cultures, so it lives as a const in AppStatsViewModel.
- Track language switch / cancel via TrackClick (matches the project
convention of emitting analytics for new settings cards).
- Log the exception when RestartApp fails to relaunch, so a bug report
can pinpoint why the app didn't come back after a language switch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #108
Summary
zh-CN/zh-Hans*/zh→ 中文; everything else → English) with manual override in Settings (restart to apply)..resx+ hand-written strongly-typedStringsaccessor (no Visual Studio Designer dependency).LocalizationManager.ApplyAtStartupruns first inApp.OnStartupto setThread.CurrentUICulture; mutex check has a 3-attempt 500ms-apart retry to handle the language-switch relaunch race window.App.xaml.cs). All ~358 hardcoded Chinese strings now flow throughStrings.<Key>accessors. Console logs translated to English in the same pass.Changes
New files
Properties/Strings.resx/Strings.zh-Hans.resx— 158 key bilingual resourcesProperties/Strings.cs— hand-written strongly-typed accessor classHelpers/LocalizationManager.cs— culture detection + applicationdocs/superpowers/specs/2026-04-29-windows-i18n-design.md— design specdocs/superpowers/plans/2026-04-29-windows-i18n.md— implementation planKey changes
App.xaml.cs: startup ordering addsLocalizationManager.ApplyAtStartup; mutex retry loop; tray menu / toasts / import-export dialogs all routed through Strings; catch block error message localizedModels/AppSettings.cs: newLanguagePreferencestring field (default"system", backward-compatible with old settings.json)Views/SettingsWindow.xaml+.xaml.cs: new Language card (System / 中文 / English); switching shows a restart confirmation dialog →Process.Start+Application.ShutdownKeyStats.csproj: explicit<ManifestResourceName>overrides ensure.zh-Hans.resxcompiles to a satellite assembly correctlyResource strategy
Strings.resx= English (also serves as fallback; non-Chinese systems auto-resolve here)Strings.zh-Hans.resx= Simplified ChineseTest plan
(
dotnetis unavailable on the macOS host; verify the items below on Windows.)Build / packaging
dotnet build KeyStats/KeyStats.csproj -c Debugsucceedsdotnet build -c Release,bin/Release/net48/zh-Hans/KeyStats.resources.dllsatellite assembly existsbuild.ps1 -Configuration Releaseproduces a zip that includes the satellite folderStartup language detection (clear or set
%LOCALAPPDATA%\KeyStats\settings.jsontolanguagePreference: "system")zh-CNsystem → Chinese UIzh-Hans-CN→ Chinese UIzh-TW/zh-HK→ English UI (strict simplified rule)en-US/ja-JP/de-DE→ English UIManual override
languagePreference: "fr"(invalid) → falls back to auto-detect, no crash"system", runs normallyUI completeness (each window, both languages)
Edge cases
CurrentCulturewas deliberately not modified)🤖 Generated with Claude Code