Saturday, July 4, 2009

ASP.NET Changing the active culture

In a previous chapter, we had a look at the CultureInfo class, and we briefly discussed how to change it for a page. However, since changing the current culture is so essential, this chapter will contain an in-depth look of the different ways to accomplish this.
Automatic
Both Culture and UICulture is automatically set based on the browser settings of your visitor. Try running the following page:




<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>CultureInfo demo</title>
</head>
<body>
<form id="MainForm" runat="server">
<% Response.Write("Your current culture: " + System.Globalization.CultureInfo.CurrentCulture.DisplayName); %>
</form>
</body>
</html>




Now, access your browsers language settings:

Internet Explorer: Click the Tools button, select Internet Options, then click the Languages button.

Firefox: Click the Tools menu, select Options, select the Content tab and then click the Choose button in the Languages group.

Add another language, and then move it to the top of the list. Close the dialog and reload the page (F5). You will now see the name of the culture matching your newly selected language.
The Page directive/class
As we saw in the previous chapter, we can simply define both Culture and UICulture by accessing the properties in the Page directive:




<%@ Page Language="C#" Culture="en-US" UICulture="en-US" %>




Since the Page directive is just a shortcut to the Page class, this can be done from CodeBehind as well. However, we have to do it at a certain point, before the page is being rendered, to make sure that it has the desired effect. This is where the InitializeCulture() method comes into play, a method that is called by ASP.NET pretty early in the Page life cycle, which you can override:




public partial class CultureInfoTest : System.Web.UI.Page
{
protected override void InitializeCulture()
{
Page.Culture = "en-GB";
Page.UICulture = "en-GB";
}

protected void Page_Load(object sender, EventArgs e)
{
Response.Write(Page.Culture);
}
}




You should be aware that the Page.Culture and Page.UICulture properties them self are merely shortcuts to the System.Threading.Thread.CurrentThread.CurrentCulture and System.Threading.Thread.CurrentThread.CurrentUICulture.
Globally
You can set the Culture and UICulture for all your pages, via the web.config file. Use the globalization node, which is a child node of the system.web node, like this:




<globalization uiCulture="en-US" culture="en-US" />





If needed, you can still override this setting on individual pages.

ASP.NET Localizing the CodeBehind

In the previous chapters, we only used localization for the markup part of the webpage, but localizing your strings in Code Behind is just as simple. Here are the techniques for doing this, with some sample code to get you started.
Direct access
Since the .NET framework takes your global resource files and turns them into strongly typed classes, you can actually reference them as such. For instance, if you have a global resource file called MyGlobalResources.resx, with a resource row with a name of HelloWorldString, you can access it like this:




lblHelloWorld.Text = Resources.MyGlobalResources.HelloWorldString;




GetLocalResourceObject()

With a call to GetLocalResourceObject(), you can get a specific row from your local resource file. It is returned as an object, but you can always call the ToString() method on it. For this to work, you have to be within a file that has a local resource file. To get a row from it, simply specify the name of the row as a parameter to the method, like this:




lblHelloWorld.Text = GetLocalResourceObject("lblHelloWorld.Text").ToString();




GetGlobalResourceObject()

The GetGlobalResourceObject() method works pretty much like the local version described above, but for global resources instead. You have to specify an extra parameter though, to tell ASP.NET in which global resource file you want to look for the row in, so the first parameter is the name of the resource class generated from the resource file, while the secondary parameter is for specifying the name of the row you're looking for, like this:




lblHelloWorld.Text = GetGlobalResourceObject("MyGlobalResources", "HelloWorldString").ToString();




Which approach to use mostly depends on the situation and what you prefer, but the first one does have a big advantage: Since it's strongly typed, the compiler will alert you if the row you are trying to retrieve no longer exists, allowing you to catch any problems before they go into production.

ASP.NET Implicit & Explicit localization

Implicit localization

Implicit localization is used within your markup, and its main advantage is that it allows you to localize several properties of the same control/object, without explicitly referencing each property. In our Localized Hello World chapter, we used it, and saw how we could set the meta:resourcekey property of a control, and then automatically have its Text property mapped to the resource row in our resource file. Let's expand a bit on that example, to have several properties localized. If you haven't already done so, you should read the Localized Hello World chapter now and create the project from it, as the following example uses it:




