以编程方式获取所有可用语言(在卫星程序集中)的方法

34

我正在设计一个使用.resx文件的多语言应用程序。

我有一些文件,如GlobalStrings.resx、GlobalStrings.es.resx、GlobalStrings.en.resx等。 当我想要使用它时,我只需要设置Thread.CurrentThread.CurrentCulture。

问题是: 我有一个下拉框包含所有可用语言,但我需要手动加载它们:

comboLanguage.Items.Add(CultureInfo.GetCultureInfo("en"));
comboLanguage.Items.Add(CultureInfo.GetCultureInfo("es"));

我尝试过使用

cmbLanguage.Items.AddRange(CultureInfo.GetCultures(CultureTypes.UserCustomCulture));

尝试了多种方式,但都没有成功。也试过使用CultureTypes中的所有元素,但只得到了一个包含更多未使用语言或空列表的长列表。

有没有办法只获取支持的语言?

8个回答

53

你可以通过编程列出应用程序中可用的区域设置

// Pass the class name of your resources as a parameter e.g. MyResources for MyResources.resx
ResourceManager rm = new ResourceManager(typeof(MyResources));

CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (CultureInfo culture in cultures)
{
    try
    {
        ResourceSet rs = rm.GetResourceSet(culture, true, false);
        // or ResourceSet rs = rm.GetResourceSet(new CultureInfo(culture.TwoLetterISOLanguageName), true, false);
        string isSupported = (rs == null) ? " is not supported" : " is supported";
        Console.WriteLine(culture + isSupported);
    }
    catch (CultureNotFoundException exc)
    {
        Console.WriteLine(culture + " is not available on the machine or is an invalid culture identifier.");
    }
}

1
请注意,GlobalStrings.resx 将在 InvariantCulture 下被检测到。 - Hans
6
这不会强制加载所有卫星程序集吗? - Taylor Buchanan
@TaylorBuchanan 可以在此之后调用 ReleaseAllResources (https://msdn.microsoft.com/zh-cn/library/system.resources.resourcemanager.releaseallresources(v=vs.110).aspx),然后再次加载特定的资源。 - George Birbilis
如果(resourceSet != null && !string.IsNullOrWhiteSpace(culture.ToString())).... 请注意,您必须排除语言不变的资源。 - Tiago Freitas Leal

6

基于@hans-holzbart的回答,但修正了不返回InvariantCulture并包装成可重复使用的方法:

public static IEnumerable<CultureInfo> GetAvailableCultures()
{
  List<CultureInfo> result = new List<CultureInfo>();

  ResourceManager rm = new ResourceManager(typeof(Resources));

  CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
  foreach (CultureInfo culture in cultures)
  {
    try
    {
      if (culture.Equals(CultureInfo.InvariantCulture)) continue; //do not use "==", won't work

      ResourceSet rs = rm.GetResourceSet(culture, true, false);
      if (rs != null)
        result.Add(culture);
    }
    catch (CultureNotFoundException)
    {
      //NOP
    }
  }
  return result;
}

使用该方法,您可以获取一个字符串列表,并将其添加到ComboBox中,具体代码如下:
public static ObservableCollection<string> GetAvailableLanguages()
{
  var languages = new ObservableCollection<string>();
  var cultures = GetAvailableCultures();
  foreach (CultureInfo culture in cultures)
    languages.Add(culture.NativeName + " (" + culture.EnglishName + " [" + culture.TwoLetterISOLanguageName + "])");
  return languages;
}

4

以下是一种解决方案的基础:
每个特定语言的卫星装配件都被命名为相同的名称,但位于以特定文化命名的子文件夹中,例如 fr 或 fr-CA。

public IEnumerable<CultureInfo> GetSupportedCulture()
{
    //Get all culture 
    CultureInfo[] culture = CultureInfo.GetCultures(CultureTypes.AllCultures);

    //Find the location where application installed.
    string exeLocation = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));

    //Return all culture for which satellite folder found with culture code.
    return culture.Where(cultureInfo => Directory.Exists(Path.Combine(exeLocation, cultureInfo.Name)));
}

1
这个解决方案适用于我的ASP.net MVC项目。谢谢!! - Jeff T.
@JeffT。是的,这种方法在很多方面都很通用,我已经在我们的WPF项目中使用了这种方法! - Ankush Madankar
请注意,这也将返回不变语言。 - MrToast

