Team LiB
Previous Section Next Section

Localization and Globalization in ASP.NET

Sitting at our desks and developing web applications, it's very easy for most of us to assume that the only language that will be used for the application is English. The truth is less short-sighted than that, however.

Even if your application server is hosted in an English-speaking country, that doesn't necessarily mean that all the users of your website will be native speakers of English. Language isn't the only cultural concern that developers need to consider at design time. Cultural locales dictate other things, such as the way that currency and date/time values are displayed. If you display 05-10-2003 to someone from the United States and 05-10-2003 to someone from England, they will more than likely disagree on the dates. The reader from the United States will think the date refers to May 10, 2003, whereas the reader from England will think that the date is referring to the October 5 of the same year. Ignoring issues like this can be disastrous. It is best to determine what level of support for globalization your application needs when you are designing it, because it is far more difficult to add support for this kind of architecture after your application has been released. The next section of this chapter will take you through various scenarios and solutions for creating globally aware ASP.NET applications.

Using Localized Resources

When most people think about globalization, they think about language first. A resource is any data that is available to an application, such as text, images, XML files, and so on. As you saw in Chapter 12, "Assemblies and AppDomains," the .NET Framework uses a hub-and-spoke model for locating and using resources. If you have resources in your application for Spanish (neutral) and English (USA), and the user of your application has her cultural information set to Spanish (Mexico), the hub-and-spoke model will allow the .NET Framework to locate the Spanish language resources and use those before using the default language resources.

Displaying Localized Content

In Chapter 12, you saw how to use a ResourceManager to get access to resources embedded within your assembly, whether they were custom embedded resources or contained within an assembly resource file, such as a string table.

Instead, this chapter discusses the architectural impact of designing an ASP.NET application for displaying localized content. There are several things you need to concern yourself with when dealing with localized content in a web page:

  • Displaying images

  • Displaying text

  • Displaying and querying data

It is very common for the cultural information to be stored in the URL; for example, http://somesite.com/content/en-us/article1.html. Duplicating your content for each location you support is definitely not something you want to do if you can avoid it. It creates a maintenance nightmare for both you and your translators. Your goal in creating a multicultural site is to support multiple cultures with a single code base. The concept of satellite assemblies enables you to do this easily if you put some thought into the design up front.

Displaying Localized Images

As with virtually everything in the .NET Framework, there are many different ways to accomplish any task. Displaying images with globalization in mind is no different.

One option, a brute-force style method, would be to create a different images directory for each supported culture and just retrieve images from the appropriate directory. Although this approach might seem appealing in that you don't have to write very much additional code, it would be a maintenance problem. In addition to being hard to maintain (every time you change an image, you have to change it in each culture's directory), you are duplicating quite a bit of data unnecessarily. Think about a typical content site. That site's graphics will usually contain dozens of images used to frame, highlight, box, and render content. These graphics are probably the same regardless of culture. Only images that have different significance in different cultures or that must be sized differently need to be changed with different cultures.

The preferred method is to use an assembly resource file. For each image, create a string that has the image's URL as a value. Because of the way .NET locates resources and its resource default fallback logic, you need supply only overriding resource names for images that vary by culture, such as a flag or other culturally significant icons.

Displaying Localized Text

Displaying globalized text should follow the same rules as images. Instead of duplicating your content in multiple places throughout your site, you should retrieve your text from resources. However, keep in mind that resources involve a decent amount of overhead. You should use assembly resource files to store things such as prompts, button labels, menu items, and so on. Don't use .resx files for things such as very large text items like articles, essays, and so forth. For large text or binary resources, you should use a system more suited to the storage and retrieval of large amounts of data, such as an RDMBS like MS SQL Server, Oracle, mySQL, and others.

A picture is worth a thousand words, so a screenshot is probably worth pretty close to that. Figure 25.1 shows the output of a website that uses a resource file to store the URLs of globalized images, as well as another resource file to store the captions for each image.

Figure 25.1. Screenshot of a simple application using localized image and text resources.


To create this application, create a new solution called CultureResource. To that solution, add a class library called CultureData and a website called CultureResource. One of the best things about the .NET Framework resource handling is that satellite assemblies make it possible for you to add additional resources to an existing application without having to recompile anything.

TIP

A common misconception when dealing with resource localization is that the programmer is the one who will be compiling the resources into the assembly. In reality, more often than not, an outsourcing firm will be given the task of translation, and the XML file for building resources. Because of this, make sure that all your resource files are in an assembly that has no other code contained within. This makes it very easy to ship off to a third party for translation or for additional language resources.


Add four resource files to CultureData: Strings, Strings.en-ca, Images, and Images.en-ca. Add a string called FlagCaption to both Strings (the default culture) and Strings.en-ca (English/Canada). These captions match what you saw in Figure 25.1. To the Images file, add a string called flag, with the image URL Images/usa.jpg. The Images.en-ca file got a flag string with the value Images/canada.jpg.

Finally, write the code shown in Listing 25.1 to utilize the new resources.

