Localization
The best practice for creating multi culture web sites which mapped the Accept-Language headers to Thread.CurrentThread.CurrentCulture and/or Thread.CurrentThread.UICulture with ASP.NET 1.x was to set these properties before the request was handled; in an HTTP module or in a global.asax event.
void Application_BeginRequest (Object sender, EventArgs e)
{
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture (Request.UserLanguages[0]);
Thread.CurrentThread.CurrentUICulture =
Thread.CurrentThread.CurrentCulture;
}
ASP.NET 2.0 makes this much easier by allowing pages to be declaratively configured to map Accept-Language headers by setting the new culture attributes of the @Page directive or globalization element in web.config to “auto”. The following declarations are functionally equivalent to the code in the preceding example.
For a single page:
<%@ Page Culture=”auto” UICulture=”auto” %>
In web.config:
<globalization culture=”auto” uiCulture=”auto” />
Whereas you had to manually read resources from a ResourceManager and assign the resource values to controls within the page in ASP.NET 1.x, ASP.NET 2.0 allows declarative mapping of control properties to resources using <%$ … %> expressions.
Automatic culture mapping and declarative resource mapping makes developing world-ready ASP.NET 2.0 sites a breeze.
Profiles
The new profile service makes a brief task of storing user settings, such as preferred culture, persistently. At first glance the profile service resembles session state since both are designed to store per-user data, but they are different. While session state stores user data for a finite period, the profile service stores it “forever”.
Profiles are defined in web.config as shown below:
<configuration>
<system.web>
<profile>
<properties>
<add name="PreferredCulture" type=”System.String” />
<add name="FavoriteNumber" type="System.Int32" />
</properties>
</profile>
</system.web>
</configuration>
When you’ve defined a profile ASP.NET dynamically compiles that profile, provides strongly typed access to it and persists the profile data. As with session state, profiles work with both anonymous and authenticated users.
The profile is easily accessed through the Profile property which ASP.NET injects into all pages that derive from System.Web.UI.Page.
Storing preferred culture in a profile
Declarative localization in ASP.NET 2.0 is achieved through yet another new feature; expression handlers and builders. Expression handlers resemble data binding expressions, but they differ in significant ways. Expression handlers is a parse-time feature, whereas data binding expressions are evaluated when the DataBind() method is called. Since expression handlers are evaluated when the page is parsed, they are called before any of the page events occur. It is therefore too late to change culture properties of the thread in any of these events. Instead, you must either use an event handler in global.asax or an HTTP module. The following example shows how to read the PreferredCulture from the profile and set the UICulture property of the running thread:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
if (HttpContext.Current.Profile["PreferredCulture "] != null)
{
Thread.CurrentThread.UICulture = CultureInfo.CreateSpecificCulture(HttpContext.Current.Profile["PreferredCulture "] as string);
}
}
If you provide a link the user can click to change the culture, for instance ImageButtons with the flags for the available cultures, the users expect the culture to change at once. The following code resembles a typical click event handler:
private void Flag_Click(object sender, EventArgs e)
{
ImageButton button = sender as ImageButton;
Profile.Culture = button.CommandArgument;}
}
The trouble with this approach is that, since localization happens before any of the page events, localization has already occured done when the preferred culture is changed. There are two ways to workaround this, you can either force the page to be reloaded by making it redirect to itself or intercept the new culture preference when changing thread’s culture properties.
To do a redirect, simply add the following line to the above method:
Response.Redirect(Page.AppRelativeTemplateSourceDirectory, true);
To intercept a culture setting passed as query string parameter (“__SetPreferredCulture”) in your PreRequestHandlerExecute event handler, add the following lines to the top of the method:
string newCulture = HttpContext.Current.Request.QueryString["__SetPreferredCulture"];
if (newCulture != null)
{
HttpContext.Current.Profile.SetPropertyValue("PreferredCulture ",newCulture);
}
The localization and profile features in ASP.NET 2.0 are awesome. Even if there are you’ll have to jump through some hoops to create common multi-cultural features, such as language selection, you can still enjoy the huge benefits of ASP.NET 2.0s declarative model. After all, it’s much easier to create world-ready application with optional culture settings in ASP.NET 2.0 than in it’s this predecessors.