<asp:Label runat="server" ID="lblHelloWorld" Text="Hello, world!" Font-Names="Verdana" ForeColor="Blue" meta:resourcekey="lblHelloWorld" />




In the three resource files we created (default, Germand and Spanish), you can now add two extra rows to each of them, with the names "lblHelloWorld.ForeColor" and "lblHelloWorld.Font-Names", and then define different values for it. For instance, the default label color could be blue, the German version could be green and the Spanish version could be red, and you could define different font names for each of them as well. This makes it really easy to localize e.g. controls, because we only have to define the name of the resource row - each property is automatically mapped.

Explicit localization

With explicit localization, you pick out a specific resource row from your resource file, which is returned to you, and while implicit localization can be helpful when localizing webcontrols and other declarative objects, explicit localization is the only possible way of doing things in any other circumstance. Let's see how the above example would look with explicit localization:




<asp:Label runat="server" ID="lblHelloWorld2" Text="<%$ Resources:lblHelloWorld.Text %>" Font-Names="<%$ Resources:lblHelloWorld.Font-Names %>" ForeColor="<%$ Resources:lblHelloWorld.ForeColor %>" />




We simply re-use the resource rows from our previous example, and as you can see, the markup for explicit localization is a bit more complicated and verbose than for implicit localization. The syntax for retrieving a resource row is like this:




<%$ Resources:[resource class name,]RowName %>





As you can see, we didn't specify a resource class name in our example, because we use a local resource. If you use a global resource, you should specify the name of the class it results it, which as a rule of thumb is the same as the filename, but without the .resx extension. So for instance, if you have a global resource file with the name of MyGlobalResources.resx, you would obtain a resource from it like this:




<%$ Resources: MyGlobalResources,NameOfRow %>




As simple as that.

ASP.NET Local & Global resources

When localizing your ASP.NET websites, you can store your resources as either a local or a global resource. A local resource is specific to a certain page, which is the only one who can access it, while global resources can be accessed from anywhere.

Local resources are kept in the special App_LocalResources folder, while global resources are kept in the App_GlobalResources folder. Local and global resource files will look exactly the same, so the only apparent difference, is the folder they're placed in. However, they are used differently. For instance, with local resources, you need a resource file for each of your pages, and an extra one of each of the desired languages. So if you have a website with 10 pages, each localized into two other languages, besides the default language, it will amount to 30 resource files. With global resources, you would only need (but not be limited to) one file per language, no matter how many pages you have. On the other hand, each of these files could get pretty hard to manage, if your site has lots of localized content.

You will likely always need some global resources, but if you prefer, you may skip the local resources and only use global ones. It's really up to you.

ASP.NET The CultureInfo class

When it comes to localization of your application, especially one class is of great importance: The CultureInfo class from the System.Globalization namespace. From this class, you can get information about pretty much every possible culture out there, including a wide range of culture specific settings. The CultureInfo class can really help you when localizing your webpages, most notably because ASP.NET will keep a reference to the specific CultureInfo instance related to a specific visitor. Does it sound complicated? Let me show you a little example:

Try running this and you will get a culture related to you - but how? Actually, since the Culture property of the Page directive is set to Auto, this is controlled by your browser. Most browsers allow you to set your favorite languages, and from that, ASP.NET tries to detect which culture to use for you. We will look into that in greater detail later on. Your current culture is used in lots of cases, for instance when formatting dates and numbers, since that varies from culture to culture. Now, you may want to use a specific culture, no matter what your visitors browser tells you. In that case, you can set the Culture property of the page, like this:



<%@ Page Language="C#" Culture="en-US" %>




If you run the example now, you will get the output "English (United States)", which is the language of English, with a specific US culture. English comes in other flavors too, for instance British or Australian. en-US is considered a specific culture, where en (just English) is considered a neutral culture, since it's just a language, not specific to any country. Now, to see the difference between two cultures, try running this example:




<%@ Page Language="C#" Culture="en-US" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>CultureInfo demo</title>
</head>
<body>
<form id="MainForm" runat="server">
<% Response.Write("Current date, in a culture specific format: " + DateTime.Now.ToString()); %>
</form>
</body>
</html>



Now, try changing the page Culture to a German one, like this:




<%@ Page Language="C#" Culture="de-DE" %>






You will see the exact same line of code output a different result, because it's culture dependant. But you can actually output a culturally aware date (or number or anything else) without changing the culture of the page. For instance, like this:




