winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }