System.Globalization.RegionInfo complete list of valid culture inputs

8.5k views Asked by At

I've got a list of ISO 3166 two-letter country codes acquired from an external source. For each, I create

     new System.Globalization.RegionInfo(countryCode) 

and occasionally one is invalid resulting in an ArgumentException "Culture name 'xx' is not supported."

I want a function to determine if the country code is valid before I pass it into the constructor. This is my attempt:

    private bool IsCultureValid(string cultureName)
    {
        return CultureInfo.GetCultures(CultureTypes.AllCultures)
            .Any(c => c.Name.Equals(cultureName, StringComparison.InvariantCultureIgnoreCase));
    }

The function returns a false negative for many inputs (function returns false, but I can create a RegionInfo object with that input if I try). Some inputs:

  • zw (Zimbabwe)
  • au (Australia)
  • mx (Mexico)
  • ve (Bolivarian Republic of Venezuela)
  • hn (Honduras)
  • kw (Kuwait)

What am I missing? Is there a better approach here? Thanks in advance!

4

There are 4 answers

1
Shawn Wheeler On BEST ANSWER

I realize this is a dated question. However, I recently ran across a similar situation where I needed to validate incoming ISO currency codes. All of the examples I could find here and elsewhere relied on catching the exception that was thrown when attempting to create a region or culture with an invalid code/id. Which is just not a good practice.

My own research into the problem led me to realize that for the most part, the problem was the invariant culture and neutral cultures. Once they are removed from the CultureInfo array it is possible to generate a list of only valid RegionInfo objects.

This is an extrapolation from my own issue to provide the requested answer. Though obviously a variation of this could be applied anywhere you need just the valid RegionInfo objects.

private bool IsValidRegion(string isoCountryCode)
    {
        return CultureInfo.GetCultures(CultureTypes.AllCultures)
            .Where(x => !x.Equals(CultureInfo.InvariantCulture)) //Remove the invariant culture as a region cannot be created from it.
            .Where(x => !x.IsNeutralCulture) //Remove nuetral cultures as a region cannot be created from them.
            .Select(x => new RegionInfo(x.LCID))
            .Any(x => x.Name.Equals(isoCountryCode, StringComparison.InvariantCulture));
    }

Edit: Unless using custom cultures this can actually be done even more directly. Simply use the "CultureTypes.SpecificCultures" enum value.

1
Noman Khan On

You are getting false because they don't exist. Here is the list of all the cultures obtained with the following loop:

foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
    ci.Name
}

