Jacek
Jacek

Reputation: 12053

How to check if culture exists in .NET

I have this code, when I try to get not existed culture I get exception.
Is there exists method like TryGetCultureInfo, which return bool value? I don't want to use try-catch statement

CultureInfo culture = CultureInfo.GetCultureInfo(cultureCode);
if (culture == null)
{
    culture = CultureInfo.GetCultureInfo(DefaultCultureCode);
}

Upvotes: 25

Views: 31371

Answers (6)

Dariusz Woźniak
Dariusz Woźniak

Reputation: 10350

tl;dr

The accepted answer is slow. You might use another solution for a better performance: CultureInfo.GetCultureInfo wrapped in a try-catch clause.

Suggested solution

Even though the try-catch is generally not a good idea, I would suggest using the CultureInfo.GetCultureInfo wrapped in the catch CultureNotFoundException clause anyway.

Most importantly, GetCultureInfo has a thread-safe (!) cached culture names, so there's no lookup on each call.

Code

public static bool Exists(string name)
{
    try
    {
        CultureInfo.GetCultureInfo(name);
        return true;
    }
    catch (CultureNotFoundException)
    {
        return false;
    }
}

Benchmark

I've made a benchmark to check which method is faster and GetCultureInfo is significantly (over 4000x) faster.

Result:

BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 11 (10.0.22621.2506/22H2/2022Update/SunValley2)
12th Gen Intel Core i5-12400F, 1 CPU, 12 logical and 6 physical cores
.NET SDK 7.0.403
  [Host]     : .NET 7.0.13 (7.0.1323.51816), X64 RyuJIT AVX2
  DefaultJob : .NET 7.0.13 (7.0.1323.51816), X64 RyuJIT AVX2


| Method                  | Mean         | Error        | StdDev       |
|------------------------ |-------------:|-------------:|-------------:|
| GetCultureInfo_TryCatch |     19.88 ns |     0.372 ns |     0.330 ns |
| GetCultures             | 83,831.36 ns | 1,638.645 ns | 2,599.065 ns |

Code for benchmark

[SimpleJob]
public class CultureExistsBenchmarks
{
    [Benchmark]
    public void GetCultureInfo_TryCatch()
    {
        try { CultureInfo.GetCultureInfo("zzz"); }
        catch (CultureNotFoundException) { }
    }
    
    [Benchmark]
    public void GetCultures() => 
        CultureInfo.GetCultures(CultureTypes.AllCultures).Any(culture => string.Equals(culture.Name, "zzz", StringComparison.CurrentCultureIgnoreCase));
}

Alternative approach

As an alternative approach, which is culture-agnostic, if you don't mind the dialect of the language, you may construct list of strings with ISO 639-2 language codes and make lookup for it.

Upvotes: 3

Spi
Spi

Reputation: 744

For whom it may interest, starting with .NET 5 this overload does exaclty that (efficiently calling Nls or Icu native API).

Upvotes: 0

Johan Larsson
Johan Larsson

Reputation: 17580

If you want it to be fast you can use:

internal static class Culture
{
    private static readonly HashSet<string> CultureNames = CreateCultureNames();

    internal static bool Exists(string name)
    {
        return CultureNames.Contains(name);
    }

    private static HashSet<string> CreateCultureNames()
    {
        var cultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures)
                                      .Where(x => !string.IsNullOrEmpty(x.Name))
                                      .ToArray();
        var allNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        allNames.UnionWith(cultureInfos.Select(x => x.TwoLetterISOLanguageName));
        allNames.UnionWith(cultureInfos.Select(x => x.Name));
        return allNames;
    }
}

Upvotes: 14

Steven S.
Steven S.

Reputation: 734

You could write a DoesCultureExist method returning a boolean value just like this:

private static bool DoesCultureExist(string cultureName)
{
    return CultureInfo.GetCultures(CultureTypes.AllCultures).Any(culture => string.Equals(culture.Name, cultureName, StringComparison.CurrentCultureIgnoreCase));
}

Upvotes: 38

Tim Schmelter
Tim Schmelter

Reputation: 460018

I think there's no such method. So you could just try-catch or check all installed cultures:

string cultureCode = "de-DE";
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures & ~CultureTypes.NeutralCultures);
var culture = cultures.FirstOrDefault(c => c.Name.Equals(cultureCode, StringComparison.OrdinalIgnoreCase));
if (culture == null)
{
    culture = cultures.FirstOrDefault(c => c.Name.Equals(DefaultCultureCode, StringComparison.OrdinalIgnoreCase));
    if (culture == null)
        culture = CultureInfo.CurrentCulture;
}

But i would prefer the try-catch, i'm sure it is more efficient.

public bool TryGetCultureInfo(string cultureCode, string DefaultCultureCode, out CultureInfo culture)
{
    try
    {
        culture = CultureInfo.GetCultureInfo(cultureCode);
        return true;
    } catch(CultureNotFoundException)
    {
        if (DefaultCultureCode == null)
            culture = CultureInfo.CurrentCulture;
        else
        {
            try
            {
                culture = CultureInfo.GetCultureInfo(DefaultCultureCode);
            } catch (CultureNotFoundException)
            {
                culture = CultureInfo.CurrentCulture;
            }
        }
    }
    return false;
}

Upvotes: 23

Daniel Pe&#241;alba
Daniel Pe&#241;alba

Reputation: 31847

No, AFAIK is not possible. You can check first if the culture exists and in that case get it.

The following code shows how to do it:

    private static CultureInfo GetCulture(string name)
    {
        if (!CultureExists(name)) return null;

        return CultureInfo.GetCultureInfo(name);
    }

    private static bool CultureExists(string name)
    {
        CultureInfo[] availableCultures =
            CultureInfo.GetCultures(CultureTypes.AllCultures);

        foreach (CultureInfo culture in availableCultures)
        {
            if (culture.Name.Equals(name))
                return true;
        }

        return false;
    }

Hope it helps

Upvotes: 3

Related Questions