Set Culture for all threads in C# apps

Set the culture for all threads in C# before .NET 4.5

In the simplest form, to change the culture of .NET applications, we can just set the current thread Culture/UICulture during the initialization phase. But, problems can occur if the application is multi-threaded. Take a look at the example bellow.

class Program
{
    static void Main(string[] args)
    {
        var culture = new System.Globalization.CultureInfo("ro-RO");
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
        
        // execute method in this thread context (ro-RO)
        WriteDate();

        // execute method in new thread context (OS culture)
        Task.Factory.StartNew(WriteDate);
        Task.Factory.StartNew(WriteDate);

        Console.ReadLine();
    }

    static void WriteDate()
    {
        var threadId = Thread.CurrentThread.ManagedThreadId;
        var threadCulture = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine($"Thread {threadId} with culture {threadCulture} => {DateTime.Now}");
    }
}

The output will be:

Thread 1 with culture ro-RO => 13.06.2018 16:11:53
Thread 3 with culture en-US => 6/13/2018 4:11:53 PM
Thread 4 with culture en-US => 6/13/2018 4:11:53 PM

When the WriteDate() method is executed in the main thread, then it picks up the “ro-RO” culture defined there. But once we execute the method in a different thread, then the culture will revert to the OS culture, unless we specify it. So, in this case, one solution would be to set the culture for all threads.

class Program
{
    static void Main(string[] args)
    {
        WriteDate();

        Task.Factory.StartNew(WriteDate);
        Task.Factory.StartNew(WriteDate);

        Console.ReadLine();
    }

    static void WriteDate()
    {
        // Set culture each time method is executed, regardless of thread
        var culture = new System.Globalization.CultureInfo("ro-RO");
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;

        var threadId = Thread.CurrentThread.ManagedThreadId;
        var threadCulture = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine($"Thread {threadId} with culture {threadCulture} => {DateTime.Now}");
   }
}

The output will now be correct:

Thread 1 with culture ro-RO => 13.06.2018 16:18:40
Thread 3 with culture ro-RO => 13.06.2018 16:18:40
Thread 4 with culture ro-RO => 13.06.2018 16:18:40

Set the culture for all threads in C# in .NET 4.5.x

In .NET 4.5, the CultureInfo class was extended with some pretty useful features. We now have 2 properties that solve the threading issues of setting cultures:

  • DefaultThreadCurrentCulture
  • DefaultThreadCurrentUICulture

As stated in the .NET documentation, these properties  enable an application to define the default culture of all threads in an application domain. So, if we execute our app in .NET 4.5, we can modify it like this:

class Program
{
    static void Main(string[] args)
    {
        // Set default culture for all threads in app domain
        var culture = new System.Globalization.CultureInfo("ro-RO");
        CultureInfo.DefaultThreadCurrentCulture = culture;
        CultureInfo.DefaultThreadCurrentUICulture = culture;

        // Execute method on main thread
        WriteDate();

        // Execute method on differenmt threads
        Task.Factory.StartNew(WriteDate);
        Task.Factory.StartNew(WriteDate);

        Console.ReadLine();
    }

    static void WriteDate()
    {
        var threadId = Thread.CurrentThread.ManagedThreadId;
        var threadCulture = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine($"Thread {threadId} with culture {threadCulture} => {DateTime.Now}");
    }
}

The output is:

Thread 1 with culture ro-RO => 13.06.2018 16:26:49
Thread 3 with culture ro-RO => 13.06.2018 16:26:49
Thread 4 with culture ro-RO => 13.06.2018 16:26:49

Set the culture for all threads in C# in .NET 4.6.x

Starting with .NET 4.6, things have evolved even more. Basically, we can take the first code that we have written (for .NET 4 or bellow), execute it under .NET 4.6 and it will behave as expected. In .NET 4.6, culture now flows from one thread to another.

class Program
{
    // IMPORTANT: Execute in .NET 4.6 in order to make the culture 'flow' from main
    // thread to the others
    static void Main(string[] args)
    {
        var culture = new System.Globalization.CultureInfo("ro-RO");
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
        
        // execute method in this thread context
        WriteDate();

        // execute method in new thread context
        Task.Factory.StartNew(WriteDate);
        Task.Factory.StartNew(WriteDate);

        Console.ReadLine();
    }

    static void WriteDate()
    {
        var threadId = Thread.CurrentThread.ManagedThreadId;
        var threadCulture = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine($"Thread {threadId} with culture {threadCulture} => {DateTime.Now}");
    }
}

The output is:

Thread 1 with culture ro-RO => 13.06.2018 16:33:15
Thread 3 with culture ro-RO => 13.06.2018 16:33:15
Thread 5 with culture ro-RO => 13.06.2018 16:33:15

Further reading

If you want to learn all the nifty details that control the culture on threads in .NET apps, you can also check out the official documentation pages: