windowswinapilocalizationglobalization

What is the right way to get the user's list of preferred languages from the control panel?


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?)


Solution

  • 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.