ar,bg,ca,zh-Hans,cs,da,de,el,en,es,fi,fr,he,hu,is,it,ja,ko,nl,no,pl,pt,rm,ro,ru,hr,sk,sq,sv,th,tr,ur,id,uk,be,sl,et,lv,lt,tg,fa,vi,hy,az,eu,hsb,mk,tn,xh,zu,af,ka,fo,hi,mt,se,ga,ms,kk,ky,sw,tk,uz,tt,bn,pa,gu,or,ta,te,kn,ml,as,mr,sa,mn,bo,cy,km,lo,gl,kok,syr,si,iu,am,tzm,ne,fy,ps,fil,dv,ha,yo,quz,nso,ba,lb,kl,ig,ii,arn,moh,br,,ug,mi,oc,co,gsw,sah,qut,rw,wo,prs,gd,ar-SA,bg-BG,ca-ES,zh-TW,cs-CZ,da-DK,de-DE,el-GR,en-US,fi-FI,fr-FR,he-IL,hu-HU,is-IS,it-IT,ja-JP,ko-KR,nl-NL,nb-NO,pl-PL,pt-BR,rm-CH,ro-RO,ru-RU,hr-HR,sk-SK,sq-AL,sv-SE,th-TH,tr-TR,ur-PK,id-ID,uk-UA,be-BY,sl-SI,et-EE,lv-LV,lt-LT,tg-Cyrl-TJ,fa-IR,vi-VN,hy-AM,az-Latn-AZ,eu-ES,hsb-DE,mk-MK,tn-ZA,xh-ZA,zu-ZA,af-ZA,ka-GE,fo-FO,hi-IN,mt-MT,se-NO,ms-MY,kk-KZ,ky-KG,sw-KE,tk-TM,uz-Latn-UZ,tt-RU,bn-IN,pa-IN,gu-IN,or-IN,ta-IN,te-IN,kn-IN,ml-IN,as-IN,mr-IN,sa-IN,mn-MN,bo-CN,cy-GB,km-KH,lo-LA,gl-ES,kok-IN,syr-SY,si-LK,iu-Cans-CA,am-ET,ne-NP,fy-NL,ps-AF,fil-PH,dv-MV,ha-Latn-NG,yo-NG,quz-BO,nso-ZA,ba-RU,lb-LU,kl-GL,ig-NG,ii-CN,arn-CL,moh-CA,br-FR,ug-CN,mi-NZ,oc-FR,co-FR,gsw-FR,sah-RU,qut-GT,rw-RW,wo-SN,prs-AF,gd-GB,ar-IQ,zh-CN,de-CH,en-GB,es-MX,fr-BE,it-CH,nl-BE,nn-NO,pt-PT,sr-Latn-CS,sv-FI,az-Cyrl-AZ,dsb-DE,se-SE,ga-IE,ms-BN,uz-Cyrl-UZ,bn-BD,mn-Mong-CN,iu-Latn-CA,tzm-Latn-DZ,quz-EC,ar-EG,zh-HK,de-AT,en-AU,es-ES,fr-CA,sr-Cyrl-CS,se-FI,quz-PE,ar-LY,zh-SG,de-LU,en-CA,es-GT,fr-CH,hr-BA,smj-NO,ar-DZ,zh-MO,de-LI,en-NZ,es-CR,fr-LU,bs-Latn-BA,smj-SE,ar-MA,en-IE,es-PA,fr-MC,sr-Latn-BA,sma-NO,ar-TN,en-ZA,es-DO,sr-Cyrl-BA,sma-SE,ar-OM,en-JM,es-VE,bs-Cyrl-BA,sms-FI,ar-YE,en-029,es-CO,sr-Latn-RS,smn-FI,ar-SY,en-BZ,es-PE,sr-Cyrl-RS,ar-JO,en-TT,es-AR,sr-Latn-ME,ar-LB,en-ZW,es-EC,sr-Cyrl-ME,ar-KW,en-PH,es-CL,ar-AE,es-UY,ar-BH,es-PY,ar-QA,en-IN,es-BO,en-MY,es-SV,en-SG,es-HN,es-NI,es-PR,es-US,bs-Cyrl,bs-Latn,sr-Cyrl,sr-Latn,smn,az-Cyrl,sms,zh,nn,bs,az-Latn,sma,uz-Cyrl,mn-Cyrl,iu-Cans,zh-Hant,nb,sr,tg-Cyrl,dsb,smj,uz-Latn,mn-Mong,iu-Latn,tzm-Latn,ha-Latn,zh-CHS,zh-CHT

So you can see it does not contain zw, but it has en-ZW similarly for au it has en-AU

6
Oscar On

You could write a function that creates the specific culture inside a try/catch block and return the CultureInfo object instead of bool.

By the way, there is no such culture as ve, it's es-VE and so on for Mexico, Honduras.. Culture info for "derived" cultures must have parent culture code before. en-AU, en-US, and so on

http://www.localeplanet.com/dotnet/es-VE/index.html

To get a list of all correct values of installed cultures, use:

CultureInfo.GetCultures();

https://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.getcultures(v=vs.110).aspx

1
Jeppe Stig Nielsen On

This is a comment-like post, not necessarily a full answer.

As in other answers, region infos can be created from culture infos. As an example, on my current system, the code:

var comp = Comparer<RegionInfo>.Create((x, y) => string.Compare(x.Name, y.Name));
var count = 0;
var set = new SortedSet<RegionInfo>(comp);
foreach (var sci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) {
    set.Add(new RegionInfo(sci.LCID));
    ++count;
}

runs through 574 specific cultures but creates only 142 different regions (and a region may possess several languages, so this is not unusual).

But (on my system at least) there are regions that do not arise from a culture.

For example, on my system, if I do:

var comp = Comparer<RegionInfo>.Create((x, y) => string.Compare(x.Name, y.Name));
var set2 = new SortedSet<RegionInfo>(comp);
for (var i = 0; i < 1000; ++i) {
    try {
        set2.Add(new RegionInfo($"{i:D3}"));
    } catch (ArgumentException) {
    }
}
for (var a = 'A'; a <= 'Z'; ++a) {
    for (var b = 'A'; b <= 'Z'; ++b) {
        try {
            set2.Add(new RegionInfo($"{a}{b}"));
        } catch (ArgumentException) {
        }
    }
}

then I get 250 distinct RegionInfo. So there seem to be more than 100 RegionInfo that do not arise from a CultureInfo.

Also note that even though

new RegionInfo(CultureInfo.InvariantCulture.LCID)

throws an exception There is no region associated with the Invariant Culture (Culture ID: 0x7F), it is still possible (again, on my system right now) to do:

new RegionInfo("IV")

which creates an instance whose EnglishName is Invariant Country.