183

Is there a way of setting culture for a whole application? All current threads and new threads?

We have the name of the culture stored in a database, and when our application starts, we do

CultureInfo ci = new CultureInfo(theCultureString);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;

But, of course, this gets "lost" when we want to do something in a new thread. Is there a way of setting that CurrentCulture and CurrentUICulture for the whole application? So that new threads also gets that culture? Or is it some event fired whenever a new thread is created that I can hook up to?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Svish
  • 152,914
  • 173
  • 462
  • 620
  • 4
    If you are using resources, you can manually force it by: Resource1.Culture = new System.Globalization.CultureInfo("fr"); This way every time you want to retrieve a string, it is localized and returned – Reza S Nov 07 '11 at 19:24

10 Answers10

207

In .NET 4.5, you can use the CultureInfo.DefaultThreadCurrentCulture property to change the culture of an AppDomain.

For versions prior to 4.5 you have to use reflection to manipulate the culture of an AppDomain. There is a private static field on CultureInfo (m_userDefaultCulture in .NET 2.0 mscorlib, s_userDefaultCulture in .NET 4.0 mscorlib) that controls what CurrentCulture returns if a thread has not set that property on itself.

This does not change the native thread locale and it is probably not a good idea to ship code that changes the culture this way. It may be useful for testing though.

bluish
  • 26,356
  • 27
  • 122
  • 180
Austin
  • 2,771
  • 1
  • 18
  • 14
  • 14
    Be careful with this setting in ASP.NET applications. Setting the culture on the AppDomain will set the culture for all users. So it's not going to be good for english user to see the web site in german for example. – Dimitar Tsonev Mar 20 '15 at 15:58
  • 2
    I inherited an ASP.NET application that only works if all cultures are in en-US. This is a scenario where this setting is perfect for the time until that flaw is fixed – Daniel Hilgarth Jan 22 '16 at 14:00
  • I used to have a MVC filter where I set the culture on the current thread. But after upgrading from .NET 4.5 to 4.8 JSON serialization seems to be happening on a different thread where the culture was not the same as set in the filter. I'm sceptical if this solution would reliably work in a ASP.NET environment when you set the the DefaultThreadCurrentCulture in the filter too. – martinoss Oct 03 '22 at 12:05
36

This gets asked a lot. Basically, no there isn't, not for .NET 4.0. You have to do it manually at the start of each new thread (or ThreadPool function). You could perhaps store the culture name (or just the culture object) in a static field to save having to hit the DB, but that's about it.

dialex
  • 2,706
  • 8
  • 44
  • 74
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    kind of annoying... seems like you are right, hehe. So we do that now (and have the culture a static class), but we still have a problem with some threads that we do not have control over. Like processing threads in the microsoft report viewer. Found a work around though. Thank you for the info :) – Svish Jan 23 '09 at 11:17
  • 8
    This is really annoying :( It would be nice if there was a way to set the Current(UI)Culture automatically for your application and have all new threads pick up that value. – Jedidja Sep 03 '10 at 17:32
  • 1
    It's so long since I worked with this now so I don't remember exactly what we did. But I think it might have been that we instead of using dates in the dataset sent to the report viewer, we formatted the date into a string first, using the culture we had set in the application. Then it didn't really matter anymore that the report rendering threads used the wrong culture. So we didn't work around the problem with the threads using the wrong culture. We just worked around the problem of getting dates formatted wrongly :) – Svish Jul 01 '11 at 14:16
  • 2
    In our app, I tried "capturing" the threads (by calling a special method from certain places) and storing them in a collection for later use (in this case to set culture), which seems to be working to far. – Mr. TA Jul 01 '11 at 17:02
  • 1
    Couldn't you store the cultureinfo object in session and (if you're using masterpages) check for it in your masterpage codebehind and set the current thread? – user609926 Jul 12 '16 at 14:14
19

If you are using resources, you can manually force it by:

Resource1.Culture = new System.Globalization.CultureInfo("fr"); 

In the resource manager, there is an auto generated code that is as follows:

