Frank Osterfeld
Frank Osterfeld

Reputation: 25165

GetUserPreferredUILanguages() never returns more than two languages

I'm trying to retrieve the complete list of the user's preferred languages from a C++/Qt application, as configured in the "Region & language" page in the user's preferences:

Preferred languages

For that, I am trying with the WinAPI function GetUserPreferredUILanguages(), on an up-to-date Windows 10 Pro system.

However, the function always only returns the first entry (the main Windows display language), and "en-US". If English is configured as the main language, then only "en-US" is returned. E.g., if I have (German, French, English) configured, ["de-de", "en-US"] is returned, French is omitted. If I add more languages to the list, they are omitted as well. I also looked at User Interface Language Management, but to no avail. GetSystemPreferredUILanguages() for example only returns "en-US". GetUILanguageFallbackList() returns ["de-de", "de", "en-US", "en"].

The code I use:

// calling GetUserPreferredUILanguages() twice, once to get number of 
// languages and required buffer size, then to get the actual data

ULONG numberOfLanguages = 0;
DWORD bufferLength = 0;
const auto result1 = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME,
                                                 &numberOfLanguages,
                                                 nullptr,
                                                 &bufferLength);
// result1 is true, numberOfLanguages=2

QVector<wchar_t> languagesBuffer(static_cast<int>(bufferLength));
const auto result2 = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME,
                                                 &numberOfLanguages,
                                                 languagesBuffer.data(),
                                                 &bufferLength);

// result2 is true, languageBuffer contains "de-de", "en-US"

Is this not the right function to use, or am I misunderstanding something about the language configuration in Windows 10? How can I get the complete list of preferred languages? I see UWP API that might do the job, but if possible, I'd like to use C API, as it integrated more easily with the C++ codebase at hand. (unmanaged C++, that is)

Upvotes: 9

Views: 2149

Answers (2)

Jimm Chen
Jimm Chen

Reputation: 3797

I found out that language list returned by GetUserPreferredUILanguages() matters with your "Windows UI language" setting, and nothing to do with "Input method list order".

For example, in following screenshot from Win10.21H2,

Win10.21H2 metro UI language setting

I can see GetUserPreferredUILanguages() return a list of three langtags:

fr-CA\0fr-FR\0en-US\0\0

Check in VC2010 debugger

In summary, for GetUserPreferredUILanguages() and GetUILanguageFallbackList() their returned langtag list is determined solely by current user's "Windows display language" selection. It is a user-wide single-selection setting. And, for a specific display-language selection, the list-items within and the order of the list-items are hard-coded by Windows itself. Yes, it is even unrelated to what "input methods(IME)" you have added to the control panel -- for example, you add "fr-CA" but not "fr-FR", and the fallback list will still be fr-CA\0fr-FR\0en-US\0\0.

The difference of the two APIs, according to my experiment, is that GetUILanguageFallbackList() returns neutral langtags("fr", "en" etc) as well, so it produces a superset of GetUserPreferredUILanguages().

Upvotes: 1

emk
emk

Reputation: 221

GlobalizationPreferences.Languages is usable from unmanaged C++ because GlobalizationPreferences has DualApiPartitionAttribute. Here is a C++/WinRT example of using GlobalizationPreferences.Languages:

#pragma once
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.System.UserProfile.h>
#include <iostream>
#pragma comment(lib, "windowsapp")

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::System::UserProfile;

int main()
{
    winrt::init_apartment();

    for (const auto& lang : GlobalizationPreferences::Languages()) {
        std::wcout << lang.c_str() << std::endl;
    }
}

And a WRL example for those who cannot migrate to C++ 17:

#include <roapi.h>
#include <wrl.h>
#include <Windows.System.UserProfile.h>
#include <iostream>
#include <stdint.h>
#pragma comment(lib, "runtimeobject.lib")

using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::System::UserProfile;

int main()
{
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize)) {
        std::cerr << "RoInitialize failed" << std::endl;
        return 1;
    }

    ComPtr<IGlobalizationPreferencesStatics> gps;
    HRESULT hr = RoGetActivationFactory(
        HStringReference(
            RuntimeClass_Windows_System_UserProfile_GlobalizationPreferences)
            .Get(),
        IID_PPV_ARGS(&gps));
    if (FAILED(hr)) {
        std::cerr << "RoGetActivationFactory failed" << std::endl;
        return 1;
    }

    ComPtr<IVectorView<HSTRING>> langs;
    hr = gps->get_Languages(&langs);
    if (FAILED(hr)) {
        std::cerr << "Could not get Languages" << std::endl;
        return 1;
    }

    uint32_t size;
    hr = langs->get_Size(&size);
    if (FAILED(hr)) {
        std::cerr << "Could not get Size" << std::endl;
        return 1;
    }
    for (uint32_t i = 0; i < size; ++i) {
        HString lang;
        hr = langs->GetAt(i, lang.GetAddressOf());
        if (FAILED(hr)) {
            std::cerr << "Could not get Languages[" << i << "]" << std::endl;
            continue;
        }
        std::wcout << lang.GetRawBuffer(nullptr) << std::endl;
    }
}

Upvotes: 6

Related Questions