5

.NET Core/6/7 supports a globalization-invariant mode which alters the behavior regarding globalization in many ways.

How can a library detect if it is running in this mode and adjust its behaviour accordingly?

The only solution I came up so far is to use the fact, that since .NET 6 creating a culture which is not the invariant culture in this mode will (according to this document) throw a CultureNotFoundException.

bool IsGlobalizationInvariantModeEnabled()
{
  try
  {
    _ = new CultureInfo("en-US");
    return false;
  }
  catch (CultureNotFoundException)
  {
    return true;
  }
}

I do not like the solution because it abuses exceptions and also assumes that the "en-US" culture is always available if the globalization-invariant mode is not activated.

Is there a better way?

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Klaus Gütter
  • 11,151
  • 6
  • 31
  • 36
  • 2
    For the record, this is how [`Microsoft.Data.SqlClient` does it](https://github.com/JRahnama/SqlClient/commit/b5ec5777fd67b3d377efbbad2203883400130b18). Not saying that it's correct because it's MS (it hardly seems elegant...), but it's something. – Jeroen Mostert Jan 31 '23 at 15:13
  • 2
    @JeroenMostert This code does not seem to take into account the breaking change described in https://learn.microsoft.com/en-us/dotnet/core/compatibility/globalization/6.0/culture-creation-invariant-mode – Klaus Gütter Jan 31 '23 at 15:18
  • 2
    Indeed, this affects `CultureInfo.GetCultureInfo` as well as it does `new CultureInfo`. Great, we found a bug when we weren't even looking. :) (And it further cements that there's probably a need for an actual API for this flag, instead of forcing people to get complicated with getting switches and environment variables...) – Jeroen Mostert Jan 31 '23 at 15:22
  • @JeroenMostert will you submit bug for SqlClient? – Guru Stron Jan 31 '23 at 15:38
  • 2
    @GuruStron: as a matter of fact I [just did](https://github.com/dotnet/SqlClient/issues/1913). – Jeroen Mostert Jan 31 '23 at 15:42
  • 1
    @JeroenMostert great, wanted to mention it in the API suggestion =) – Guru Stron Jan 31 '23 at 15:43

1 Answers1

5

It seems there is no public API for this. You can try analyzing corresponding AppContext switch and environment variable:

var isInvariantGLob = GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");

static bool GetBooleanConfig(string switchName, string envVariable, bool defaultValue = false)
{
    if (!AppContext.TryGetSwitch(switchName, out bool ret))
    {
        string? switchValue = Environment.GetEnvironmentVariable(envVariable);
        ret = switchValue != null 
            ? (switchValue.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase) || switchValue.Equals("1")) 
            : defaultValue;
    }
 
    return ret;
}

Basically the same is done internally - in GlobalizationMode.

UPD

Created an API proposal to expose GlobalizationMode publicly.

UPD2

As explained in the discussion to the API proposal, this method is not actually reliable for AOT/trimming scenarios:

The switch is not reliable way to detect invariant mode in the presence of trimming. Trimming can hardcode the app to "always invariant mode" or "never invariant mode". The switch is ignored in that case.

So the exception approach similar to yours was suggested (though based on the discussion it has it's own edge cases):

private static bool IsGlobalizationInvariantModeEnabled()
{
    try
    {
        return CultureInfo.GetCultureInfo("en-US").NumberFormat.CurrencySymbol == "¤";
    }
    catch (CultureNotFoundException)
    {
        return true;
    }
}
Guru Stron
  • 102,774
  • 10
  • 95
  • 132