/// <summary>
///   Overrides the current thread's CurrentUICulture property for all
///   resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
    get {
        return resourceCulture;
    }
    set {
        resourceCulture = value;
    }
}

Now every time you refer to your individual string within this resource, it overrides the culture (thread or process) with the specified resourceCulture.

You can either specify language as in "fr", "de" etc. or put the language code as in 0x0409 for en-US or 0x0410 for it-IT. For a full list of language codes please refer to: Language Identifiers and Locales

Reza S
  • 9,480
  • 3
  • 54
  • 84
13

For .NET 4.5 and higher, you should use:

var culture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
Pang
  • 9,564
  • 146
  • 81
  • 122
Andreas
  • 3,843
  • 3
  • 40
  • 53
  • Apologize for maybe a dumb question, but where is supposed this code should be located? This force absolutely all app run under culture en-US? – MrBi Jun 21 '22 at 05:25
6

Actually you can set the default thread culture and UI culture, but only with Framework 4.5+

I put in this static constructor

static MainWindow()
{
  CultureInfo culture = CultureInfo
    .CreateSpecificCulture(CultureInfo.CurrentCulture.Name);
  var dtf = culture.DateTimeFormat;
  dtf.ShortTimePattern = (string)Microsoft.Win32.Registry.GetValue(
    "HKEY_CURRENT_USER\\Control Panel\\International", "sShortTime", "hh:mm tt");
  CultureInfo.DefaultThreadCurrentUICulture = culture;
}

and put a breakpoint in the Convert method of a ValueConverter to see what arrived at the other end. CultureInfo.CurrentUICulture ceased to be en-US and became instead en-AU complete with my little hack to make it respect regional settings for ShortTimePattern.

Hurrah, all is well in the world! Or not. The culture parameter passed to the Convert method is still en-US. Erm, WTF?! But it's a start. At least this way

  • you can fix the UI culture once when your app loads
  • it's always accessible from CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) will use your customised regional settings

If you can't use version 4.5 of the framework then give up on setting CurrentUICulture as a static property of CultureInfo and set it as a static property of one of your own classes. This won't fix default behaviour of string.Format or make StringFormat work properly in bindings then walk your app's logical tree to recreate all the bindings in your app and set their converter culture.

Peter Wone
  • 17,965
  • 12
  • 82
  • 134
  • 1
    Austin's answer already provided the insight that CultureInfo.DefaultThreadCurrentCulture can be set to change the culture of the AppDomain. CultureInfo.DefaultThreadCurrentUICulture is to CurrentUICulture as CultureInfo.DefaultThreadCurrentCulture is to CurrentCulture. There really is no reason to be getting the value directly from the registry in your code. If you want to change the CurrentCulture to some specific culture but retain the user overrides, then you just need to get the value from the CurrentCulture's DateTimeFormat's before you override it. – Eric MSFT Jan 23 '14 at 23:34
  • 1
    I will have to check but I seem to remember trying that without success, leading to getting it from the registry. Do you really think I would go to all that trouble for no reason? Registry access is a giant PITA in this brave new world of UAC. – Peter Wone Jan 24 '14 at 04:12
4

For ASP.NET5, i.e. ASPNETCORE, you can do the following in configure:

app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(new CultureInfo("en-gb")),
    SupportedCultures = new List<CultureInfo>
    {
        new CultureInfo("en-gb")
    },
            SupportedUICultures = new List<CultureInfo>
    {
        new CultureInfo("en-gb")
    }
});

Here's a series of blog posts that gives more information:

habakuk
  • 2,712
  • 2
  • 28
  • 47
