Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Auto Selecting Cultures for Localization in ASP.NET


Localization in ASP.NET hasn’t changed much since the days of WebForms and ASP.NET 1.0 with some minor updates (for WebForms) in ASP.NET 2.0. Since then things have been rather quiet in regards to new localization features.

One of the things that just about any localized ASP.NET app needs to do is set the user’s locale, often based on the active browser language setting. There’s some limited support for auto-local switching built into .NET but this often doesn’t hit all the needs of a typical application scenario where more is required.

This post describes what’s ‘in the box’, and how you can create a simple custom solution that provides a bit more flexibility along with some discussion on how to best set the culture depending on your application requirements, whether its automatic culture detection and setting, or explicitly assigning cultures based on user preferences.

ASP.NET Native Support for Auto Locale Switching

ASP.NET has some native support for automatic locale switching via the <globalization> web.config section. The following setting can be made in web.config:

<configuration>
  <system.web>
    <globalization culture="auto:en-US" uiCulture="fr" />
  </system.web>
</configuration>

This setting automatically switches the ASP.NET request to the browser client’s language if a match can be found. If the browser doesn’t provide a language or the language can’t be matched to one of the .NET installed cultures, the fallback value is used – in this case en-US.

This setting applies the ASP.NET request thread’s CurrentCulture and UICulture. The culture is switched very early in the ASP.NET HttpApplication lifecycle, so you see the selected culture applied and available even in Application_BeginRequest and then throughout the rest of the request cycle.

Culture and UICulture

As a refresher, recall that a .NET Culture drives things like number and date formats, currency symbols, sort order, casing etc. – ie. it’s primarily geared towards formatting and converting things. UICulture on the other hand is what .NET uses for resource localization – if you’re using Resx resources or a custom ResourceManager or ResourceProvider (in ASP.NET) the UICulture is what affects which resources are selected for display.

WebForms

In addition to the global web.config settings that apply globally to all ASP.NET requests, WebForms Page objects can also apply Culture and UI culture on a per page basis, and use the same auto-culture detections. For example:

<%@ Page Language="C#" Culture="auto:en-us" UICulture="auto:en-us" %>

WebForms Pages also include an InitializeCulture() handler that can be overridden. It fires very early in the page cycle as a pre-init event that allows you to hook into the process of assigning a new culture before other code in the page runs and before the initial page tree and the controls within it are constructed.. InitializeCulture() can be used to override culture values set with the above attributes, or completely create a custom culture switching routine based on application logic.

Don’t use Page level Settings

Generally I’d advise against using Page level localization settings, to avoid missing non-Page resources in your application that might also need to be localized. For example, you might have a module that also produces localized output or error messages and if you use only page level localization only pages that have the attributes set localize properly. It’s better to use localization globally and ensure your entire application uses the same settings.

Auto-detecting and setting the Culture with Code

Today a lot of new ASP.NET applications don’t use WebForms so the above solution clearly doesn’t work for everything. Even if it did, the solution is very generic with very little control over the process if you need to customize how localization is applied in any way. In real-world applications the requirements for locale switching tend to be a bit more complex, involving switching only to certain supported languages and locales as well as overriding some of the common culture settings. I’ll come back to this in a minute, but first lets look at a routine that I use to switch my .NET Culture and UICulture in Web applications.

To make this work you need to:

  • Sniff the Browser’s language via Accept-Header using Request.UserLanguages in ASP.NET
  • Determine the locale to select
  • Set the Thread’s CurrentCulture and CurrentUICulture to the chosen locale

The HTTP Accept-Language Header

The Accept-Language header is sent by most browsers and looks something like this:

Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4

The header includes a list of languages that are defined in the browser’s language settings. Typically you’re only interested in the first one for setting the language as that’s the primary language. Although browsers send this automatically, keep in mind that non-browser HTTP clients typically don’t so if you’re checking for Accept-Language always assume it’s not present first, before using it.

In ASP.NET you can use the Request.UserLanguages[] property to retrieve the list of languages supported. You can check the UserLanguages[], ensure that it’s available and then read the first item:

culture = CultureInfo.InstalledUICulture.IetfLanguageTag;

