Locale.cpp (5050B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "winget/Locale.h" 5 #include "AppInstallerStrings.h" 6 #include "AppInstallerLogging.h" 7 8 namespace AppInstaller::Locale 9 { 10 namespace 11 { 12 constexpr int MAX_LOCALE_SNAME_LEN = 85; 13 14 // We will just leak this. The module is shared as both functions will always be together. 15 HMODULE g_bcp47 = (HMODULE)(-1); 16 typedef bool(WINAPI* IsWellFormedTagFunc)(PCWSTR); 17 typedef HRESULT(WINAPI* GetDistanceOfClosestLanguageInListFunc)(PCWSTR, PCWSTR, wchar_t, double*); 18 19 HMODULE LoadBcp47ModuleFrom(_In_ PCWSTR moduleName) 20 { 21 HMODULE module = LoadLibraryExW(moduleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); 22 if (module != nullptr) 23 { 24 // All BCP47 APIs we are interested are always exported from the same dll together. So we just pick anyone for probe. 25 IsWellFormedTagFunc func = (IsWellFormedTagFunc)(GetProcAddress(module, "IsWellFormedTag")); 26 if (func != nullptr) 27 { 28 return module; 29 } 30 FreeLibrary(module); 31 } 32 33 return nullptr; 34 } 35 36 HMODULE LoadBcp47Module() 37 { 38 HMODULE module = LoadBcp47ModuleFrom(L"bcp47mrm.dll"); 39 if (module == nullptr) 40 { 41 // In downlevel OS, the API is exposed by bcp47langs.dll. 42 module = LoadBcp47ModuleFrom(L"bcp47langs.dll"); 43 } 44 45 return module; 46 } 47 48 void InitializeBcp47Module() 49 { 50 HMODULE comparand = (HMODULE)(-1); 51 if (InterlockedCompareExchangePointer(reinterpret_cast<PVOID*>(&g_bcp47), comparand, comparand) == comparand) 52 { 53 HMODULE module = LoadBcp47Module(); 54 InterlockedExchangePointer(reinterpret_cast<PVOID*>(&g_bcp47), module); 55 } 56 } 57 } 58 59 bool IsWellFormedBcp47Tag(std::string_view bcp47Tag) 60 { 61 // Before new SDK is released, we need to use LoadLibrary/GetProcAddress 62 InitializeBcp47Module(); 63 64 if (g_bcp47 == nullptr) 65 { 66 // Didn't find an implementation. Just return true. 67 AICLI_LOG(Core, Warning, << "bcp47 module not found."); 68 return true; 69 } 70 71 IsWellFormedTagFunc func = (IsWellFormedTagFunc)(GetProcAddress(g_bcp47, "IsWellFormedTag")); 72 if (func != nullptr) 73 { 74 auto wBcp47Tag = Utility::ConvertToUTF16(bcp47Tag); 75 return func(wBcp47Tag.c_str()); 76 } 77 78 // Should not reach here. 79 return TRUE; 80 } 81 82 double GetDistanceOfLanguage(std::string_view target, std::string_view available) 83 { 84 // Before new SDK is released, we need to use LoadLibrary/GetProcAddress 85 InitializeBcp47Module(); 86 87 if (g_bcp47 == nullptr) 88 { 89 // Didn't find an implementation. Just return 0 as no match. 90 AICLI_LOG(Core, Warning, << "bcp47 module not found."); 91 return 0; 92 } 93 94 GetDistanceOfClosestLanguageInListFunc func = 95 (GetDistanceOfClosestLanguageInListFunc)(GetProcAddress(g_bcp47, "GetDistanceOfClosestLanguageInList")); 96 if (func != nullptr) 97 { 98 double distance = 0; 99 auto wTarget = Utility::ConvertToUTF16(target); 100 auto wAvailable = Utility::ConvertToUTF16(available); 101 102 // Do not check HRESULT because the method returns ERROR_NO_MATCH on no match, which is a valid case. 103 (void)func(wTarget.c_str(), wAvailable.c_str(), L';' /* Not used, we compare one at a time */, &distance); 104 return distance; 105 } 106 107 // Should not reach here. 108 return 0; 109 } 110 111 std::vector<std::string> GetUserPreferredLanguages() 112 { 113 std::vector<std::string> result; 114 115 for (const auto& lang : winrt::Windows::System::UserProfile::GlobalizationPreferences::Languages()) 116 { 117 result.emplace_back(Utility::ConvertToUTF8(lang)); 118 } 119 120 return result; 121 } 122 123 std::vector<std::wstring> GetUserPreferredLanguagesUTF16() 124 { 125 std::vector<std::wstring> result; 126 127 for (const auto& lang : winrt::Windows::System::UserProfile::GlobalizationPreferences::Languages()) 128 { 129 result.emplace_back(std::wstring(lang)); 130 } 131 132 return result; 133 } 134 135 std::string LocaleIdToBcp47Tag(LCID localeId) 136 { 137 WCHAR localeName[MAX_LOCALE_SNAME_LEN] = {0}; 138 int ret = LCIDToLocaleName( 139 localeId, 140 localeName, 141 MAX_LOCALE_SNAME_LEN, 142 LOCALE_ALLOW_NEUTRAL_NAMES); 143 144 if (ret <= 0) 145 { 146 return {}; 147 } 148 149 return Utility::ConvertToUTF8(std::wstring(localeName)); 150 } 151 }