Reputation: 25165
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:
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
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,
I can see GetUserPreferredUILanguages()
return a list of three langtags:
fr-CA\0fr-FR\0en-US\0\0
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
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