How to map a two letter ISO-3166 country code to a Culture in C#

13,104

Solution 1

From CultureInfo(string) constructor documentation;

For a list of predefined culture names, see the National Language Support (NLS) API Reference

Also from CultureInfo.TwoLetterISOLanguageName Property

The ISO 639-1 two-letter code for the language of the current CultureInfo.

There is no US defined but there is en (if you really have to use two letter name) which you can use it. All of them defined in that standard.

var cultureInfo = new CultureInfo("en");

All of that two-letter codes are valid except iv which is for InvariantCulture so we can ignore it. I wrote a simple code to check it.

foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
     try
     {
          var c = new CultureInfo(culture.TwoLetterISOLanguageName);
     }
     catch (Exception)
     {
          Console.WriteLine(culture.TwoLetterISOLanguageName); // Only prints "iv"
     }
}

How to create your own mapping? I suggest to follow these steps;

  • Find the different two-letter codes in ISO-3166 and ISO 639-1 standards.
  • Create your own KeyValuePair<string, string> structure which contains ISO-3166 two-letter codes as the first one and ISO 639-1 two-letter codes as the second one.
  • If your two-letter ISO-3166 two-letter code fails in this constructor, check it's mapped two-letter code and try to create your CultureInfo using that two-letter code.

Solution 2

You should be able to use an invariant culture, if it's supported by .net.

E.g. This works:

var culture = new CultureInfo("EN");

As you've noted, there doesn't appear to be full support for all country codes, this is because the two characters it takes are a Neutral Culture.

You can get a list of all supported cultures via:

CultureInfo.GetCultures(CultureTypes.AllCultures);

And specifically Neutral ones with:

CultureInfo.GetCultures(CultureTypes.NeutralCultures);

The reason that var culture = new CultureInfo("US"); doesn't work is because the neutral culture for US is EN. (the specific culture being en-US).

There are other high voted answers on converting Country Codes to Cultures, which I'm not going to dupe here.

Solution 3

The last part of CultureInfo.Name is country's ISO-3166 code, so this is how I ended up my solution,

public static CultureInfo GetCultureFromTwoLetterCountryCode(string twoLetterISOCountryCode)
{
    try
    {
        return CultureInfo.GetCultures(CultureTypes.AllCultures 
                                           & ~CultureTypes.NeutralCultures)
                  .Where(m => m.Name.EndsWith("-"+twoLetterISOCountryCode))
                  .FirstOrDefault();
     }
     catch
     {
         return null;
     }
}

The only problem is some countries have more than one culture like GB, so you can't select the right culture just with ISO-3166 code,

cy-GB Welsh (United Kingdom)

en-GB English (United Kingdom)

gd-GB Scottish Gaelic (United Kingdom)

So GetCultureFromTwoLetterCountryCode("GB") will return "cy-GB" culture which may be ok for Wales but not for Scotland.

You can ignore these kind of exceptions or create your own cultureinfo database and store only one culture for each country. That will be the best solution.

Share:
13,104
Gianluca Ghettini
Author by

Gianluca Ghettini

I'm a software developer.

Updated on June 13, 2022

Comments

  • Gianluca Ghettini
    Gianluca Ghettini almost 2 years

    I need to localize my C# DateTime object based on a two letter ISO-3166 country code

    https://en.wikipedia.org/wiki/ISO_3166-2

    Is there a way to do it?

    I'm only able to apply a given culture using the 5 character country representation (en-US, nl-NL and so on..)

    System.Globalization.CultureInfo cultureinfo =
            new System.Globalization.CultureInfo("nl-NL");
    DateTime dt = DateTime.Parse(date, cultureinfo);
    

    Even a surjective mapping would be ok (i.e. more than one country code map to a single C# culture)