Introduction
More often than not, your web application users will be spread across many geographical regions, each region having its own way of displaying and handling text. A truly international web application should be authored in a way that all users, from different geographical regions, can work with the same level of comfort and see content that they can comprehend. This article is the first of the multi-part series which talks about how such applications can be written with .NET.
Creating an international application is by no means, a trivial task. Typically, for an application to serve localized content, it has to consider the following aspects:
- Labels & messages
- Date & Time formats
- Number and Currency formats
- Calendars
- Fonts
- Sorting and many other aspects
Microsoft, has published a fantastic, step, by step resource Globalization Step-by-Step which explains all of the above aspects with a lot of examples and explanation. This link clearly explains the meaning of terms, which developers get confused with and often use them interchangeably: Globalization and Localization. I would not like to re-iterate the content here, but what I have tried to do in this article is supplement this content with some ASP.NET specific implementation details and considerations.
.NET and globalization
The .NET framework provides a rich set of classes in the System.Globalization
namespace which make developing world-ready applications relatively easier. Chief among these classes is CultureInfo
which I will be primarily using. Before delving into culture, lets see what �Culture� is. Culture can be considered as a set of settings and conventions for a particular language and/or region. That is, it is the culture that determines date, time, currency formats etc. Cultures can be divided into three groups:
- Invariant: This is associated only with the English language but not with any region. Typically, this can be used for storing data in a culture agnostic way. Hence, it should not be used for user interface elements.
- Neutral culture: It is associated with a language, but not a region. It can be used only for language related but not Date, Time or currency formatting. For example, fr etc.
- Specific culture: Associated with a language and a region and hence, can be used for all types of formatting. For example, en-US, de-De etc.
CultureInfo
encloses a set of settings specific to a particular culture. The .NET framework provides the implementation of about 200 cultures and they can be enumerated by using the GetCultures
shared method. In this article, I am considering two aspects of developing world-ready applications: methods to select a culture and handling of labels and messages.
Selecting a culture
The first step would be to select a language (or culture) for which data has to be shown. For this, you can consider many options. One of them is to get the user's language preferences from the browser. This can be obtained by accessing the Languages
collection of the Request
object. For, instance, Request.Languages(0)
gets the user�s first preference (Languages
is a collection sorted as per the user�s preference). The figure shown below shows how this preference is set by a web application user in his browser. In the example shown below, the Request header will include the user's language preference, which is English (US), followed by Hindi.
The second option is to provide explicit options to the user as part of the user interface, like a combo box etc. This option may be better in situations when you are supporting only a limited set of cultures and restrict the user to select one of them. The code sample below creates a drop down list which gives the user an option to select a culture from all cultures supported by .NET:
For Each culture In CultureInfo.GetCultures(CultureTypes.SpecificCultures)
Dim item As New ListItem
item.Value = culture.Name
item.Text = culture.DisplayName
cboAllCultures.Items.Add(item)
Next
Our next step is to translate one of the above two options into specific CultureInfo
instances in our ASP.NET page. More specifically, we need to set the CurrentCulture
and CurrentUICulture
properties of the application thread. The CurrentCulture
property influences the display (or format) of date, currency, time, calendar etc. The CurrentUICulture
value determines the resources that need to be loaded by the Resource Manager. Typically, these resources are labels and messages required for user interface elements (I'll detail them in the following section). The code sample shown below sets the thread's CurrentUICulture
and CurrentCulture
values to that selected in the dropdownlist:
With Thread.CurrentThread
.CurrentUICulture = New CultureInfo(cboAllCultures.SelectedValue)
.CurrentCulture = .CurrentUICulture
End With
Labels and messages
This is one of the first major steps towards localization. It basically involves creating and displaying labels and messages in a particular language. To achieve the same, we would need to store text that is displayed by the application in separate files called resource files. A resource file can be considered to be a collection of labels, messages and other resources for a particular language. I would not be explaining much about resource files as I stumbled into an article, recently posted at CodeProject, which does just that: MultiLanguage Applications. Though this article considers localizing a Windows application, most of it would hold good for web applications too. Anyway here's the gist:
- Store resources in separate files (.Resx). One resource file would need to be created for each language (culture) supported.
- Create satellite assemblies from resource files. With VS.NET, this process is fairly simple. Satellite assemblies, by the way, are assemblies that contain only resources and no code.
- Use
ResourceManager
class to obtain specific resources at runtime.
Now, you might pop up a question - How are the right (culture-specific) resources to be picked up at runtime? Should we code for it? The answer is No, this is done by the ResourceManager
automatically and the CurrentUICulture
property of the thread is used for just that. The specifics of this process can be obtained here (MSDN). The next important question - What do we store in resource files? They can be labels accompanying the input elements like "First Name", "Address Line 1" etc, descriptive notes or text of any kind, page and table headers, window titles, image alternative text and so on. Essentially, all places where some text is shown to the user. That said, one of the other areas where this needs to be considered are error messages. These can be messages shown as a part of server validation/processing and also messages shown through client side scripting. Shown below are some example for the same:
Server side: Here, I am getting the error message for a RequiredFieldValidator
from the resource file.
rfvCity.ErrorMessage = rm.GetString("msg_MandCity")
Client side (Browser): This is slightly more tricky because all the messages have to be available in script. This can be facilitated by having global script variables to house the error messages. The code snippet shown below demonstrates this.
Dim sbScript As New StringBuilder
sbScript.Append("<script language="
sbScript.Append(Environment.NewLine)
sbScript.Append("var msgMandFName = '")
sbScript.Append(rm.GetString("msgMandFName"))
sbScript.Append("';")
sbScript.Append(Environment.NewLine)
sbScript.Append("</script>")
RegisterStartupScript("MessageBlock", sbScript.ToString)
A client script function using the message (a mandatory check in this case) may look like:
function submitForm()
{
if(document.forms[0].elements["txtFName"] == "")
{
alert(msgMandFName);
return false;
}
}
You can also include images in your resource files. This feature really comes handy when images itself contain culture specific content or text. Another example I came across in one of the samples was having images of country flags stored in the resource files! Going one step ahead, you can even serialize your objects which may have culture specific state into the resource file. There is no limit to what you can store in the resource file!
Finally, I would like to mention about a couple of considerations to think about when localizing ASP.NET pages. The implementation steps mentioned above is one where localization is made generic and hence one implementation (or page) can serve content of any culture. By virtue of using ResoureManager
and extra satellite assemblies, we incur a little overhead in terms of memory and processing time. On the other hand, we can do away with generic code and have copies of source files (one for each culture supported). This approach is actually used in many web sites. Here, the only overhead would be the development effort, but pages might serve a little faster.
Conclusion
Thus far, we saw just two aspects of developing international applications. I would like to say that we have just embarked on a rather long (and hopefully interesting) journey. In the forthcoming article, I'll be covering the other aspects like date, time, number formatting etc. Before concluding, I would like to mention some other good resources for internationalization: