在 C# 中循环访问不同语言/文化的嵌入式资源

Posted

技术标签:

【中文标题】在 C# 中循环访问不同语言/文化的嵌入式资源【英文标题】:Loop through embedded resources of different languages/cultures in C# 【发布时间】:2011-02-25 02:03:22 【问题描述】:

从this question 上一级,存储所有(并循环访问)可用资源和相关文化以允许用户选择特定文化的方式是什么?

进一步解释:

假设三个资源文件:

GUILanguage.resx GUILanguage.fr.resx GUILanguage.it.resx

我可以在每个字符串中都有一个名为LanguageName 的字符串。我如何能够以编程方式循环遍历不同的 LanguageName 值以列出它们(比如列表框)?

编辑:WinForms 项目,嵌入式资源。

【问题讨论】:

【参考方案1】:

这是我认为适用于 Winforms 的解决方案:

// get cultures for a specific resource info
public static IEnumerable<CultureInfo> EnumSatelliteLanguages(string baseName)

    if (baseName == null)
        throw new ArgumentNullException("baseName");

    ResourceManager manager = new ResourceManager(baseName, Assembly.GetExecutingAssembly());
    ResourceSet set = manager.GetResourceSet(CultureInfo.InvariantCulture, true, false);
    if (set != null)
        yield return CultureInfo.InvariantCulture;

    foreach (CultureInfo culture in EnumSatelliteLanguages())
    
        set = manager.GetResourceSet(culture, true, false);
        if (set != null)
            yield return culture;
    


// determine what assemblies are available
public static IEnumerable<CultureInfo> EnumSatelliteLanguages()

    foreach (string directory in Directory.GetDirectories(AppDomain.CurrentDomain.BaseDirectory))
    
        string name = Path.GetFileNameWithoutExtension(directory); // resource dir don't have an extension...

        // format is XX or XX-something, we discard directories that can't match.
        // could/should be replaced by a regex but we still need to catch cultures errors...
        if (name.Length < 2)
            continue;

        if (name.Length > 2 && name[2] != '-')
            continue;

        CultureInfo culture = null;
        try
        
            culture = CultureInfo.GetCultureInfo(name);
        
        catch
        
            // not a good directory...
            continue;
        

        string resName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".resources.dll";
        if (File.Exists(Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, name), resName)))
            yield return culture;
    

下面是您将它用于 WindowsFormsApplication1 的方法:

    List<CultureInfo> cultures = new List<CultureInfo>(EnumSatelliteLanguages("WindowsFormsApplication1.GUILanguage"));

【讨论】:

这不适用于某些有 6 个字符代码的汉语方言。 zh-CHS 和 zh-CHT。对于 .Net 4 中引入的一些新文化,它也将失败:msdn.microsoft.com/en-us/library/vstudio/…【参考方案2】:

假设您有一个名为 ListBox1 的 ListBox 和名为 Resource.resxResource.es.resx 等的资源文件:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web.UI.WebControls;
using Resources;

public partial class _Default : System.Web.UI.Page

    protected void Page_Load(object sender, EventArgs e)
    
        if (ListBox1.Items.Count < 1)
        
            var installedCultures = GetInstalledCultures();
            IEnumerable<ListItem> listItems = installedCultures.Select(cultInfo =>
                new ListItem(Resource.ResourceManager.GetString("LanguageName", cultInfo), cultInfo.Name));
            ListBox1.Items.AddRange(listItems.ToArray());
        
    

    public IEnumerable<CultureInfo> GetInstalledCultures()
    
        foreach (string file in Directory.GetFiles(Server.MapPath("~/App_GlobalResources"), "*.resx"))
        
            if (!file.EndsWith(".resx"))
                continue;
            var endCropPos = file.Length - ".resx".Length;
            var beginCropPos = file.LastIndexOf(".", endCropPos - 1) + 1;
            string culture = "en";
            if (beginCropPos > 0 && file.LastIndexOf("\\") < beginCropPos)
                culture = file.Substring(beginCropPos, endCropPos - beginCropPos);
            yield return new CultureInfo(culture);
        
    

    // override to set the Culture with the ListBox1 (use AutoPostBack="True")
    protected override void InitializeCulture()
    
        base.InitializeCulture();

        var cult = Request["ctl00$MainContent$ListBox1"];
        if (cult != null)
        
            Culture = cult;
            UICulture = cult;
        
    

【讨论】:

这是一个很棒的答案。我会问它是否可以作为嵌入式资源来破坏它,因为它是一个 WinForms 项目。【参考方案3】:

这是一种返回 IEnumerable 字符串的方法,其中包含资源文件中的值,并带有示例用法。您只需传入资源名称的命令部分和所需资源中的密钥即可。

[Test]
    public void getLanguageNames()
    
        var names = GetResourceLanguageNames(Assembly.GetExecutingAssembly(), "GUILanguage", "LanguageName");
        Console.WriteLine(string.Join("-",names));
    

    public IEnumerable<string> GetResourceLanguageNames(Assembly assembly, string resourceName, string key)
    
        var regex = new Regex(string.Format(@"(\w)?0(\.\w+)?", resourceName));

        var matchingResources =  assembly.GetManifestResourceNames()
                                    .Where(n => regex.IsMatch(n)).Select(rn=>rn.Remove(rn.LastIndexOf(".")));

        return matchingResources.Select(rn => new ResourceManager(rn, assembly).GetString(key));
    

【讨论】:

以上是关于在 C# 中循环访问不同语言/文化的嵌入式资源的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中将 Exe 文件作为嵌入式资源运行

在 c# 中从其他资源中引用嵌入式资源

相同的 fql(facebook 查询语言)在图形资源管理器中显示不同的结果并嵌入在 javascript 代码中

在 C++ 项目中嵌入 XML 字符串资源

C# 应用程序中的资源和嵌入式资源有啥区别?

C#中的foreach循环怎么用?