if (HttpContext.Current != null && HttpContext.Current.Request.UserLanguages != null) { culture = Request.UserLanguages[0]; }

Thread Culture

You can use different values for Culture and UI culture. But typically you use the same values for both or specify a specific culture for the Culture and a generic non-specific culture for the UICulture – ie. en-US for culture, and en for UICulture – as generally the differences between regional versions of a given language are relatively minor and not worth customizing for (color vs. colour type of issues).

Cultures are applied on the thread level in .NET and ASP.NET assigns these culture settings on the active request thread which remains active for the lifetime of a typical ASP.NET request. The culture is accessible and can also be assigned manually like this:

Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture); // de-DE
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(culture); 
Posted in ASP.NET  Localization  

The Voices of Reason


 

Richard
March 30, 2014

# re: Auto Selecting Cultures for Localization in ASP.NET

Thanks for the article, Rick. Localization is the thing that is killing us the most in our current application being written.

However, I was actually wondering if you have the code snippet (or another blog posting) behind the "RequireSslBasedOnConfigSetting()" decorator on your controller in the MVC example.

Rick Strahl
March 31, 2014

# re: Auto Selecting Cultures for Localization in ASP.NET

@Richard - re:RequireSslBasedOnConfigSetting attribute. It's pretty simple - subclass RequireHttpsAttribute and override the OnAuthorize method so you check some config setting (in our case from the config file using my AppConfiguration class)...

public class RequireSslBasedOnConfigSettingAttribute : RequireHttpsAttribute
{
    public bool RequireSsl { get; set; }
        
    public RequireSslBasedOnConfigSettingAttribute()
    {
        RequireSsl = App.AdminConfiguration.RequireSsl;
    }
    public RequireSslBasedOnConfigSettingAttribute(bool requireSsl)
    {
        RequireSsl = requireSsl;            
    }
 
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext != null && 
            RequireSsl && 
            !filterContext.HttpContext.Request.IsSecureConnection)
        {
            HandleNonHttpsRequest(filterContext);
        }
    }

Richard
March 31, 2014

# re: Auto Selecting Cultures for Localization in ASP.NET

Rick - Thanks so much for this. It's crazy elegant. I'm currently using a crazy #DEBUG-IF block to do this check based on "Release" vs "Debug" building. I love this idea.

twomm
July 01, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

Hi Rick,

nice post, as usual!

I am wondering if you could give some input on the following. It is a bit more about routing, but related to the topic:
I am using MVC and have to support URLs, that contain the culture and the same URLs, that do not.
So basically, I have one route with "culture" in the URL and a matching route without culture in the URL, where I set the culture to a defaults "notset".

            routes.MapRoute(
                name: "Default",
                url: "{culture}/{controller}/{action}/{id}",
                defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional },
                constraints: new { culture = new CultureConstraint(Culture.enUS, ...) },
                namespaces: new[] { "..." })
                .RouteHandler = new CultureRouteHandler();
            routes.MapRoute(
                name: "DefaultCulture",
                url: "{controller}/{action}/{id}",
                defaults: new { culture = "notset", controller = "Default", action = "Index", id = UrlParameter.Optional },
                constraints: new { culture = "notset" },
                namespaces: new[] { "..." })
                .RouteHandler = new CultureRouteHandler();


On all my routes, I use a custom CultureRouteHandler. Within that in GetHttpHandler, if the culture is set to "notset", I try to get it from the accept-header, or use a system default one. In the end I overwrite
        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            ...
            // read culture if it is set to "notset" from routing
            ...
            requestContext.RouteData.Values["culture"] = culture;
            ...
            // set on CurrentThread
            ...
         }

with the desired culture.

Now my question is, can this have any implications on the further request pipeline, or is that approach fine?

Any thoughts would be much appreciated!

Thanks

Rick Strahl
July 01, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

@twomm - I think you should be fine because anything you do in the GetHandler happens before the MVC routehandler gets control of the request. I think you do want to try and set the culture/uiCulture as soon as possible in order to make sure that all aspects of the request like authentication get to use the current culture settings. THat's generally why you want to do things in the BeginRequest() cycle if possible.

