Use the current locale to sort country names (#259)
This commit is contained in:
		@@ -75,9 +75,24 @@ LocalizationService::LocalizationService(_In_ const wchar_t * const overridedLan
 | 
			
		||||
{
 | 
			
		||||
    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")
 | 
			
		||||
    m_flowDirection = ResourceContext::GetForViewIndependentUse()->QualifierValues->Lookup(L"LayoutDirection")
 | 
			
		||||
        != L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight;
 | 
			
		||||
    wstring localeName = wstring(m_language->Data());
 | 
			
		||||
    localeName += L".UTF8";
 | 
			
		||||
 | 
			
		||||
    try
 | 
			
		||||
    {
 | 
			
		||||
        // Convert wstring to string for locale
 | 
			
		||||
        int size_needed = WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), NULL, 0, NULL, NULL);
 | 
			
		||||
        string localeNameStr(size_needed, 0);
 | 
			
		||||
        WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), &localeNameStr[0], size_needed, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
        m_locale = locale(localeNameStr.data());
 | 
			
		||||
    }
 | 
			
		||||
    catch (...)
 | 
			
		||||
    {
 | 
			
		||||
        m_locale = locale("");
 | 
			
		||||
    }
 | 
			
		||||
    auto resourceLoader = AppResourceProvider::GetInstance();
 | 
			
		||||
    m_fontFamilyOverride = resourceLoader.GetResourceString(L"LocalizedFontFamilyOverride");
 | 
			
		||||
 | 
			
		||||
@@ -371,7 +386,7 @@ 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) const
 | 
			
		||||
DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format) const
 | 
			
		||||
{
 | 
			
		||||
    IIterable<String ^> ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
 | 
			
		||||
    if (languageIdentifiers == nullptr)
 | 
			
		||||
@@ -384,7 +399,7 @@ DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatt
 | 
			
		||||
 | 
			
		||||
// If successful, returns a formatter that respects the user's regional format settings,
 | 
			
		||||
// as configured by running intl.cpl.
 | 
			
		||||
DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format, _In_ String ^ calendarIdentifier, _In_ String ^ clockIdentifier) const
 | 
			
		||||
DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format, _In_ String ^ calendarIdentifier, _In_ String ^ clockIdentifier) const
 | 
			
		||||
{
 | 
			
		||||
    IIterable<String ^> ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
 | 
			
		||||
    if (languageIdentifiers == nullptr)
 | 
			
		||||
@@ -421,7 +436,7 @@ IIterable<String ^> ^ LocalizationService::GetLanguageIdentifiers() const
 | 
			
		||||
 | 
			
		||||
    if (m_isLanguageOverrided)
 | 
			
		||||
    {
 | 
			
		||||
        auto overridedLanguageList = ref new Vector<String^>();
 | 
			
		||||
        auto overridedLanguageList = ref new Vector<String ^>();
 | 
			
		||||
        overridedLanguageList->Append(m_language);
 | 
			
		||||
        return overridedLanguageList;
 | 
			
		||||
    }
 | 
			
		||||
@@ -562,3 +577,11 @@ String ^ LocalizationService::GetNarratorReadableString(String ^ rawString)
 | 
			
		||||
 | 
			
		||||
    return ref new String(readableString.str().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LocalizationService::Sort(std::vector<Platform::String ^>& source)
 | 
			
		||||
{
 | 
			
		||||
    const collate<wchar_t>& coll = use_facet<collate<wchar_t>>(m_locale);
 | 
			
		||||
    sort(source.begin(), source.end(), [&coll](Platform::String ^ str1, Platform::String ^ str2) {
 | 
			
		||||
        return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,8 +31,8 @@ namespace CalculatorApp
 | 
			
		||||
            DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(double, FontSize);
 | 
			
		||||
 | 
			
		||||
        internal:
 | 
			
		||||
        static LocalizationService^ GetInstance();
 | 
			
		||||
        static void OverrideWithLanguage(_In_ const wchar_t * const language);
 | 
			
		||||
            static LocalizationService ^ GetInstance();
 | 
			
		||||
            static void OverrideWithLanguage(_In_ const wchar_t* const language);
 | 
			
		||||
 | 
			
		||||
            Windows::UI::Xaml::FlowDirection GetFlowDirection();
 | 
			
		||||
            bool IsRtlLayout();
 | 
			
		||||
@@ -43,6 +43,19 @@ namespace CalculatorApp
 | 
			
		||||
            Windows::UI::Text::FontWeight GetFontWeightOverride();
 | 
			
		||||
            double GetFontScaleFactorOverride(LanguageFontType fontType);
 | 
			
		||||
 | 
			
		||||
            void Sort(std::vector<Platform::String ^>& source);
 | 
			
		||||
 | 
			
		||||
            template <typename T>
 | 
			
		||||
            void Sort(std::vector<T>& source, std::function<Platform::String ^ (T)> func)
 | 
			
		||||
            {
 | 
			
		||||
                const collate<wchar_t>& coll = use_facet<collate<wchar_t>>(m_locale);
 | 
			
		||||
                sort(source.begin(), source.end(), [&coll, &func](T obj1, T obj2) {
 | 
			
		||||
                    Platform::String ^ str1 = func(obj1);
 | 
			
		||||
                    Platform::String ^ str2 = func(obj2);
 | 
			
		||||
                    return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Windows::Globalization::NumberFormatting::DecimalFormatter ^ GetRegionalSettingsAwareDecimalFormatter() const;
 | 
			
		||||
            Windows::Globalization::DateTimeFormatting::DateTimeFormatter ^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String ^ format) const;
 | 
			
		||||
            Windows::Globalization::DateTimeFormatting::DateTimeFormatter ^ GetRegionalSettingsAwareDateTimeFormatter(
 | 
			
		||||
@@ -85,6 +98,7 @@ namespace CalculatorApp
 | 
			
		||||
            Windows::UI::Text::FontWeight m_fontWeightOverride;
 | 
			
		||||
            double m_uiTextFontScaleFactorOverride;
 | 
			
		||||
            double m_uiCaptionFontScaleFactorOverride;
 | 
			
		||||
            std::locale m_locale;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
// Copyright (c) Microsoft Corporation. All rights reserved.
 | 
			
		||||
// Copyright (c) Microsoft Corporation. All rights reserved.
 | 
			
		||||
// Licensed under the MIT License.
 | 
			
		||||
 | 
			
		||||
#include "pch.h"
 | 
			
		||||
@@ -527,8 +527,11 @@ bool CurrencyDataLoader::TryParseStaticData(_In_ String ^ rawJson, _Inout_ vecto
 | 
			
		||||
        staticData[i] = CurrencyStaticData{ countryCode, countryName, currencyCode, currencyName, currencySymbol };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO - MSFT 8533667: this sort will be replaced by a WinRT call to sort localized strings
 | 
			
		||||
    sort(begin(staticData), end(staticData), [](CurrencyStaticData unit1, CurrencyStaticData unit2) { return unit1.countryName < unit2.countryName; });
 | 
			
		||||
    auto sortCountryNames = [](const UCM::CurrencyStaticData & s) {
 | 
			
		||||
        return ref new String(s.countryName.c_str());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    LocalizationService::GetInstance()->Sort<UCM::CurrencyStaticData>(staticData, sortCountryNames);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -244,6 +244,7 @@
 | 
			
		||||
    <ClCompile Include="CurrencyConverterUnitTests.cpp" />
 | 
			
		||||
    <ClCompile Include="DateCalculatorUnitTests.cpp" />
 | 
			
		||||
    <ClCompile Include="HistoryTests.cpp" />
 | 
			
		||||
    <ClCompile Include="LocalizationServiceUnitTests.cpp" />
 | 
			
		||||
    <ClCompile Include="Mocks\CurrencyHttpClient.cpp" />
 | 
			
		||||
    <ClCompile Include="Module.cpp" />
 | 
			
		||||
    <ClCompile Include="MultiWindowUnitTests.cpp" />
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
    <ClCompile Include="Mocks\CurrencyHttpClient.cpp">
 | 
			
		||||
      <Filter>Mocks</Filter>
 | 
			
		||||
    </ClCompile>
 | 
			
		||||
    <ClCompile Include="LocalizationServiceUnitTests.cpp" />
 | 
			
		||||
    <ClCompile Include="RationalTest.cpp" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								src/CalculatorUnitTests/LocalizationServiceUnitTests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/CalculatorUnitTests/LocalizationServiceUnitTests.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
// Copyright (c) Microsoft Corporation. All rights reserved.
 | 
			
		||||
// Licensed under the MIT License.
 | 
			
		||||
 | 
			
		||||
#include "pch.h"
 | 
			
		||||
 | 
			
		||||
#include <CppUnitTest.h>
 | 
			
		||||
 | 
			
		||||
#include "CalcViewModel/Common/LocalizationService.h"
 | 
			
		||||
 | 
			
		||||
using namespace CalculatorApp::Common;
 | 
			
		||||
using namespace Platform;
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
 | 
			
		||||
 | 
			
		||||
namespace CalculatorUnitTests
 | 
			
		||||
{
 | 
			
		||||
    TEST_CLASS(LocalizationServiceUnitTests)
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
 | 
			
		||||
        TEST_METHOD(TestSortStrings)
 | 
			
		||||
        {
 | 
			
		||||
            auto localizationService = LocalizationService::GetInstance();
 | 
			
		||||
            vector<String^> stringsToSort =
 | 
			
		||||
            {
 | 
			
		||||
                L"Zebra",
 | 
			
		||||
                L"Alpha",
 | 
			
		||||
                L"beta1",
 | 
			
		||||
                L"Gamma",
 | 
			
		||||
                L"Beta",
 | 
			
		||||
                L"alpha1",
 | 
			
		||||
                L"États-Unis",
 | 
			
		||||
                L"Epsilon",
 | 
			
		||||
                L"Etude",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            vector<String^> expectedResult =
 | 
			
		||||
            {
 | 
			
		||||
                L"Alpha",
 | 
			
		||||
                L"alpha1",
 | 
			
		||||
                L"Beta",
 | 
			
		||||
                L"beta1",
 | 
			
		||||
                L"Epsilon",
 | 
			
		||||
                L"États-Unis",
 | 
			
		||||
                L"Etude",
 | 
			
		||||
                L"Gamma",
 | 
			
		||||
                L"Zebra",
 | 
			
		||||
            };
 | 
			
		||||
            VERIFY_ARE_EQUAL(stringsToSort.size(), expectedResult.size());
 | 
			
		||||
            VERIFY_IS_FALSE(equal(stringsToSort.begin(), stringsToSort.end(), expectedResult.begin()));
 | 
			
		||||
            localizationService->Sort(stringsToSort);
 | 
			
		||||
            VERIFY_IS_TRUE(equal(stringsToSort.begin(), stringsToSort.end(), expectedResult.begin()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TEST_METHOD(TestSortEmptyStrings)
 | 
			
		||||
        {
 | 
			
		||||
            //Verify if LocalizationService::Sort doesn't crash when the vector is empty or null
 | 
			
		||||
            auto localizationService = LocalizationService::GetInstance();
 | 
			
		||||
            vector<String^> stringsToSort = {};
 | 
			
		||||
            localizationService->Sort(stringsToSort);
 | 
			
		||||
            stringsToSort = { L"" };
 | 
			
		||||
            localizationService->Sort(stringsToSort);
 | 
			
		||||
            stringsToSort = { L"",L"",L"" };
 | 
			
		||||
            localizationService->Sort(stringsToSort);
 | 
			
		||||
            stringsToSort = { nullptr,L"",L"" };
 | 
			
		||||
            localizationService->Sort(stringsToSort);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        TEST_METHOD(TestSortGeneric)
 | 
			
		||||
        {
 | 
			
		||||
            vector<String^> stringsToSort =
 | 
			
		||||
            {
 | 
			
		||||
                L"fermentum",
 | 
			
		||||
                L"fringilla",
 | 
			
		||||
                L"Curabitur",
 | 
			
		||||
                L"rhoncus",
 | 
			
		||||
                L"Aenean",
 | 
			
		||||
                L"Fusce",
 | 
			
		||||
                L"sollicitudin",
 | 
			
		||||
                L"empor",
 | 
			
		||||
                L"edapibus",
 | 
			
		||||
                L"édapibas",
 | 
			
		||||
                L"édapîbos",
 | 
			
		||||
                L"édapîbÉs",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            vector<String^> expectedResult =
 | 
			
		||||
            {
 | 
			
		||||
                L"Aenean",
 | 
			
		||||
                L"Curabitur",
 | 
			
		||||
                L"édapibas",
 | 
			
		||||
                L"édapîbÉs",
 | 
			
		||||
                L"édapîbos",
 | 
			
		||||
                L"edapibus",
 | 
			
		||||
                L"empor",
 | 
			
		||||
                L"fermentum",
 | 
			
		||||
                L"fringilla",
 | 
			
		||||
                L"Fusce",
 | 
			
		||||
                L"rhoncus",
 | 
			
		||||
                L"sollicitudin",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            auto sortFunction = [](String^ s) {
 | 
			
		||||
                return ref new String(L"CAL:") + s + L"TEST";
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            VERIFY_ARE_EQUAL(stringsToSort.size(), expectedResult.size());
 | 
			
		||||
            VERIFY_IS_FALSE(equal(stringsToSort.begin(), stringsToSort.end(), expectedResult.begin()));
 | 
			
		||||
 | 
			
		||||
            auto localizationService = LocalizationService::GetInstance();
 | 
			
		||||
            localizationService->Sort<String^>(stringsToSort, sortFunction);
 | 
			
		||||
            VERIFY_IS_TRUE(equal(stringsToSort.begin(), stringsToSort.end(), expectedResult.begin()));
 | 
			
		||||
 | 
			
		||||
            vector<String^> expected2Result =
 | 
			
		||||
            {
 | 
			
		||||
                L"édapibas",
 | 
			
		||||
                L"édapîbÉs",
 | 
			
		||||
                L"édapîbos",
 | 
			
		||||
                L"edapibus",
 | 
			
		||||
                L"Aenean",
 | 
			
		||||
                L"fermentum",
 | 
			
		||||
                L"rhoncus",
 | 
			
		||||
                L"empor",
 | 
			
		||||
                L"sollicitudin",
 | 
			
		||||
                L"fringilla",
 | 
			
		||||
                L"Curabitur",
 | 
			
		||||
                L"Fusce",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            auto sort2Function = [](String^ s) {
 | 
			
		||||
                return ref new String(s->Begin()+1);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            VERIFY_ARE_EQUAL(stringsToSort.size(), expected2Result.size());
 | 
			
		||||
            VERIFY_IS_FALSE(equal(stringsToSort.begin(), stringsToSort.end(), expected2Result.begin()));
 | 
			
		||||
 | 
			
		||||
            localizationService->Sort<String^>(stringsToSort, sort2Function);
 | 
			
		||||
            VERIFY_IS_TRUE(equal(stringsToSort.begin(), stringsToSort.end(), expected2Result.begin()));
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user