Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loop through embedded resources of different languages/cultures in C#

One level up from this question, what would be the way to store all (and loop through) available resources and associated cultures, to allow user selection of a specific culture?

Further explanation:

Suppose three resource files:

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

I could have a string in each called LanguageName. How would I be able to programmatically loop through the different LanguageName values to list them (in say a list box)?

EDIT: WinForms project, embedded resources.

like image 472
MPelletier Avatar asked Feb 25 '11 02:02

MPelletier


3 Answers

Here is a solution I think works for 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;
    }
}

And here is how you would use it for a WindowsFormsApplication1:

    List<CultureInfo> cultures = new List<CultureInfo>(EnumSatelliteLanguages("WindowsFormsApplication1.GUILanguage"));
like image 128
Simon Mourier Avatar answered Nov 12 '22 13:11

Simon Mourier


Assuming you have a ListBox named ListBox1 and your resource files named Resource.resx, Resource.es.resx, etc.:

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;
        }
    }
}
like image 26
Mariano Desanze Avatar answered Nov 12 '22 13:11

Mariano Desanze


Here is a method to return an IEnumerable of string with the values from the resource files, with an example usage. You just pass in the command part of the resource name and the key from the resources that you want.

[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));
    }
like image 1
pjbss Avatar answered Nov 12 '22 14:11

pjbss