2

根据Rune Grimstad的说法,我得出以下结论:

string executablePath = Path.GetDirectoryName(Application.ExecutablePath);
string[] directories = Directory.GetDirectories(executablePath);
foreach (string s in directories)
{
    try
    {
        DirectoryInfo langDirectory = new DirectoryInfo(s);
        cmbLanguage.Items.Add(CultureInfo.GetCultureInfo(langDirectory.Name));
    }
    catch (Exception)
    {

    }
}

或者另一种方式。
int pathLenght = executablePath.Length + 1;
foreach (string s in directories)
{
    try
    {
        cmbLanguage.Items.Add(CultureInfo.GetCultureInfo(s.Remove(0, pathLenght)));
    }
    catch (Exception)
    {

    }
}

我仍然认为这不是一个好主意...


我认为这是我们唯一的解决方案。我和你处境相同。 - Jippers
这样做会不会也获取你的依赖项部署的资源目录(如果有的话)?因此,您可能会得到一些您不支持但是您的依赖项支持的文化。 - Kaan C. Fidan

2
我不确定如何获取语言,也许您可以扫描安装文件夹中的dll文件,但将语言设置为不支持的语言不应该是问题。
如果没有找到特定语言的文件,.NET会回退到文化中性资源,因此您可以安全地选择不支持的语言。
只要您自己控制应用程序,您就可以在某个应用程序设置中存储可用的语言。只需使用逗号分隔的字符串来存储区域设置名称即可:"en, es"

1

指定要搜索的资源类型的通用答案。使用反射但已缓存。

用法:

List<string> comboBoxEntries = CommonUtil.CulturesOfResource<GlobalStrings>()
    .Select(cultureInfo => cultureInfo.NativeName)
    .ToList();

实现(实用类):
static ConcurrentDictionary<Type, List<CultureInfo>> __resourceCultures = new ConcurrentDictionary<Type, List<CultureInfo>>();

/// <summary>
/// Return the list of cultures that is supported by a Resource Assembly (usually collection of resx files).
/// </summary>
static public List<CultureInfo> CulturesOfResource<T>()
{
    return __resourceCultures.GetOrAdd(typeof(T), (t) =>
    {
        ResourceManager manager = new ResourceManager(t);
        return CultureInfo.GetCultures(CultureTypes.AllCultures)
            .Where(c => !c.Equals(CultureInfo.InvariantCulture) && 
                        manager.GetResourceSet(c, true, false) != null)
            .ToList();
    });
}

它可能会遇到与被接受的答案相同的问题,即所有语言资源可能都会被加载。

全局字符串的类型是什么? - Mecanik
已经有一段时间了,但我相信类型可以是任何公共类型,只要它作为资源提供(可能在不同的程序集中)。另一个例子可能是(完全限定更有意义):MC.Server.SomeLocalizableResourcesNamespace.Resource。 - crokusek

0

@"Ankush Madankar" 提供了一个有趣的起点,但它有两个问题: 1)还会查找引用程序集的资源文件夹 2)无法找到基础程序集语言的资源

我不打算解决问题2),但对于问题1),代码应该是

public List<CultureInfo> GetSupportedCultures()
{
    CultureInfo[] culture = CultureInfo.GetCultures(CultureTypes.AllCultures);

    // get the assembly
    Assembly assembly = Assembly.GetExecutingAssembly();

    //Find the location of the assembly
    string assemblyLocation =
        Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(assembly.CodeBase).Path));

    //Find the file anme of the assembly
    string resourceFilename = Path.GetFileNameWithoutExtension(assembly.Location) + ".resources.dll";

    //Return all culture for which satellite folder found with culture code.
    return culture.Where(cultureInfo =>
        assemblyLocation != null &&
        Directory.Exists(Path.Combine(assemblyLocation, cultureInfo.Name)) &&
        File.Exists(Path.Combine(assemblyLocation, cultureInfo.Name, resourceFilename))
    ).ToList();
}

-1

1. 获取用户首选的语言集合,按照优先级排序:

Windows.System.UserProfile.GlobalizationPreferences.Languages;

2. 获取或设置此上下文(应用程序)的语言限定符:

Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Languages;

两者都是 List<string>


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