在Windows 10中以编程方式列出所有支持的语言环境

4

Windows 10支持自定义语言环境,这些语言环境不像旧版本的Windows中那样具有LCID。在每个Windows 10更新中,Microsoft都会添加少数族裔和土著语言的较少使用的语言环境。

最近一次Windows 10更新添加了Võro kiil (vro)语言环境。当使用EnumSystemLocalesEx枚举所有支持的语言环境时,vro没有以任何形式出现。但是,在系统设置UI中添加新语言或键盘时,Võro kiil确实出现了。

但是,如果用户随后启用此语言,则调用EnumSystemLocalesEx时,vrovro-Latnvro-Latn-001现在被列出。如果然后从UI中删除此语言环境,则它将不再出现在此函数调用的结果中。

问题:是否有办法(不管是不受支持的还是受支持的)获取操作系统中所有已知的语言环境列表,而不管用户是否启用了它们?

我发现这种输出很奇怪,因为它包括其他少数民族语言,如Skolt Sami,而不需要用户事先启用它。

如果C/C++ API中不存在该API,我将非常乐意接受使用.NET Framework的答案,只要我能够实际获得这些数据。


生成语言环境输出的示例代码:

#include <cstdio>
#include "stdafx.h"
#include "windows.h"

BOOL CALLBACK Callback(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
{
    wprintf(L"%ls\n", pStr);
    return TRUE;
}

int main()
{
    EnumSystemLocalesEx(Callback, 0, 0, 0);
    return 0;
}

在“区域和语言”设置屏幕中启用Võro kiil后,最后三个结果是vro-Latnvro-Latn-001vro。如果没有启用,则它们将完全不会出现在输出中。使用.NET API似乎具有相同的行为。
#include "stdafx.h"

using namespace System;
using namespace System::Globalization;
int main()
{
    System::Collections::IEnumerator^ enum0 = CultureInfo::GetCultures(CultureTypes::AllCultures)->GetEnumerator();
    while (enum0->MoveNext())
    {
        CultureInfo^ ci = safe_cast<CultureInfo^>(enum0->Current);
        Console::WriteLine("{0}", ci->Name);
    }
}

1
请提供 EnumSystemLocalesEx 的 [mcve],以便我们尝试复现并确定这是否是API的问题还是您使用它的方式的问题。 - zett42
已添加一个示例。 - Brendan Molloy
我可以重现,但是我不知道为什么会发生这种情况。如果你找不到另一个API,你可以使用进程监视器来确定设置应用程序使用的注册表键,以获取所有可安装区域设置的列表。 - zett42
我已经检查了活动监视器,似乎它并没有从注册表中提取数据。根据一些注册表调用的堆栈,我的最佳猜测是它存储在Winlangdb.dll中,该DLL由系统设置应用程序本身使用...但是没有可用的文档API或调试符号。 - Brendan Molloy
你实际需要这个是做什么的?也许有更好/其他的方法来完成它。 - zett42
只有在操作系统不支持自定义语言环境时,才需要在注册表中启用它并为该语言环境安装自定义键盘,以安装和启用自定义语言环境。问题在于,系统设置使用的实际语言代码通常与语言环境的普通形式略有不同,因此所选语言将变为“vro-Latn”而不是“vro”。使用UI不理解的代码将导致其锁定和崩溃。 - Brendan Molloy
1个回答

0
实际上,Windows 的新语言模型意味着除了具有历史 LCID 的语言列表之外,没有其他的“列表”。
Windows 8.1 和 10 的设置工具链接到 bcp47langs.dll 和 winlangdb.dll,提供了启用语言和输入法的功能,只要提供的输入是有效的 ISO 639-3 语言代码。
在某些情况下,如果您希望您的语言出现在用户界面或通过这些 API,您必须至少提供脚本,有时还需要提供地区。例如,Erzya 语言的代码是 myv-Cyrl。
使用这些 API
通过对 PowerShell 捆绑的 cmdlet 进行 MSIL 反汇编,我找到了一个 p/invoke 定义,使我能够成功地从 C# 和 Rust 代码中使用这些 API。
以下是它,为了后世留存:
// Decompiled with JetBrains decompiler
// Type: Microsoft.InternationalSettings.Commands.LPAPIWrapper
// Assembly: Microsoft.InternationalSettings.Commands, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: E0B49792-544F-4FBD-8C35-D4DA177385AF
// Assembly location: C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.InternationalSettings.Commands\v4.0_3.0.0.0__31bf3856ad364e35\Microsoft.InternationalSettings.Commands.dll

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Microsoft.InternationalSettings.Commands
{
  internal class LPAPIWrapper
  {
    public static uint GEO_NATION = 1;
    public static uint GEO_FRIENDLYNAME = 8;
    public static uint GEOCLASS_NATION = 16;
    public static uint GEOCLASS_REGION = 14;

    [DllImport("kernelbase.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int NlsUpdateLocale(string LocaleName, int Flags);

    [DllImport("intl.cpl", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int IntlUpdateSystemLocale(string LocaleName, int dwFlags);

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SystemParametersInfo(
      uint Action,
      uint UnsignedParam,
      IntPtr Param,
      uint WinIni);

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SendNotifyMessage(
      IntPtr wWwnd,
      uint Msg,
      IntPtr wParam,
      string lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetSystemDefaultLocaleName(
      StringBuilder LocaleName,
      int LocaleNameSize);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetUserGeoID(uint GeoClass);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetUserGeoID(int GeoId);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetGeoInfo(
      int Location,
      uint GeoType,
      StringBuilder GeoData,
      int Length,
      ushort LangID);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetUserLanguages(char Delimiter, [MarshalAs(UnmanagedType.HString)] ref string UserLanguages);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetUserLanguageInputMethods(
      string Language,
      char Delimiter,
      [MarshalAs(UnmanagedType.HString)] ref string InputMethods);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int LcidFromBcp47([MarshalAs(UnmanagedType.HString)] string LanguageTag, ref int Lcid);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetPendingUserDisplayLanguage([MarshalAs(UnmanagedType.HString)] ref string language);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetUserDisplayLanguageOverride([MarshalAs(UnmanagedType.HString)] ref string language);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetUserDisplayLanguageOverride(string LanguageTag);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int ClearUserDisplayLanguageOverride();

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetHttpAcceptLanguageOptOut(ref bool IsOptOut);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetHttpAcceptLanguageOptOut();

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int ClearHttpAcceptLanguageOptOut();

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetUserLocaleFromLanguageProfileOptOut(ref bool IsOptOut);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetUserLocaleFromLanguageProfileOptOut();

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int ClearUserLocaleFromLanguageProfileOptOut();

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int RemoveInputsForAllLanguagesInternal();

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetInputMethodOverride(string TipString);

    [DllImport("bcp47langs.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int Bcp47GetIsoLanguageCode([MarshalAs(UnmanagedType.HString)] string languageTag, [MarshalAs(UnmanagedType.HString)] ref string isoLanguageCode);

    [DllImport("ext-ms-win-globalization-input-l1-1-2.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int WGIGetDefaultInputMethodForLanguage(
      [MarshalAs(UnmanagedType.HString)] string Language,
      [MarshalAs(UnmanagedType.HString)] ref string DefaultTipString);

    [DllImport("ext-ms-win-globalization-input-l1-1-2.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int WGITransformInputMethodsForLanguage(
      [MarshalAs(UnmanagedType.HString)] string TipString,
      [MarshalAs(UnmanagedType.HString)] string Language,
      [MarshalAs(UnmanagedType.HString)] ref string TransformedTipString);

    [DllImport("winlangdb.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetUserLanguages(char Delimiter, [MarshalAs(UnmanagedType.HString)] string UserLanguages);

    [DllImport("winlangdb.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetLanguageNames(
      string Language,
      StringBuilder Autonym,
      StringBuilder EnglishName,
      StringBuilder LocalName,
      StringBuilder ScriptName);

    [DllImport("ext-ms-win-globalization-input-l1-1-2.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int WGIIsImeInputMethod([MarshalAs(UnmanagedType.HString)] string TipString, ref int result);

    [DllImport("winlangdb.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int EnsureLanguageProfileExists();

    [DllImport("input.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int InstallLayoutOrTip(string TipString, int Flags);

    [DllImport("input.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetDefaultLayoutOrTip(string TipString, int Flags);

    [DllImport("input.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetLayoutDescription(
      string LayoutId,
      StringBuilder LayoutDescription,
      ref int DescriptionLength);

    private LPAPIWrapper()
    {
    }
  }
}
 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接