2

I have an old API with switches the current culture in that way:

private void ChangeCulture(int lcid)
    {
        Debug.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] ChangeCulture Culture Start: {Thread.CurrentThread.CurrentCulture}");

        var newCulture = CultureInfo.GetCultureInfo(lcid);

        Debug.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Changing culture of currentThread to: {newCulture}");

        Thread.CurrentThread.CurrentCulture = newCulture;
        Thread.CurrentThread.CurrentUICulture = newCulture;

        // Old framework compatibility (not important for this example)
        CultureInfo.DefaultThreadCurrentCulture = newCulture;
        CultureInfo.DefaultThreadCurrentUICulture = newCulture;

        Debug.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] ChangeCulture Culture End: {Thread.CurrentThread.CurrentCulture}");
    }

Previously, the code was called from a synchronous context. However, since it is now required that the code is called from an asynchronous context. Like here:

private async void Button_Click(object sender, RoutedEventArgs e)
    {
        Debug.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}]Culture Start: {Thread.CurrentThread.CurrentCulture}");
        if (this.currrentLcid == 1031)
        {
            await Task.Delay(1000);
            this.ChangeCulture(1033);
            this.currrentLcid = 1033;
        }
        else
        {
            await Task.Delay(1000);
            this.ChangeCulture(1031);
            this.currrentLcid = 1031;
        }

        Debug.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}]Culture End: {Thread.CurrentThread.CurrentCulture}");
    }

Output:

[1]Culture Start: de-DE
[1] ChangeCulture Culture Start: de-DE
[1] Changing culture of currentThread to: en-US
[1] ChangeCulture Culture End: en-US
[1]Culture End: en-US

The culture flow with the async execution. But it doesn't flow globally back. If i call:

    private void CheckCulture_Click(object sender, RoutedEventArgs e)
    {
        Debug.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}]CheckCulture_Click: {Thread.CurrentThread.CurrentCulture}");
    }

Output:

[1]CheckCulture_Click: de-DE

Everything is MainThread. (ManagedThreadId=1)

.NET Framework 4.8

How can i switch the current culture globally without knowledge of the calling context?

Klappstuhl
  • 33
  • 5

1 Answers1

1

In .NET Core these properties are implemented as AsyncLocal<T>'s which in short means that any updates to them only affect the current asynchronous control flow, such as an async method.

Depending on what you are trying to accomplish, you may consider to create and set your own static CultureInfo properties.

On .NET Framework 4.6 and later, you could set the NoAsyncCurrentCulture switch to true in your App.config to get back the old behaviour:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
  </startup>
  <runtime>
    <AppContextSwitchOverrides value="Switch.System.Globalization.NoAsyncCurrentCulture=true"/>
  </runtime>
</configuration>

<AppContextSwitchOverrides> element

Retargeting Changes for Migration from .NET Framework 4.5 to 4.6

mm8
  • 163,881
  • 10
  • 57
  • 88
  • I had already tried the flag, but set it by code (in the Application_Startup). Somehow this did not work. When I set it via App.config it seems to work. Thanks. Now I have to think about whether this flag is the right solution, but that's how it is, decisions are sometimes hard. – Klappstuhl Sep 08 '20 at 06:44
  • Maybe it is better if I switch the culture in a dispatcher.invokeAsync In relation to this sentence: Apps affected by this change may work around it by explicitly setting the desired System.Globalization.CultureInfo.CurrentCulture or System.Globalization.CultureInfo.CurrentUICulture as the first operation in an async Task – Klappstuhl Sep 08 '20 at 06:48