Sunday, February 13, 2005 - Posts

ASP.NET 2.0 Auto Localization with culture preference stored in Profile

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.

Firefox, Outlook Web Access and a full mailbox

I recently discovered that the e-mails I’d been sending last week never reached its recipients. The cause was that my Exchange mailbox was full and I wasn’t allowed to send any more mail until I cleaned it out. At the time I was out-of-office every single day, so I used the Outlook Web Access client. Firefox is by browser of choice, so even though the UI for “low level”* browsers such as Firefox has limited features, I choose to cope with it than the hassle of staring Internet Explorer just to read mail.

The trouble was I never got any error messages telling me that my mailbox was full so I thought that the mail I was sending actually got sent. It wasn’t, nor did it end up in my outbox nor the sent items folder, it just disappeared into oblivion.

If you’re expecting an email from me, please notify me and I’ll get back to you as soon as possible.

* Irony
The noun "irony" has 3 senses:
1. sarcasm, irony, satire, caustic remark -- (witty language used to convey insults or scorn; "he used sarcasm to upset his opponent"; "irony is wasted on the stupid"; "Satire is a sort of glass, wherein beholders do generally discover everybody's face but their own"--Johathan Swift)
2. irony -- (incongruity between what might be expected and what actually occurs; "the irony of Ireland's copying the nation she most hated")
3. irony -- (a trope that involves incongruity between what is expected and what occurs)
Source: WordNet