diff --git a/src/CalcViewModel/Common/LocalizationService.cpp b/src/CalcViewModel/Common/LocalizationService.cpp index df1d85b3..828ae4f0 100644 --- a/src/CalcViewModel/Common/LocalizationService.cpp +++ b/src/CalcViewModel/Common/LocalizationService.cpp @@ -56,9 +56,15 @@ LocalizationService^ LocalizationService::GetInstance() return s_singletonInstance; } -LocalizationService::LocalizationService() +void LocalizationService::OverrideWithLanguage(const wchar_t * const language) { - m_language = ApplicationLanguages::Languages->GetAt(0); + s_singletonInstance = ref new LocalizationService(language); +} + +LocalizationService::LocalizationService(const wchar_t * const overridedLanguage) +{ + m_isLanguageOverrided = overridedLanguage != nullptr; + m_language = m_isLanguageOverrided ? ref new Platform::String(overridedLanguage) : ApplicationLanguages::Languages->GetAt(0); m_flowDirection = ResourceContext::GetForCurrentView()->QualifierValues->Lookup(L"LayoutDirection") != L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight; @@ -340,9 +346,9 @@ void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) // If successful, returns a formatter that respects the user's regional format settings, // as configured by running intl.cpl. -DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter() +DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter() const { - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable^ languageIdentifiers = GetLanguageIdentifiers(); if (languageIdentifiers != nullptr) { return ref new DecimalFormatter(languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion); @@ -355,9 +361,9 @@ DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter( // as configured by running intl.cpl. // // This helper function creates a DateTimeFormatter with a TwentyFour hour clock -DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String^ format) +DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String^ format) const { - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable^ languageIdentifiers = GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) { languageIdentifiers = ApplicationLanguages::Languages; @@ -371,9 +377,9 @@ DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatte DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter( _In_ String^ format, _In_ String^ calendarIdentifier, - _In_ String^ clockIdentifier) + _In_ String^ clockIdentifier) const { - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable^ languageIdentifiers = GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) { languageIdentifiers = ApplicationLanguages::Languages; @@ -387,13 +393,13 @@ DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatte clockIdentifier); } -CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter() +CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter() const { String^ userCurrency = (GlobalizationPreferences::Currencies->Size > 0) ? GlobalizationPreferences::Currencies->GetAt(0) : StringReference(DefaultCurrencyCode.data()); - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable^ languageIdentifiers = GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) { languageIdentifiers = ApplicationLanguages::Languages; @@ -410,10 +416,19 @@ CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatte return currencyFormatter; } -IIterable^ LocalizationService::GetLanguageIdentifiers() +IIterable^ LocalizationService::GetLanguageIdentifiers() const { + WCHAR currentLocale[LOCALE_NAME_MAX_LENGTH] = {}; int result = GetUserDefaultLocaleName(currentLocale, LOCALE_NAME_MAX_LENGTH); + + if (m_isLanguageOverrided) + { + auto overridedLanguageList = ref new Vector(); + overridedLanguageList->Append(m_language); + return overridedLanguageList; + } + if (result != 0) { // GetUserDefaultLocaleName may return an invalid bcp47 language tag with trailing non-BCP47 friendly characters, diff --git a/src/CalcViewModel/Common/LocalizationService.h b/src/CalcViewModel/Common/LocalizationService.h index 7560682d..0c8291d5 100644 --- a/src/CalcViewModel/Common/LocalizationService.h +++ b/src/CalcViewModel/Common/LocalizationService.h @@ -29,6 +29,7 @@ namespace CalculatorApp { namespace Common internal: static LocalizationService^ GetInstance(); + static void OverrideWithLanguage(const wchar_t * const language); Windows::UI::Xaml::FlowDirection GetFlowDirection(); bool IsRtlLayout(); @@ -39,14 +40,14 @@ namespace CalculatorApp { namespace Common Windows::UI::Text::FontWeight GetFontWeightOverride(); double GetFontScaleFactorOverride(LanguageFontType fontType); - static Windows::Globalization::NumberFormatting::DecimalFormatter^ GetRegionalSettingsAwareDecimalFormatter(); - static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String^ format); - static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter( + Windows::Globalization::NumberFormatting::DecimalFormatter^ GetRegionalSettingsAwareDecimalFormatter() const; + Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String^ format) const; + Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter( _In_ Platform::String^ format, _In_ Platform::String^ calendarIdentifier, - _In_ Platform::String^ clockIdentifier); + _In_ Platform::String^ clockIdentifier) const; - static Windows::Globalization::NumberFormatting::CurrencyFormatter^ GetRegionalSettingsAwareCurrencyFormatter(); + Windows::Globalization::NumberFormatting::CurrencyFormatter^ GetRegionalSettingsAwareCurrencyFormatter() const; static Platform::String^ GetNarratorReadableToken(Platform::String^ rawToken); static Platform::String^ GetNarratorReadableString(Platform::String^ rawString); @@ -55,7 +56,7 @@ namespace CalculatorApp { namespace Common Windows::Globalization::Fonts::LanguageFont^ GetLanguageFont(LanguageFontType fontType); Windows::UI::Text::FontWeight ParseFontWeight(Platform::String^ fontWeight); - static Windows::Foundation::Collections::IIterable^ GetLanguageIdentifiers(); + Windows::Foundation::Collections::IIterable^ GetLanguageIdentifiers() const; // Attached property callbacks static void OnFontTypePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, LanguageFontType oldValue, LanguageFontType newValue); @@ -66,8 +67,7 @@ namespace CalculatorApp { namespace Common static std::unordered_map GetTokenToReadableNameMap(); - private: - LocalizationService(); + LocalizationService(const wchar_t * const overridedLanguage = nullptr); static LocalizationService^ s_singletonInstance; @@ -76,6 +76,7 @@ namespace CalculatorApp { namespace Common Windows::UI::Xaml::FlowDirection m_flowDirection; bool m_overrideFontApiValues; Platform::String^ m_fontFamilyOverride; + bool m_isLanguageOverrided; Windows::UI::Text::FontWeight m_fontWeightOverride; double m_uiTextFontScaleFactorOverride; double m_uiCaptionFontScaleFactorOverride; diff --git a/src/CalcViewModel/Common/LocalizationSettings.h b/src/CalcViewModel/Common/LocalizationSettings.h index c1d4dfe2..17386dd5 100644 --- a/src/CalcViewModel/Common/LocalizationSettings.h +++ b/src/CalcViewModel/Common/LocalizationSettings.h @@ -17,7 +17,7 @@ namespace CalculatorApp // Use DecimalFormatter as it respects the locale and the user setting Windows::Globalization::NumberFormatting::DecimalFormatter^ formatter; - formatter = CalculatorApp::Common::LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + formatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDecimalFormatter(); formatter->FractionDigits = 0; formatter->IsDecimalPointAlwaysDisplayed = false; diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp index 4bb4f727..d5b093d4 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp @@ -92,18 +92,28 @@ namespace CalculatorApp } } -CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr client) : +CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr client, const wchar_t * forcedResponseLanguage) : m_client(move(client)), m_loadStatus(CurrencyLoadStatus::NotLoaded), - m_responseLanguage(L"en-US"), m_ratioFormat(L""), m_timestampFormat(L""), m_networkManager(ref new NetworkManager()), m_meteredOverrideSet(false) { - if (GlobalizationPreferences::Languages->Size > 0) + if (forcedResponseLanguage != nullptr) { - m_responseLanguage = GlobalizationPreferences::Languages->GetAt(0); + m_responseLanguage = ref new Platform::String(forcedResponseLanguage); + } + else + { + if (GlobalizationPreferences::Languages->Size > 0) + { + m_responseLanguage = GlobalizationPreferences::Languages->GetAt(0); + } + else + { + m_responseLanguage = L"en-US"; + } } if (m_client != nullptr) @@ -112,13 +122,14 @@ CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr clie m_client->SetResponseLanguage(m_responseLanguage); } + auto localizationService = LocalizationService::GetInstance(); if (CoreWindow::GetForCurrentThread() != nullptr) { // Must have a CoreWindow to access the resource context. - m_isRtlLanguage = LocalizationService::GetInstance()->IsRtlLayout(); + m_isRtlLanguage = localizationService->IsRtlLayout(); } - m_ratioFormatter = LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + m_ratioFormatter = localizationService->GetRegionalSettingsAwareDecimalFormatter(); m_ratioFormatter->IsGrouped = true; m_ratioFormatter->IsDecimalPointAlwaysDisplayed = true; m_ratioFormatter->FractionDigits = FORMATTER_DIGIT_COUNT; diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h index cdb006bb..ffd6c34e 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h @@ -51,7 +51,7 @@ namespace CalculatorApp public UCM::ICurrencyConverterDataLoader { public: - CurrencyDataLoader(_In_ std::unique_ptr client); + CurrencyDataLoader(_In_ std::unique_ptr client, const wchar_t* overrideLanguage = nullptr); ~CurrencyDataLoader(); bool LoadFinished(); diff --git a/src/CalcViewModel/DateCalculatorViewModel.cpp b/src/CalcViewModel/DateCalculatorViewModel.cpp index dde91cc3..3c40690e 100644 --- a/src/CalcViewModel/DateCalculatorViewModel.cpp +++ b/src/CalcViewModel/DateCalculatorViewModel.cpp @@ -233,7 +233,7 @@ void DateCalculatorViewModel::UpdateStrDateResultAutomationName() void DateCalculatorViewModel::InitializeDateOutputFormats(_In_ String^ calendarIdentifier) { // Format for Add/Subtract days - m_dateTimeFormatter = LocalizationService::GetRegionalSettingsAwareDateTimeFormatter( + m_dateTimeFormatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDateTimeFormatter( L"longdate", calendarIdentifier, ClockIdentifiers::TwentyFourHour); // Clock Identifier is not used diff --git a/src/CalcViewModel/UnitConverterViewModel.cpp b/src/CalcViewModel/UnitConverterViewModel.cpp index 393165eb..4cffc109 100644 --- a/src/CalcViewModel/UnitConverterViewModel.cpp +++ b/src/CalcViewModel/UnitConverterViewModel.cpp @@ -130,14 +130,15 @@ UnitConverterViewModel::UnitConverterViewModel(const shared_ptrSetViewModelCallback(make_shared(this)); m_model->SetViewModelCurrencyCallback(make_shared(this)); - m_decimalFormatter = LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + m_decimalFormatter = localizationService->GetRegionalSettingsAwareDecimalFormatter(); m_decimalFormatter->FractionDigits = 0; m_decimalFormatter->IsGrouped = true; m_decimalSeparator = LocalizationSettings::GetInstance().GetDecimalSeparator(); - m_currencyFormatter = LocalizationService::GetRegionalSettingsAwareCurrencyFormatter(); + m_currencyFormatter = localizationService->GetRegionalSettingsAwareCurrencyFormatter(); m_currencyFormatter->IsGrouped = true; m_currencyFormatter->Mode = CurrencyFormatterMode::UseCurrencyCode; m_currencyFormatter->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown); diff --git a/src/Calculator/Views/Calculator.xaml.cpp b/src/Calculator/Views/Calculator.xaml.cpp index 10858a30..8bb31963 100644 --- a/src/Calculator/Views/Calculator.xaml.cpp +++ b/src/Calculator/Views/Calculator.xaml.cpp @@ -108,7 +108,7 @@ void Calculator::SetFontSizeResources() { L"Default", 104, 29.333, 23, 40, 56, 40, 56 } }; - DecimalFormatter^ formatter = LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + DecimalFormatter^ formatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDecimalFormatter(); const FontTable* currentItem = fontTables; while (currentItem->numericSystem.compare(std::wstring(L"Default")) != 0 && diff --git a/src/Calculator/Views/DateCalculator.xaml.cpp b/src/Calculator/Views/DateCalculator.xaml.cpp index 574ad4b5..f7fc084b 100644 --- a/src/Calculator/Views/DateCalculator.xaml.cpp +++ b/src/Calculator/Views/DateCalculator.xaml.cpp @@ -79,7 +79,7 @@ DateCalculator::DateCalculator() DateDiff_ToDate->MaxDate = maxYear; // Set the PlaceHolderText for CalendarDatePicker - DateTimeFormatter^ dateTimeFormatter = LocalizationService::GetRegionalSettingsAwareDateTimeFormatter( + DateTimeFormatter^ dateTimeFormatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDateTimeFormatter( L"day month year", localizationSettings.GetCalendarIdentifier(), ClockIdentifiers::TwentyFourHour); // Clock Identifier is not used diff --git a/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp b/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp index 756e7085..fa24c2e8 100644 --- a/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp +++ b/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp @@ -200,7 +200,7 @@ namespace CalculatorUnitTests { RemoveFromLocalSettings(CurrencyDataLoaderConstants::CacheTimestampKey); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromCacheAsync().get(); @@ -218,7 +218,7 @@ namespace CalculatorUnitTests dayOld.UniversalTime = now.UniversalTime - CurrencyDataLoaderConstants::DayDuration - 1; InsertToLocalSettings(CurrencyDataLoaderConstants::CacheTimestampKey, dayOld); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromCacheAsync().get(); @@ -237,7 +237,7 @@ namespace CalculatorUnitTests VERIFY_IS_TRUE(DeleteFileFromLocalCacheFolder(CurrencyDataLoaderConstants::StaticDataFilename)); VERIFY_IS_TRUE(WriteToFileInLocalCacheFolder(CurrencyDataLoaderConstants::AllRatiosDataFilename, CurrencyHttpClient::GetRawAllRatiosDataResponse())); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromCacheAsync().get(); @@ -256,7 +256,7 @@ namespace CalculatorUnitTests VERIFY_IS_TRUE(WriteToFileInLocalCacheFolder(CurrencyDataLoaderConstants::StaticDataFilename, CurrencyHttpClient::GetRawStaticDataResponse())); VERIFY_IS_TRUE(DeleteFileFromLocalCacheFolder(CurrencyDataLoaderConstants::AllRatiosDataFilename)); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromCacheAsync().get(); @@ -276,7 +276,7 @@ namespace CalculatorUnitTests VERIFY_IS_TRUE(WriteToFileInLocalCacheFolder(CurrencyDataLoaderConstants::StaticDataFilename, CurrencyHttpClient::GetRawStaticDataResponse())); VERIFY_IS_TRUE(DeleteFileFromLocalCacheFolder(CurrencyDataLoaderConstants::AllRatiosDataFilename)); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromCacheAsync().get(); @@ -289,7 +289,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromCacheAsync().get(); @@ -300,7 +300,7 @@ namespace CalculatorUnitTests TEST_METHOD(LoadFromWeb_Fail_ClientIsNullptr) { - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); bool didLoad = loader.TryLoadDataFromWebAsync().get(); @@ -311,7 +311,7 @@ namespace CalculatorUnitTests TEST_METHOD(LoadFromWeb_Fail_WebException) { - CurrencyDataLoader loader{ make_unique() }; + CurrencyDataLoader loader(make_unique(), L"en-US"); bool didLoad = loader.TryLoadDataFromWebAsync().get(); @@ -337,7 +337,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); @@ -388,7 +388,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); @@ -418,7 +418,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); @@ -451,7 +451,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); @@ -480,7 +480,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); @@ -527,7 +527,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); @@ -556,7 +556,7 @@ namespace CalculatorUnitTests { StandardCacheSetup(); - CurrencyDataLoader loader{ nullptr }; + CurrencyDataLoader loader(nullptr, L"en-US"); auto data_loaded_event = task_completion_event(); loader.SetViewModelCallback(make_shared(data_loaded_event)); diff --git a/src/CalculatorUnitTests/UnitTestApp.xaml.cpp b/src/CalculatorUnitTests/UnitTestApp.xaml.cpp index de9d5bb1..995d72b0 100644 --- a/src/CalculatorUnitTests/UnitTestApp.xaml.cpp +++ b/src/CalculatorUnitTests/UnitTestApp.xaml.cpp @@ -8,6 +8,7 @@ #include "pch.h" #include "UnitTestApp.xaml.h" +#include "Common/LocalizationService.h" using namespace CalculatorUnitTests; @@ -81,6 +82,9 @@ void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEvent Window::Current->Activate(); + // Override the current locale to use English (US) to be compatible with all tests based on formatting + CalculatorApp::Common::LocalizationService::OverrideWithLanguage(L"en-US"); + Microsoft::VisualStudio::TestPlatform::TestExecutor::WinRTCore::UnitTestClient::Run(e->Arguments); }