0

In the "language settings" control panel in Windows 10 (and older versions -- this appears to have been introduced in Win8?) there is a list of "Preferred Languages". What is the correct way to programmatically obtain that list?

I can see it stored in the registry at HKEY_CURRENT_USER\Control Panel\International\User Profile\Languages, but I assume that it is not intended that this be read directly.

I found an API GetUserPreferredUILanguages that sounds like the right thing -- but it returns the wrong results.

Specifically, in the control panel and registry key I currently have the list en-NZ en-US it-IT, but the API returns en-GB en-US. I have no idea where it's getting that from. (Or why Italian is missing.)

The GetUserDefaultLocaleName API does correctly return en-NZ, but it also seems to be getting that from somewhere else -- when I rearrange the language list in the control panel, the registry updates but the API return value doesn't change.

I do want the full list of languages, not just a single answer. (Also, out of curiosity, which control panel is the API getting its answers from?)

Miral
  • 12,637
  • 4
  • 53
  • 93
  • Possibly of note is that if I run [`Get-WinUserLanguageList`](https://learn.microsoft.com/en-us/powershell/module/international/get-winuserlanguagelist) in PowerShell then this does correctly report the language list matching the control panel. However this isn't the kind of API that I'm looking for. – Miral Sep 14 '20 at 01:37
  • I hadn't tried `EnumUILanguages` (as a now-deleted comment suggested), because none of its docs indicated that it would return values in preference order. And indeed the results appear to be `en-GB` `en-US` `it-IT` -- which is slightly better, in that Italian at least appears, but it still appears in this order even if I move Italian to be the first language in the preferred list. Also it's still saying `en-GB` rather than `en-NZ`. – Miral Sep 14 '20 at 02:38
  • 1
    Does [this answer](https://stackoverflow.com/a/58933495/10611792) work for you? – Drake Wu Sep 14 '20 at 05:30
  • I don't think so -- I wanted to get the values both from unmanaged C++ (MFC) and from C# (.NET Framework), both from regular desktop apps. And I don't want a hard dependency on Win8 libs, there may still be some Win7 users in the wild. – Miral Sep 14 '20 at 05:38
  • APIs in Drake's link work for C++ and C#, and also for desktop apps as they are marked "UniversalApiContract". But it works only on Windows 10. For other systems, have you tried EnumUILanguages (works for me): https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-enumuilanguagesw – Simon Mourier Sep 14 '20 at 06:43
  • Windows 10 1607 is also a target that I need to support, and from the looks of things that isn't supported for C# calling UWP. As for EnumUILanguages, read the earlier comment. – Miral Sep 14 '20 at 07:08
  • Other than that, there is this the GetUserLanguages function: https://pastebin.com/raw/nLF1uWjc should work on Windows 8. Not Windows 7 but Windows 7 has no such UI/languages concepts. – Simon Mourier Sep 14 '20 at 07:52
  • GetUserLanguages seems to work as expected; you could post that as an answer. Although it appears to be undocumented, which makes me a bit nervous. – Miral Sep 14 '20 at 23:26

1 Answers1

0

You can use undocumented GetUserLanguages API from bcp47langs.dll that is available since Windows 8.

#include <hstring.h>
#include <winstring.h>

typedef int (WINAPI* GetUserLanguagesFn)(char Delimiter, HSTRING* UserLanguages);

int main()
{
    auto h = LoadLibrary(L"bcp47langs.dll");
    auto fn = (GetUserLanguagesFn)GetProcAddress(h, "GetUserLanguages");
    HSTRING str;
    fn(',', &str);
    WindowsDeleteString(str);
    return 0;
}
public static void Main(string[] args)
{
    GetUserLanguages(',', out var langs);
    Console.WriteLine(langs);
}

[DllImport("bcp47langs.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetUserLanguages(char Delimiter, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(HStringMarshaler))] out string UserLanguages);

PS: there is also GetUserLanguageInputMethods exists that can get list of input methods for a language in:

[DllImport("bcp47langs.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetUserLanguageInputMethods(string Language, char Delimiter, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(HStringMarshaler))] out string InputMethods);

The string format of the keyboard layout:

<LangID>:<KLID>

The string format of the text service is:

<LangID>:{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

Posing on behalf of @Simon Mourier.

DJm00n
  • 1,083
  • 5
  • 18