diff --git a/src/CalcViewModel/Common/LocalizationService.cpp b/src/CalcViewModel/Common/LocalizationService.cpp
index 5c28e42d..a331b5d4 100644
--- a/src/CalcViewModel/Common/LocalizationService.cpp
+++ b/src/CalcViewModel/Common/LocalizationService.cpp
@@ -74,8 +74,27 @@ void LocalizationService::OverrideWithLanguage(_In_ const wchar_t* const languag
/// RFC-5646 identifier of the language to use, if null, will use the current language of the system
LocalizationService::LocalizationService(_In_ const wchar_t * const overridedLanguage)
{
+ using namespace Windows::System::UserProfile;
+
m_isLanguageOverrided = overridedLanguage != nullptr;
- m_language = m_isLanguageOverrided ? ref new Platform::String(overridedLanguage) : ApplicationLanguages::Languages->GetAt(0);
+ if (m_isLanguageOverrided)
+ {
+ m_language = ref new Platform::String(overridedLanguage);
+ }
+ else
+ {
+ // Prefer the system Display Language over Regional Settings
+ auto displayLanguages = GlobalizationPreferences::Languages;
+ if (displayLanguages != nullptr && displayLanguages->Size > 0)
+ {
+ m_language = ref new Platform::String(displayLanguages->GetAt(0)->Data());
+ }
+ else
+ {
+ // Fallback to the default application language list
+ m_language = ApplicationLanguages::Languages->GetAt(0);
+ }
+ }
m_flowDirection = ResourceContext::GetForViewIndependentUse()->QualifierValues->Lookup(L"LayoutDirection")
!= L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight;
wstring localeName = wstring(m_language->Data());
diff --git a/src/Calculator/App.xaml.cs b/src/Calculator/App.xaml.cs
index 13fe4a6f..1c5bc286 100644
--- a/src/Calculator/App.xaml.cs
+++ b/src/Calculator/App.xaml.cs
@@ -35,6 +35,20 @@ namespace CalculatorApp
///
public App()
{
+ // Ensure we choose the UI language based on System Display Language
+ // before any resources are loaded or singletons are created.
+ // If user selected a forced language, apply it; otherwise use Display Language
+ var selected = LanguageHelper.SelectedLanguage;
+ if (!string.IsNullOrEmpty(selected) && !string.Equals(selected, "system", StringComparison.OrdinalIgnoreCase))
+ {
+ LanguageHelper.ApplyUserLanguageSelection(selected);
+ }
+ else
+ {
+ LanguageHelper.InitializeDisplayLanguage();
+ }
+ LanguageHelper.LogLanguageDebugInfo(); // Debug
+
InitializeComponent();
NarratorNotifier.RegisterDependencyProperties();
@@ -149,6 +163,18 @@ namespace CalculatorApp
// Place the frame in the current Window
Window.Current.Content = rootFrame;
ThemeHelper.InitializeAppTheme();
+
+ // Initialize Calculator to use System Display Language instead of Regional Settings
+ // Re-apply language at view creation based on persisted selection
+ var selected = LanguageHelper.SelectedLanguage;
+ var effective = selected == "system" ? LanguageHelper.GetSystemDisplayLanguage() : selected;
+ if (!string.IsNullOrEmpty(effective))
+ {
+ LanguageHelper.ApplyViewResourceLanguages(effective);
+ LanguageHelper.ApplyGlobalResourceLanguages(effective);
+ }
+ LanguageHelper.LogLanguageDebugInfo(); // Debug
+
Window.Current.Activate();
}
diff --git a/src/Calculator/Utils/LanguageHelper.cs b/src/Calculator/Utils/LanguageHelper.cs
new file mode 100644
index 00000000..a6483775
--- /dev/null
+++ b/src/Calculator/Utils/LanguageHelper.cs
@@ -0,0 +1,256 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Windows.Storage;
+
+namespace CalculatorApp.Utils
+{
+ ///
+ /// Helper class for managing application language settings
+ ///
+ public static class LanguageHelper
+ {
+ private const string SelectedLanguageKey = "SelectedLanguage"; // "system" or BCP-47 tag
+
+ ///
+ /// Initialize the application to use the System Display Language instead of Regional Settings
+ ///
+ public static void InitializeDisplayLanguage()
+ {
+ try
+ {
+ var displayLanguage = GetSystemDisplayLanguage();
+ if (!string.IsNullOrEmpty(displayLanguage))
+ {
+ Debug.WriteLine($"Setting Calculator to use Display Language: {displayLanguage}");
+ SetApplicationLanguage(displayLanguage);
+ }
+ else
+ {
+ Debug.WriteLine("Could not detect system display language, using default behavior");
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error initializing display language: {ex.Message}");
+ // Don't throw - let the app continue with default behavior
+ }
+ }
+
+ ///
+ /// Persisted selected language code. "system" means follow system (Display Language)
+ ///
+ public static string SelectedLanguage
+ {
+ get => ApplicationData.Current.LocalSettings.Values[SelectedLanguageKey]?.ToString() ?? "system";
+ set => ApplicationData.Current.LocalSettings.Values[SelectedLanguageKey] = value;
+ }
+
+ ///
+ /// Return available UI languages for selection (System Default + supported app languages)
+ ///
+ public static List GetAvailableLanguages()
+ {
+ var list = new List();
+ list.Add(new LanguageInfo { Code = "system", NativeName = "System Default" });
+
+ foreach (var code in GetSupportedLanguageCodes())
+ {
+ try
+ {
+ var lang = new Windows.Globalization.Language(code);
+ list.Add(new LanguageInfo { Code = code, NativeName = lang.NativeName });
+ }
+ catch
+ {
+ list.Add(new LanguageInfo { Code = code, NativeName = code });
+ }
+ }
+
+ return list;
+ }
+
+ private static IEnumerable GetSupportedLanguageCodes()
+ {
+ // Full list based on src/Calculator/Resources/* folders
+ return new[]
+ {
+ "af-ZA","am-ET","ar-SA","az-Latn-AZ","bg-BG","ca-ES","cs-CZ","da-DK","de-DE","el-GR",
+ "en-GB","en-US","es-ES","es-MX","et-EE","eu-ES","fa-IR","fi-FI","fil-PH","fr-CA","fr-FR",
+ "gl-ES","he-IL","hi-IN","hr-HR","hu-HU","id-ID","is-IS","it-IT","ja-JP","kk-KZ","km-KH",
+ "kn-IN","ko-KR","lo-LA","lt-LT","lv-LV","mk-MK","ml-IN","ms-MY","nb-NO","nl-NL","pl-PL",
+ "pt-BR","pt-PT","ro-RO","ru-RU","sk-SK","sl-SI","sq-AL","sr-Latn-RS","sv-SE","ta-IN",
+ "te-IN","th-TH","tr-TR","uk-UA","vi-VN","zh-CN","zh-TW"
+ };
+ }
+
+ ///
+ /// Apply a user-selected language code (or "system") immediately and persist preference
+ ///
+ public static void ApplyUserLanguageSelection(string code)
+ {
+ SelectedLanguage = code;
+
+ string languageToUse = code == "system" ? GetSystemDisplayLanguage() : code;
+ if (string.IsNullOrEmpty(languageToUse))
+ {
+ Debug.WriteLine("No language resolved; skipping override");
+ return;
+ }
+
+ SetApplicationLanguage(languageToUse);
+ ApplyViewResourceLanguages(languageToUse);
+ ApplyGlobalResourceLanguages(languageToUse);
+ }
+
+ ///
+ /// Gets the system's Display Language (not Regional Settings)
+ ///
+ /// The primary display language code (e.g., "en-US", "ja-JP")
+ public static string GetSystemDisplayLanguage()
+ {
+ try
+ {
+ // Primary method: Get the user's preferred UI languages from GlobalizationPreferences
+ // This returns the Display Language, not Regional Settings
+ var userLanguages = Windows.System.UserProfile.GlobalizationPreferences.Languages;
+ if (userLanguages.Count > 0)
+ {
+ var primaryLanguage = userLanguages[0];
+ Debug.WriteLine($"Display Language from GlobalizationPreferences: {primaryLanguage}");
+ return primaryLanguage;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error getting GlobalizationPreferences: {ex.Message}");
+
+ // Fallback method: Try ResourceContext
+ try
+ {
+ var resourceContext = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
+ if (resourceContext.Languages.Count > 0)
+ {
+ var primaryLanguage = resourceContext.Languages[0];
+ Debug.WriteLine($"Display Language from ResourceContext: {primaryLanguage}");
+ return primaryLanguage;
+ }
+ }
+ catch (Exception ex2)
+ {
+ Debug.WriteLine($"Error getting ResourceContext languages: {ex2.Message}");
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Sets the application language using ApplicationLanguages.PrimaryLanguageOverride
+ ///
+ /// Language code (e.g., "en-US", "ja-JP")
+ public static void SetApplicationLanguage(string languageCode)
+ {
+ try
+ {
+ // Set the primary language override for this application
+ // This overrides the default language resolution and forces the app to use the specified language
+ Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = languageCode;
+ Debug.WriteLine($"ApplicationLanguages.PrimaryLanguageOverride set to: {languageCode}");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error setting application language to {languageCode}: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Apply the specified language to ResourceContext used outside of any view
+ ///
+ public static void ApplyGlobalResourceLanguages(string languageCode)
+ {
+ try
+ {
+ // Set process-wide qualifier for language
+ Windows.ApplicationModel.Resources.Core.ResourceContext.SetGlobalQualifierValue("Language", languageCode);
+ Debug.WriteLine($"Global qualifier 'Language' set to: {languageCode}");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error applying global resource language: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Apply the specified language to ResourceContext for the current view
+ ///
+ public static void ApplyViewResourceLanguages(string languageCode)
+ {
+ try
+ {
+ var ctx = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
+ // Set qualifier value for this view
+ ctx.QualifierValues["Language"] = languageCode;
+ Debug.WriteLine($"View qualifier 'Language' set to: {languageCode}");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error applying view resource language: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Clears the application language override (for debugging/testing)
+ ///
+ public static void ClearApplicationLanguageOverride()
+ {
+ try
+ {
+ Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "";
+ Debug.WriteLine("ApplicationLanguages.PrimaryLanguageOverride cleared");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error clearing application language override: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Gets debug information about current language settings
+ ///
+ public static void LogLanguageDebugInfo()
+ {
+ try
+ {
+ var currentOverride = Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride;
+ var systemLanguages = Windows.System.UserProfile.GlobalizationPreferences.Languages;
+ var appLanguages = Windows.Globalization.ApplicationLanguages.Languages;
+
+ Debug.WriteLine("=== Language Debug Info ===");
+ Debug.WriteLine($"PrimaryLanguageOverride: '{currentOverride}'");
+ Debug.WriteLine($"System Display Languages: {string.Join(", ", systemLanguages)}");
+ Debug.WriteLine($"Application Languages: {string.Join(", ", appLanguages)}");
+ Debug.WriteLine("===========================");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error getting language debug info: {ex.Message}");
+ }
+ }
+ }
+
+ public class LanguageInfo
+ {
+ public string Code { get; set; }
+ public string NativeName { get; set; }
+
+ public override string ToString()
+ {
+ return string.IsNullOrEmpty(NativeName) ? Code : NativeName;
+ }
+ }
+}
diff --git a/src/Calculator/Views/Settings.xaml b/src/Calculator/Views/Settings.xaml
index dbd1fc01..3c2ab130 100644
--- a/src/Calculator/Views/Settings.xaml
+++ b/src/Calculator/Views/Settings.xaml
@@ -109,6 +109,21 @@
+
+
+
+
+
+
+
+
+
+
+
().FirstOrDefault(c => c?.Tag?.ToString() == currentTheme)).IsChecked = true;
+ // Initialize language selection to current preference
+ try
+ {
+ var items = LanguageComboBox.ItemsSource as List;
+ var selected = CalculatorApp.Utils.LanguageHelper.SelectedLanguage;
+ if (items != null)
+ {
+ foreach (var it in items)
+ {
+ if (it.Code == selected)
+ {
+ _isSettingLanguageProgrammatically = true;
+ LanguageComboBox.SelectedItem = it;
+ _isSettingLanguageProgrammatically = false;
+ break;
+ }
+ }
+ }
+ }
+ catch { _isSettingLanguageProgrammatically = false; }
+
SetDefaultFocus();
}
@@ -139,6 +164,48 @@ namespace CalculatorApp
ContributeRunAfterLink.Text = contributeTextAfterHyperlink;
}
+ private void InitializeLanguageSettings()
+ {
+ try
+ {
+ var list = CalculatorApp.Utils.LanguageHelper.GetAvailableLanguages();
+ LanguageComboBox.ItemsSource = list;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Language init error: {ex.Message}");
+ }
+ }
+
+ private void OnLanguageSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (_isSettingLanguageProgrammatically)
+ {
+ return;
+ }
+
+ if (e.AddedItems.Count == 0)
+ return;
+
+ if (e.AddedItems[0] is CalculatorApp.Utils.LanguageInfo lang)
+ {
+ var current = CalculatorApp.Utils.LanguageHelper.SelectedLanguage;
+ if (string.Equals(current, lang.Code, StringComparison.OrdinalIgnoreCase))
+ {
+ return; // no actual change
+ }
+
+ CalculatorApp.Utils.LanguageHelper.ApplyUserLanguageSelection(lang.Code);
+
+ // Inform user that full app restart may be required to update all resources
+ var title = AppResourceProvider.GetInstance().GetResourceString("LanguageChangeDialog/Title");
+ var message = AppResourceProvider.GetInstance().GetResourceString("LanguageChangeDialog/Message");
+ if (string.IsNullOrEmpty(title)) title = "Language Changed";
+ if (string.IsNullOrEmpty(message)) message = "Some UI may update after restart.";
+ _ = new ContentDialog { Title = title, Content = message, PrimaryButtonText = "OK" }.ShowAsync();
+ }
+ }
+
private void System_BackRequested(object sender, BackRequestedEventArgs e)
{
if (!e.Handled && BackButton.IsEnabled)