But if you're changing culture based on the route you have no choice to do it until later, unless you create a module that looks at the URL and figures out the route culture earlier in the cycle (which wouldn't be difficult to do since you know what the culture Urls look like).

twomm
July 01, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

Yes, I will try the outlined approach and if I see any issues, I will use a module.
Thanks for your answer.

rudy
August 19, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

Give an example of how to get a string in the appropriate localized language from the resource file in the C# code based on the user's culture passed in with the HTTP request.

Rick Strahl
August 19, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

@rudy - that depends on what you are using. In most of ASP.NET you can use

HttpContext.Current.GetGlobalResourceObjects("resourceSet","resId")


https://msdn.microsoft.com/en-us/library/ms149949(v=vs.110).aspx

If you're using MVC or WebAPI you're likely using strongly typed resources.

William Dicks
October 15, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

We have come across strange behaviour in reports that run from our website.

I use
System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencySymbol
to get hold of the currency symbol for display in our reports. Yesterday while going through the different reports on the website, we had some reports displaying dollar ($) signs and others pound (£) signs. The website is hosted here in South Africa, and should have displayed the Rand (R) symbol. The webpage that handles the report viewer was set up with
Culture="auto" CultureUI="auto"


What I don't understand is why that would happen, since each page was requested by the same browser from the same hosting server?

What would be the correct way to do this?

Rick Strahl
October 15, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

@William - Auto culture uses the CLIENT browser to determine the culture. So you have users from other locales. If you want to always use a fixed locale (South African) remove the Culture commands from web.config or explicitly set them to the locale you want to use.

William Dicks
October 19, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

Thanks @Rick!

Our websites are connected to several different databases per client. Our clients are not South African. They are from Algeria, Libya, Ivory Coast, Burkina Faso, Uganda, Namibia, South Africa, etc. We host all the websites all on the same server.

What would be the best practice in terms of globalization and localization to ensure that each client gets to see their website with their own currencies in reports, etc? Would it be fine to set each of their cultures in their individual web.config files?

Rick Strahl
October 19, 2015

# re: Auto Selecting Cultures for Localization in ASP.NET

@William - I'm not really sure what you're asking. This post describes how you can limit the locales your application supports by checking the locale and matching it to a set you define. If it's anything else but those you fall back to a default locale (whatever that may be).

Donovan
February 22, 2016

# re: Auto Selecting Cultures for Localization in ASP.NET

I came across your post which was very informative.

I have a strange issue where my culture appears to be changing across the request. The details are http://stackoverflow.com/questions/35544639/prevent-globalization-culture-changes-across-request. I was wondering if you had any insights as to why this was happening. Any suggestions would be appreciated as I am really stumped on this one.

Shekhar
June 14, 2017

# re: Auto Selecting Cultures for Localization in ASP.NET

Hi,

This article is very much useful to understand the localization concepts, i have a requirement like i have 3 different projects (1 is Web project, 2 is MVC project and 3 is Wcf service project) and i would like to have a common Globalization project where i can manage the Resources and keys. Now the question is how i can use this common Globalization project in all those 3 projects????

Please suggest asap,

TIA, Shekhar


Rick Strahl
June 14, 2017

# re: Auto Selecting Cultures for Localization in ASP.NET

@Shekar - you can create a separate assembly that holds all the resources and exposes the resources publicly. You can then import the assembly in every project and access those resources in all projects. With strongly typed resources it's relatively easy to reuse resources in each project.


Shekhar
June 14, 2017

# re: Auto Selecting Cultures for Localization in ASP.NET

Thanks Rick for the quick reply, i tried the way, what you suggested but

in case of Web Project without physical resource files in the project (physical resource fiels in application root folder) its not working and how we can call/get the resource value based on key in Wcf project (this is a class library where donot have HttpContext)

HttpContext.GetGlobalResourceObject(resourceName, key)

Could you please provide the example, if any?

Thank you


Rick Strahl
June 14, 2017

# re: Auto Selecting Cultures for Localization in ASP.NET

If you use GetGlobalResourceObject() that won't work as that uses the ASP.NET Resource Provider and it only knows about resources in the ASP.NET project.

The only localization that works across projects is via strongly typed resources (or direct resource stream access).

+++ Rick ---

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017