<% Response.Write("Current date, in a culture specific format: " + DateTime.Now.ToString(System.Globalization.CultureInfo.GetCultureInfo("de-DE").DateTimeFormat)); %>




Culture vs. UICulture
This was all about the Culture property, but the Page class does come with another related property: The UICulture property. UI stands for User Interface, so in fact, this is the property we can use to identify the language used for the visual part of the page, while the Culture property is used for the non-UI stuff, like date and number formatting, sorting and so on. In lots of situations, you would want to use the same Culture and UICulture, but there are cases where you would want to separate these two, for instance when you want the text of a website to be localized, while still outputting a consistent date or number format, no matter where the user comes from.


We simply get a reference to a German culture, and then use it as a parameter to the ToString() method on the DateTime class. CultureInfo comes with a NumberFormat property for formatting numbers too. Obviously, formatting dates and numbers is just a small part of localizing an application, but the CultureInfo class is (or at least can be) the foundation of this process, mainly because it's so well integrated with the .NET framework, and in particular ASP.NET.

ASP.NET Example in Localization

ASP.NET Hello, localized world! Example in localized

With the knowledge acquired from the previous chapters, let's try actually localizing some text, to see that it works. You should create a new project in Visual Web Developer or Visual Studio. You can use the default page added, or create a new one for the purpose. The content should look something like this:




<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Demo</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label runat="server" ID="lblHelloWorld" Text="Hello, world!" />
</form>
</body>
</html>




If you run it, you will get the good old "Hello, world!" message, in plain English as expected. Now let's add some localization magic to it. We will use local, implicit localization for this - both are new terms, which will be explained later on. For now, just right click on your project and from the Add ASP.NET folder sub menu, select App_LocalResources. This is the folder where we place local resources, each of them specific to a file in your project.

Now, right click this new folder, and add a new item. This should be a Resource file, and the name should be the same as your page, but with a .resx extension. For instance, I called my page Test.aspx, so my Resource file should be named Test.aspx.resx. This part is important - if we want ASP.NET to automatically map a resource file to a specific ASP.NET page, the names should be like this. This is our default resource file, used to keep the default version of our strings. Let's add a couple of other languages. Once again, the filename is used to map the resource file, so for instance, to add a German language file, the name should be .aspx.de.resx, or in the culture specific version: .aspx.de-DE.resx

I have added Test.aspx.de-DE.resx and Test.aspx.es-ES.resx, to translate the page into German and Spanish. Then I add a new row to Test.aspx.resx, with the name lblHelloWorld.Text. In my project, English is the default language, so I give this row a value of "Hello, world!". I then open Test.aspx.de-DE.resx, add a row with the same name as before, and set the value to "Hallo, Welt!". I do the same for Test.aspx.es-ES.resx, where I set the value to "Hola, mundo!". Your three resource files should now all have a row with the name of "lblHelloWorld.Text", and a localized version of the Hello world string.

Now, go back to our ASP.NET page and use the meta:resourcekey property on our Label control, to make it use our resource string. It should look like this:




<asp:Label runat="server" ID="lblHelloWorld" Text="Hello, world!" meta:resourcekey="lblHelloWorld" />



As you can see, I've used the same string as for the ID of the control. You probably remeber that we added a resource row with the name of "lblHelloWorld.Text". This corresponds to a control with the resource key of "lblHelloWorld", mapped to the Text property of this control. Now, try setting the UICulture property on your page and run the example:




<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" UICulture="de-DE" %>




The label is now in German. Change UICulture to "es-ES" and reload the page. It's now in Spanish. Then try changing it to something completely different, like fr-FR, and you will see our default language used instead, simply because we don't have a localized version of the string in French.

This was a simple example, to show you how it can work, but you need a bit more information about HOW it works. In the next couple of chapters we will look into local and global localization, as well as implicit and explicit localization. First up is the CultureInfo class though, since it's used heavily when doing localization

ASP.NET localization

ASP.NET localization

According to Wikipedia, "Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text", and that is exactly what we'll look into in the next couple of chapters. Offering your website in other languages than the default one can be a big job, but fortunately, ASP.NET makes the process a lot easier.

There are quite a few concepts that you need to know about, right from the CultureInfo class to the concepts of local and global resources, as well as implicit and explicit localization. In the next couple of chapters, we will look into all of that, but first, let's see it in action, in our very own "Hello, localized world!" example.