Listing 25.1. The Code for a Web Form Utilizing the New Resources
private void Page_Load(object sender, System.EventArgs e)
{
  // when using a ResourceManager, _ALWAYS_ remember the full namespace name
  // oh, and never use a hardcoded path like this :)
  Assembly cultureData = Assembly.LoadFrom(
  Server.MapPath("~/bin/CultureData.dll"));
  ResourceManager images = new ResourceManager("CultureData.Images",
    cultureData, null );
  ResourceManager strings = new ResourceManager("CultureData.Strings",
    cultureData, null );

  Response.Write( strings.GetString("FlagCaption") + "<BR>");
  Response.Write( 
    string.Format("<img src=\"{0}\"/><br>",
    images.GetString("flag")));

  CultureInfo ci= new CultureInfo("en-ca");
  System.Threading.Thread.CurrentThread.CurrentCulture = ci;
  System.Threading.Thread.CurrentThread.CurrentUICulture = ci;

  Response.Write( strings.GetString("FlagCaption") + "<BR>");
  Response.Write(
    string.Format("<img src=\"{0}\"/><br>",
    images.GetString("flag")));

}

The first step is to load the assembly. Because the assembly has no actual code in it, you can't use a shortcut and use the Assembly property of a Type; the assembly must be loaded manually. After the code has a reference to the resource assembly, two ResourceManagers are created: one for the images and one for the text captions. The rest of the code is pretty straightforward. It displays the default resource (the flag of the United States, in this case) and then changes the thread to indicate Canada (English) and displays that localized resource (the Canadian flag).

Using this model and architecture for localization, your application's support for multiple cultures will be robust and scalable, and provide a wide range of features for the consumers of your application.

Displaying and Querying Localized Data

So far you have seen code dealing with displaying information to the user in multiple languages and culture formats. You've seen some good ways to deal with localizing images as well as text. A more difficult task exists in providing localization services for large amounts of data and for data that exists in a database.

It is fortunate that you can provide the same kind of default fallback functionality that you get with resources against database structures like tables or stored procedures. You can provide a default stored procedure, and override it only for the cultures in which it makes sense to do so. Taking the previous model of storing URLs and text in resource files, you can also store the names of stored procedures and database tables in those resource files. For example, assume that you have a stored procedure called sp_GetLocalTaxInfo. This stored procedure works perfectly fine for 80% of your users, but the other 20% are from a country where the data in that stored procedure is incorrect. If you store the name of that stored procedure in a resource file as a string named "LocalTaxInfo", you can then override that string in a localized resource for cultures that might need to call sp_GetAlternateLocalTaxInfo.

You might be thinking that all of this sounds like a lot of work. The truth is that it is a lot of work. However, if you put in more effort up front, the rewards later on during the product's life cycle will be more than worth it. Assume you produce an application that follows the models and examples discussed earlier, and your architecture supports all facets of globalization. Now assume that your boss tells you that you need to get your application ready to support French and Spanish and you have only a short time period in which to do it.

If you followed the earlier advice, you could simply hand your .resx files and image directories to a translation company and have both the Spanish and French versions of your site completed simultaneously and still maintain only one single code base. Now picture the other alternative: You coded your site with no resources, everything defaulted to English, and no preparation done for globalization. It should be obvious where the payoff is in early investment in globalization.

"Out of the Box" Localization Functionality

In addition to you providing your own functionality as far as obtaining language strings, image references, and data references from resource files, ASP.NET (and the .NET Framework) provides quite a bit of localized functionality for you by default. This next section shows you a few of the elements for localizing your application that you get for free from ASP.NET.

Localized Dates

As you probably know, different cultures write dates in different formats. A United Kingdom date starts with the day, and a United States date starts with the month when displayed in short format. When you display the date in long format, .NET displays the name of the month in either abbreviated or full form. What happens if the viewer of that date is not from an English-speaking country? Fortunately, when you output date and time formats using .NET built-in methods, those formats automatically take into consideration the thread's current culture (not the current user interface culture).

Localized Currency Details

Currency is another thing that many developers take for granted. Too many developers will automatically just output a dollar ($) sign in front of a currency variable and think they're done. Each culture not only writes its currency in different ways (some use commas instead of decimal points), but they all have different names and symbols for their currency. By using the .NET built-in formatters for currency, you can be sure that the values you are displaying will be displayed in the appropriate way for the culture viewing the value.

To show how much built-in functionality .NET provides, add a few lines of code to the earlier resource sample. First, drop a placeholder control onto the Web Form, and then add the following lines of code to the end of the Page_Load event handler:

ci= new CultureInfo("hi-in");
System.Threading.Thread.CurrentThread.CurrentCulture = ci;
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
System.Web.UI.WebControls.Calendar cal = new System.Web.UI.WebControls.Calendar();
PlaceHolder1.Controls.Add(cal);
Response.Write(DateTime.Now.ToLongDateString() + " " + 
   DateTime.Now.ToLongTimeString());
Response.Write("<br>");
Response.Write( string.Format("{0:C}", 12.75) );

This code is pretty simple. Essentially all that's being done is setting the current thread's UI Culture and Culture to Hindi (India), and then using stock functions to display a date, a calendar, and some currency. Figure 25.2 shows the new output of the Default.aspx page.

Figure 25.2. The new output of the Default.aspx page, showing localized dates, calendars, and currency.


Localization and Time Zones

Another somewhat egocentric thing that many do as developers is store and display time without regard to time zone. If your website stores content and the time and date that the content appeared is importanthow does someone know that the content was added at 9 a.m. in their time zone, or 9 a.m. in the time zone of the host server?

One more thing that you can do to ensure that your users have the richest possible localized experience is to store all relevant times and dates in the GMT central time zone, and then simply convert the times when rendered to the time zone of the viewing user. You can do this by storing the user's home time zone in his profile in your database, in a cookie, or using some other means.

    Team LiB
    Previous Section Next Section