In this article you’ll examine the features that ASP.NET provides to help you build fully localized web applications. In particular you’ll see how .NET manages culture information, and how you localize different types of information?including images, content from a database, text such as copyright messages, and small pieces of information like numbers, currencies and dates. This article will give you a good grounding in the more interesting localization features of the platform.
How do you specify a culture unambiguously? When you say “French,” or “UK English” or “US English,” can you be sure you’re talking about the same language/culture as someone else? Thankfully there is an Internet standard called RFC 1766 that specifies codes for cultures. Here are some examples:
- en-GB?English?United Kingdom
- en-US?English?United States
- fr-FR?French – France
- zh-CN?Chinese – China
You can see that the first part (lower case) specifies the language, and the second part (upper case) specifies a country or region. There are more variants than are shown here, you can get much more information here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemGlobalizationCultureInfoClassTopic.asp.
Now that you can uniquely specify a culture, it’s interesting to know that Windows 2000 introduced the concept of a pair of culture settings; one called the Current Culture and the other is called the Current UI Culture, or the user interface (UI) culture. Although the two settings are generally the same, there are cases where it is useful for them to be different. For example if a user is British and using a UK English application (CurrentUICulture=en-GB) but wanted to see application data displayed in the French format (CurrentCulture=fr-FR) the two settings could be different.
- CurrentUICulture?specifies the culture for the user interface and is used for resource file lookups.
- CurrentCulture?specifies the culture for data and information. It is used to format dates, numbers, currencies and sort strings.
To see how these settings work, put these two lines into any ASP.NET page:
You’ll see that the output shows the values of the two culture settings. On my machine I see these:
You can change the CurrentCulture setting through the Control Panel, but the CurrentUICulture is set when the operating system is installed. Applications use the CurrentUICulture setting to render the user interface, and the CurrentCulture setting to display data and information (for example, dates, numbers and currencies). When you write code that localizes text, you must consider whether the information should be classed as part of the user interface or as data and use the appropriate setting. On my machine, the user interface is setting US English, displays data in UK English; however, on most machines the two settings will either be the same, or set to very similar cultures.
End-to-end Unicode support is another .NET feature that’s of great help to developers. The .NET Framework String classes all use Unicode, ASP.NET uses Unicode, and both SQL Server and Access both support Unicode.
To work with cultures, .NET provides a CultureInfo class that represents a particular culture. For example you can create a culture object representing UK English like this:
CultureInfo culture = new CultureInfo.CreateSpecificCulture("en-GB");
The CultureInfo class supports a variety of methods. For example, DisplayName displays its name in the culture’s language; EnglishName gives you the culture name in English; Calendar specifies which calendar is in use; DateTimeFormat holds settings for displaying dates and times; NumberFormat provides options for displaying numbers; and LCID returns the locale/region identifier for the specified culture (1033 for UK). You can set the CurrentCulture and CurrentUICulture objects using a CultureInfo object as follows:
System.Threading.CurrentThread.CurrentCulture = culture; System.Threading.CurrentThread.CurrentUICulture = culture;
In Web applications it’s often useful to set culture info in the Application_BeginRequest() method in the global.asax file. That method always runs first when an application begins, which ensures that the your application has already set the culture before any page code executes.
The first time a user accesses your site, you want the page to display the most appropriate of the supported cultures automatically. In other words, it’s convenient for the Web site to make a smart guess at the default language for a new user. Fortunately, most browsers provide a value in the HTTP request headers that gives you a list of cultures desired by the user, sorted in descending order of importance. In Internet Explorer, users control the list by selecting Tools, Options from the menu and then clicking the Languages button. That displays the Language Preference dialog (see Figure 1). The sample application compares the first language in the culture list sent by the browser to the list of cultures supported by the application, and if there’s a match, it selects that culture; otherwise, the site uses the default culture.
|Like many Web applications, performance is important for the sample application. Because the articles that make up the site change infrequently, you can easily cache article output by placing the following OutputCache directive in the article.aspx file.
Note that the OutputCache directive uses the VaryByParam attribute. That causes ASP.NET to cache the article.aspx output several times?once for each article identifier passed with the “id” parameter. The Duration attribute value of 600 means the cache retains each cached copy for ten minutes (600 seconds) before refreshing the article from the database. Serving the pages from cache takes a significant load off the database and provides faster performance.
However, there is a problem with this caching scheme. If you load an article and then change the language using the drop-down list, you’ll the same article. The VaryByParam setting maintains articles in the cache by the article ID, not by language. The VaryByParam setting maintains articles in the cache by article id, not by language, so changing the language has no effect, as the cache will return the article in whichever language was requested first.
The solution to this problem lies in the OutputCache directive’s VaryByCustom attribute. The standard use for VaryByCustom is to set it to “browser,” which directs the framework to cache a copy of the page each different web browser that hits the site?meaning you can deliver browser-specific page versions. However, in this case, you’re not interested in browser versions. Instead, you can use the VaryByCustom attribute to cache the articles once for each language. To do this, you need to override the GetVaryByCustomString() method in global.asax.cs, which is where processing of the VaryByCustom cache code occurs.
The GetVaryByCustomString() method returns a string, and the framework maintains a cache copy for each different string. So, to create a cached copy of each article in each culture, you just need to return a unique code for each culture?in this case the value of the CulturePref cookie.
To alter the way article.aspx caches pages, add the VaryByCustom attribute to the page.
If you now rebuild the application, refresh the page and change the language using the drop-down list you’ll see that the caching occurs for each language. The application now benefits both from localization and from ASP.NET’s output caching.
In a Windows Forms application, it would be natural to put images in resource files, but web applications typically serve images from image files using HTTP, so using resource files is not a viable solution; the server would need to write each image to disk as a GIF, PNG or JPEG before it could be served. For the same reason, you’re unlikely to store the images in a database. In this solution you’ll create a folder to store the images for each culture that the sample site supports. You’ll then use the localization code to dynamically assign the correct URLs. For example, the URLs for the French and UK English versions of the sample site differ.
To dynamically create these URLs , create another server control containing an HTML tag. Name the new control “Image” and create one private imageName member variable to hold the base name of the image. Create a public property called “Name” that gets and sets the imageName member variable’s value. At runtime, you want to be able to assign the tag’s href attribute value dynamically. To do that, override the CreateChildControls method and construct the control’s href attribute by concatenating the “images” folder to the culture name and the ImageUrl property. To create a French version for example, if the base imageName property contained “image.gif”, you would end up with the URL “/appname/images/fr-FR/image.gif”. Here’s the overridden CreateChildControls method.
Notice that the code uses properties for the width and height of the image, which were set by sizing the control in the designer. There are two limitations with this scheme. First, you must ensure an image is present across all the culture folders. Second, all the images for the cultures must be the same size.
In the article page, add two of these Image controls; one to display a flag relating to the culture, and the other to display a map of the geographic area identified with the culture.
Note that you use the Image control in much the same way that you use the tag?all you need to do is specify the image name and the dimensions of the image. To test the image localization, select a language from the drop-down list (see Figure 2) and then refresh the page in the browser to see the effect of the new language choice (see Figure 3).