Sean
  • 14,359
  • 13
  • 74
  • 124
  • 1
    I came searching for a way to do this in a legacy ASP.NET 2.0 site, and it's frustrating how easily I managed it with a different Core site in comparison! I'm in a multinational company, and for internal sites, all our formatting is en-US to prevent date confusion. But this legacy site is set up for en-US, en-CA and fr-CA. And in a "hack-y" way, so when I go to fix it, all of their type conversions blow up in the data layer! (French money values being "1 234,56 $" – Andrew S Oct 04 '17 at 15:28
  • 1
    @Sean your link is broken. Maybe it points to [this](https://www.jerriepelser.com/blog/how-aspnet5-determines-culture-info-for-localization/), [this](https://www.jerriepelser.com/blog/allowing-user-to-set-culture-settings-aspnet5-part1/) and [this](https://www.jerriepelser.com/blog/allowing-user-to-set-culture-settings-aspnet5-part2/) – Fer R May 29 '18 at 17:03
4

Working solution to set CultureInfo for all threads and windows.

  1. Open App.xaml file and add a new "Startup" attribute to assign startup event handler for the app:
<Application ........
             Startup="Application_Startup"
>
  1. Open App.xaml.cs file and add this code to created startup handler (Application_Startup in this case). The class App will look like this:
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            CultureInfo cultureInfo = CultureInfo.GetCultureInfo("en-US");
            System.Globalization.CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
            System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
        }
    }
ˈvɔlə
  • 9,204
  • 10
  • 63
  • 89
3

DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture are present in Framework 4.0 too, but they are Private. Using Reflection you can easily set them. This will affect all threads where CurrentCulture is not explicitly set (running threads too).

Public Sub SetDefaultThreadCurrentCulture(paCulture As CultureInfo)
    Thread.CurrentThread.CurrentCulture.GetType().GetProperty("DefaultThreadCurrentCulture").SetValue(Thread.CurrentThread.CurrentCulture, paCulture, Nothing)
    Thread.CurrentThread.CurrentCulture.GetType().GetProperty("DefaultThreadCurrentUICulture").SetValue(Thread.CurrentThread.CurrentCulture, paCulture, Nothing)
End Sub
Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
HCAxel
  • 69
  • 3
  • 1
    This one worked for me. Although using Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; Which looks the same didnt. For clarity this is being used in a WPF app where the app itself was changing its culture however usercontrols in other projects were using the browser culture not the culture selected by the user – chillfire Dec 08 '15 at 09:11
3

This answer is a bit of expansion for @rastating's great answer. You can use the following code for all versions of .NET without any worries:

    public static void SetDefaultCulture(CultureInfo culture)
    {
        Type type = typeof (CultureInfo);
        try
        {
            // Class "ReflectionContext" exists from .NET 4.5 onwards.
            if (Type.GetType("System.Reflection.ReflectionContext", false) != null)
            {
                type.GetProperty("DefaultThreadCurrentCulture")
                    .SetValue(System.Threading.Thread.CurrentThread.CurrentCulture,
                        culture, null);

                type.GetProperty("DefaultThreadCurrentUICulture")
                    .SetValue(System.Threading.Thread.CurrentThread.CurrentCulture,
                        culture, null);
            }
            else //.NET 4 and lower
            {
                type.InvokeMember("s_userDefaultCulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});

                type.InvokeMember("s_userDefaultUICulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});

                type.InvokeMember("m_userDefaultCulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});

                type.InvokeMember("m_userDefaultUICulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});
            }
        }
        catch
        {
            // ignored
        }
    }
}
polfosol ఠ_ఠ
  • 1,840
  • 26
  • 41
2

Here is the solution for c# MVC:

  1. First : Create a custom attribute and override method like this:

    public class CultureAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // Retreive culture from GET
            string currentCulture = filterContext.HttpContext.Request.QueryString["culture"];
    
            // Also, you can retreive culture from Cookie like this :
            //string currentCulture = filterContext.HttpContext.Request.Cookies["cookie"].Value;
    
            // Set culture
            Thread.CurrentThread.CurrentCulture = new CultureInfo(currentCulture);
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(currentCulture);
        }
    }
    
  2. Second : In App_Start, find FilterConfig.cs, add this attribute. (this works for WHOLE application)

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // Add custom attribute here
            filters.Add(new CultureAttribute());
        }
    }    
    

That's it !

If you want to define culture for each controller/action in stead of whole application, you can use this attribute like this:

[Culture]
public class StudentsController : Controller
{
}

Or:

[Culture]
public ActionResult Index()
{
    return View();
}
Meng Xue
  • 491
  • 5
  • 7