feat(i18n): localize installer with language picker and config write-through

- Init Localizer at app startup (before self-update prompt) and assign to TrExtension.Localizer
- Register ILocalizer in DI; inject into WizardViewModel and SettingsViewModel
- WizardViewModel: SelectedLanguage ComboBox binding with OnSelectedLanguageChanged -> SetLanguage + InstallContext.Language
- WizardWindow.xaml: DockPanel wraps step chips + language ComboBox (right-aligned)
- Localize all installer XAML: WizardWindow, WelcomePage, PathsPage, ServicePage, UiSettingsPage, InstallPage, SettingsWindow, SelfUpdatePromptWindow
- Localize page Title properties and WizardViewModel.NextButtonText via TrExtension.Localizer
- Persist chosen Language in WriteConfigStep and SettingsViewModel.Save into ui.config.json
- Append installer section to en.json (nav, welcome, paths, service, uiSettings, install, settings, selfUpdate)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-03 12:55:08 +02:00
co-authored by Claude Sonnet 4.6
parent 2fbf054a57
commit 364a037cb3
19 changed files with 210 additions and 83 deletions
+19 -2
View File
@@ -1,7 +1,12 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Windows;
using ClaudeDo.Installer.Core;
using ClaudeDo.Installer.Localization;
using ClaudeDo.Localization;
using ClaudeDo.Releases;
using ClaudeDo.Installer.Pages.InstallPage;
using ClaudeDo.Installer.Pages.PathsPage;
@@ -22,6 +27,17 @@ public partial class App : Application
{
base.OnStartup(e);
// --- Initialize localizer as early as possible so all windows can use {loc:Tr} ---
var localesDir = Path.Combine(AppContext.BaseDirectory, "locales");
var localeStore = LocaleStore.Load(localesDir);
var existingSettings = InstallerAppSettings.Load();
var initialLang = !string.IsNullOrWhiteSpace(existingSettings.Language)
? existingSettings.Language
: CultureResolver.Resolve(CultureInfo.CurrentUICulture.Name,
localeStore.Available.Select(l => l.Code).ToArray(), "en");
var localizer = new Localizer(localeStore, initialLang);
TrExtension.Localizer = localizer;
// --- Self-update pre-flight ---
// Resolve current exe path. Assembly.Location may point to a .dll for apphost-based
// .NET apps; swap to the .exe companion when that happens.
@@ -120,7 +136,7 @@ public partial class App : Application
// --- Existing wizard start-up unchanged below this line ---
_services = BuildServices();
_services = BuildServices(localizer);
var context = _services.GetRequiredService<InstallContext>();
context.InstallerVersion = GetInstallerVersion();
@@ -183,9 +199,10 @@ public partial class App : Application
return infoAttr?.InformationalVersion ?? "0.0.0";
}
private static ServiceProvider BuildServices()
private static ServiceProvider BuildServices(ILocalizer localizer)
{
var sc = new ServiceCollection();
sc.AddSingleton(localizer);
// Core
sc.AddSingleton<InstallContext>();