24

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);
}
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Jacek
  • 11,661
  • 23
  • 69
  • 123
  • 9
    "I don't want to use try-catch statement" -- *why*? This is a perfectly good use of try/catch. – Jon Dec 05 '12 at 13:00
  • When you expect to get a specific culture and it does not exist, then it is an *exception*. – Shiplu Mokaddim Dec 05 '12 at 13:02
  • 4
    @Jon and shiplu.mokadd.im - There are very good reasons to avoid try/catch. I came looking for an answer to this question because my application enumerates a subdirectory of my bin folder and tries to determine _if_ that directory name is a valid culture name, indicating that it contains satellite resources, so that the application can include valid cultures in a dropdown. This is not a case of expecting the value to be a culture. Now my debugger keeps breaking on this unnecessary exception. – DannyMeister May 29 '14 at 21:35
  • 1
    @DannyMeister: Sounds like you need to disable first-chance exceptions in the debugger. In your case I would also avoid try/catch, but that's because of the extra context you have provided. Without context there's no compelling reason to avoid it. – Jon May 29 '14 at 22:08
  • @Jon, agreed that without context it isn't as compelling. However, we almost always leave out much of the context of our questions as we can't relate the 10 year histories of our applications, the corporate politics that constrain us, etc., but maybe Q's should be more explicit. As far as breaking on first-chance exceptions... that is the most useful debugging tool I have ever come across! 90% of the bugs I encounter can be found right away by breaking on exceptions. If you don't, then your application's own exception handling will kick in obscure the issue. – DannyMeister May 30 '14 at 16:39
  • @DannyMeister: If your own exception handling *obscures* the issue then you are doing something wrong. Exceptions are thrown and caught internally in the BCL all the time. – Jon May 30 '14 at 16:46
  • @Jon, one method leaves your debugger at the site of a problem (or near it) while the other traces an error and continues on, but does not leave your debugger at the site of the problem. Which is easier to debug? As for the .NET library's propensity for throwing and catching exceptions frequently, shame. However, when debugging just our own code those _usually_ don't interfere. The main thing here that I want developers to do is to stop treating expected situations as exceptional situations. That comes down to a judgement call. I err on the side of performance and ease of debugging. – DannyMeister May 30 '14 at 20:30
  • @Jon, the author requirements on exceptions makes sense. There is overhead associated with throwing and catching exceptions. If it can be avoided then it should be. Now most unfortunately in this case it seems there's no other (efficient) way to do that other than catching `CultureNotFoundException`. – Crono Mar 14 '16 at 23:49
  • 1
    No you cannot, but also upgrading to .net 6 I found the CultureInfo constructor is not throwing the exception anymore. I tried the suggestions below but they are incomplete, I found many valid strings failing the check but that are valid when using new CultureInfo(""), one example is "no". The only way is to test the properties inside the returned CultureInfo generated using an invalid name. – Norcino Aug 05 '22 at 16:24

5 Answers5

38

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));
}
Crono
  • 10,211
  • 6
  • 43
  • 75
Steven S.
  • 724
  • 7
  • 10
  • 4
    I took the liberty to edit your code so that case will be ignored, since this is how `CultureInfo` usually works. – Crono Mar 14 '16 at 23:48
22

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;
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 3
    Catch the specific exception type – Aidan Ryan Feb 15 '13 at 07:41
  • @AidanRyan: Why? I surround a very specific part with a `Try-Catch` that raises an exception only when the given `cultureCode` or the `DefaultCultureCode` is `null` or not available. In this case it should fallback to `CultureInfo.CurrentCulture`. In my opinion exception type parameters would be just noise and cause the code to be less readable. Someone would assume that the exception is used somewhere or that it matters at all. – Tim Schmelter Feb 15 '13 at 07:47
  • 3
    Because you only want to catch `CultureNotFoundException` - the type does matter. There are plenty of other exceptions that can arise that you probably don't want to swallow here: `ArgumentNullException` or `OutOfMemoryException`, for example. Also, no one will think you're using the exception if you don't name the parameter -- `catch (CultureNotFoundException) { culture = CultureInfo.CurrentCulture; }`. It demonstrates your precise purpose and exactly what you expect to happen. – Aidan Ryan Feb 15 '13 at 08:00
  • 2
    @AidanRyan: Somehow missed the `CultureNotFoundException` ;) Ok, point taken, edited the answer. Thanks. – Tim Schmelter Feb 15 '13 at 08:16
  • 3
    Just want to point out that in 3.5 and earlier versions of .NET, the exception that gets thrown is indeed an ArgumentException. The CultureNotFoundException is only for 4+. source = [link](http://msdn.microsoft.com/en-us/library/yck8b540.aspx) – Rich Oct 18 '13 at 19:41
  • @AidanRyan OutOfMemoryException may not be so usual when getting a culture :) but can happen from other threads. (I guess that other threads get the exception though). More relevant would be ThreadAbortException ... –  Aug 04 '16 at 13:11
  • 5
    `CultureInfo.GetCultureInfo("asd");` won't throw an `Exception`. So this answer is false! – Dominic Jonas Mar 15 '18 at 06:52
  • The try-catch is slightly more efficient when the input is valid, but vastly less efficient otherwise: https://gist.github.com/concubicycle/b8aaaae4ddb6d8bd2d05bba0a1686cb1 . I would prefer a solution that doesn't use try-catch for control flow. – Sava B. Jan 15 '19 at 19:03
  • @DominicJonas As per MSDN, this behaviour change is only effective if running Windows 10 or later. – RobSiklos Mar 11 '19 at 18:02
  • 1
    @DominicJonas (and RobSiklos) is right! On newer versions of Windows, like Windows 10, the `CultureNotFoundException` is not thrown anymore. The OS is happy with any non-existing code. So the `try`-`catch` thing is not a good idea as of today. See thread [Invalid CultureInfo no longer throws CultureNotFoundException](https://stackoverflow.com/questions/35074033/). In Windows 10, the `.LCID` of the dubious `CultureInfo` obtained, seems to be always `4096`. Maybe you could rely on that? – Jeppe Stig Nielsen Oct 30 '19 at 10:09
13

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;
    }
}
Johan Larsson
  • 17,112
  • 9
  • 74
  • 88
  • This should be the preferred answer, as exceptions-as-flow-control should be avoided. – Sava B. Jan 15 '19 at 18:45
  • Thank you for this. Simple and effective – Smorkster Mar 08 '22 at 08:02
  • 'zh-MO', 'no', and more strings are valid for CultureInfo, when used as constructor parameter you get the right culture, so something is missing from this solution, but thanks – Norcino Aug 05 '22 at 16:10
3

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

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
0

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

Spi
  • 686
  • 3
  • 18