Reputation: 2479
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:
What am I missing? Is there a better approach here? Thanks in advance!
Upvotes: 1
Views: 9888
Reputation: 3826
I tested out the code in the accepted answer inside Linqpad 7, which uses .NET 7. I got a crash running the code in the answer.
This code however, seems to work, slightly adjusted :
void Main(){
string countryCodeToCheck = "no"; //case insensitive comparison here against the "NO" region
bool isValidRegion = IsValidRegion(countryCodeToCheck);
Console.WriteLine($"Country code to check: {countryCodeToCheck} {isValidRegion}");
}
private bool IsValidRegion(string isoCountryCode)
{
var regions = CultureInfo.GetCultures(CultureTypes.SpecificCultures)
.Select(x => new RegionInfo(x.Name))
.OrderBy(x => x.Name);
return regions.Any(x => x.Name.Equals(isoCountryCode, StringComparison.InvariantCulture));
}
SpecificCultures being used here as suggested in the accepted answer. In additon, the property Name is sent into the constructor of RegionInfo, not LCID, which give crashes for some CultureInfo-s.
Upvotes: 0
Reputation: 62002
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
.
Upvotes: 0
Reputation: 199
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.
Upvotes: 4
Reputation: 92
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
Upvotes: -1
Reputation: 13980
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
Upvotes: 1