Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find which resource cultures are available in a ResourceManager?

I have a situation where I would like to present a list of the "available languages" for my application (which, incidentally, is an ASP .NET MVC 3 application if that makes any odds). I thought that I could automatically get this list somehow since it should just be the resx files that are included in the build (I don't need to support English UK, German Austria or anything, just English or German) and I came up with a scheme that I will present below (implemented as a singleton since it is a bit of an intensive approach).

The problem is that on some machines it returns "Arabic" even though I have no such resource and on mine (since I installed VS 2012) it returns all of them (this makes more sense to me than returning just the two real cultures plus Arabic but it seems that the ResourceManager just wasn't designed to let me get at this information so I probably should not complain). Here is the scheme...

(I have a Strings.resx and a Strings.de.resx file)

IEnumerable<CultureInfo> cultures =
    CultureInfo.GetCultures(CultureTypes.NeutralCultures)
        .Where(c =>
                   {
                       // Exclude the invariant culture and then load up
                       // an arbitrary string so the resource manager
                       // loads a resource set, then get the set for the
                       // current culture specifically and it is, sometimes
                       // (I thought always but I was wrong) null if no
                       // set exists
                       if (c.LCID == CultureInfo.InvariantCulture.LCID)
                           return false;

                       var rm = Strings.ResourceManager;
                       rm.GetString("HELLO", c);
                       return rm.GetResourceSet(c, false, false) != null;
                   });

So then I thought, well, I could do this based on whether the language-specific directory exists like so:

var neutralCulture = new[]
{
    CultureInfo
        .CreateSpecificCulture(((NeutralResourcesLanguageAttribute)
                                Assembly
                                    .GetExecutingAssembly()
                                    .GetCustomAttributes(
                                        typeof (NeutralResourcesLanguageAttribute),
                                        false)[0])
                                .CultureName)
};

IEnumerable<CultureInfo> cultures =
    CultureInfo.GetCultures(CultureTypes.NeutralCultures)
        .Where(c => Directory.Exists(c.TwoLetterISOLanguageName))
        .Union(neutralCulture);

This "works" (in so much as it returns English and German) but I think it is not a very stable approach being as it is prone to random problems like someone creating a folder and throwing it all out of whack. I can probably alleviate these issues with some more judicious checks (the where clause is crying out for more sophistication) but and here is the question (finally)...

Right now I am thinking of just going with a config file and keeping it totally simple since I do not really like where I have got to but is there a better way to do this (or: can it be done automatically in a safe way)?

like image 715
kmp Avatar asked Aug 20 '12 06:08

kmp


2 Answers

I like your second approach for automatic detection. I would add though that you should only do this once (on application start or as part of a static constructor) and make it static instead of computing it every time you request the supported culture info.

I think the config approach would work as well, though it's not really automatic. My only thought for that case is that if you are localizing for a language, it's not something that's going to sneak into your application under the radar. That being said, adding a config value at that point seems like an easy thing to do (or to forget to do).

I'm not aware of anything built into the .NET Framework to give you this information with a method call.

like image 184
Brian Dishaw Avatar answered Nov 11 '22 23:11

Brian Dishaw


I found no valid and reliable way of doing this.

In each of my assemblies I write a code like this that references the supported cultures. That's all we can do. You have to remember to update this array when you add a localization.

internal static class ThisAssembly
{
    static readonly string[] SupportedCultures = new string[] { "en-US", "de-DE", };
}

I also tried to:

  • enumerate the loaded sattelite assemblies: there is no method for that;
  • get the value of ResourceManager._resourceSets: the dictionary of loaded sets gives correct and invalid values.

But to no avail.

like image 29
SandRock Avatar answered Nov 11 '22 23:11

SandRock