Localization in Applications...

Applications that reach to an international level have been on the rise. The reason is obvious; dot com businesses can increase revenue share or can increase their user base if they reach out overseas.


By: Brian Mains Date: November 13, 2004 Download the code.

Applications that reach to an international level have been on the rise. The reason is obvious; dot com businesses can increase revenue share or can increase their user base if they reach out overseas. There are plenty of examples of this: http://www.google.com, http://www.dell.com, or http://www.harley-davidson.com. Most sites implement globalization in a different way. Some have a drop-down list of all the cultured sites, some redirect based on language settings in the browser, and some provide a list of links for sites available, usually at the bottom of the site or on another page.

Localization in .NET 2.0 web applications really has come a long way, but before we get into it, resource files must be discussed first. Resource files are XML-based files that store text to be "localized". This is not their only feature; however, the topic of localization uses resource files in this manner only for this article. Just because it is XML doesn’t mean resource files are limited to textual data; there are actually six categories of data. Other than the first and most obvious, the string category, resource files can contain images, icons, audio, files, and other (which is the actual name) types of data, such as Booleans, integers, etc. When editing resource files directly, Visual Studio .NET 2005 beta 1 has a resource editor that allows the developer to setup the resource file definitions visually. This editor has three columns and is shown below:

In the beta 1 editor, it is important to understand a little about the editor. When copying and pasting data into the value column, do not enter it in using Ctrl + V, even if the cell is selected. Remove the default text in the cell (if no text exists, just select the cell), right-click it, and select the Paste option. When using Ctrl + V, the pasted text adds another resource entry in the Name column. Also, when pasting data in, you may notice the cell is blank. Usually the editor adds a new line, which backspacing once will remove.

Generating resource files are a snap. Open up an ASP.NET page in design view. Go to the Tools menu, and select "Generate Local Resource". Resource files are create one per page, and are placed in the LocalResources folder.

To vary resource files by language, each file must have the language code embedded in the name. For example, the resource file for the German culture will have a “de” language code embedded in the name, in the format of Default.aspx.de.resx. Each language has a culture code. For example, English has a culture code of "en", Spanish has a culture code of "es", and China has a culture code of "zh". These codes are based off of the culture's language name, and are referred to as the neutral language. Culture codes are region and locale specific, in the format of xx-XX. For example, the United States has a culture code of en-US.

For an example application that has a default.aspx page, this page would have many resource files. Their names would be "default.aspx.resx" (the default), "default.aspx.de.resx" (for German cultures), "default.aspx.zh.resx" (for Chinese cultures), and "default.aspx.es.resx" (for Spanish cultures). In this scenario, any other language is considered the default. The default is typically based on the originating culture of the company or organization the site belongs to. When generating resource files using the instructions above, the properties available to be localized are automatically placed in the resource file and linked to the ASP.NET page. This feature makes it a lot easier to create an international web site.

Culture information is usually established in the browser, which is the case for Internet Explorer. You can view the acceptable languages in the Internet Options menu. Click the "Languages" button at the bottom, and view the languages that are acceptable for the user. The language at the top of the list is the "preferred" language. Instructions for other browsers are attached at the end of the article.

The language preferences are passed through using an HTTP header. This header contains the language code (region and the locale) that the user resides in. This setting is established by the browser. It is important to note that this is often setup by the company manufacturing the computer and setting up the Operating System. This also takes into account that the user is using his/her private computer. If the computer is a public computer, it may be set for a culture that isn’t appropriate for the individual user. It is always a good idea to allow the user to change region/locale within the application, if possible.

Going back a step to the resource files example from above; those resource files named previously are considered "local" resources. Local resources are individualized per page and aren't used in another ASP.NET page outside of the "default.aspx" page. Local resources can be assigned to an ASP.NET page by adding a meta:resourcekey attribute to the desired ASP.NET control. For example, below is a label that is linked to a resource string entry in the local resource file:

<asp:Label id="lblAddress1" runat="server" Text="Address Line 1:" meta:resourcekey="LabelResource2"></asp:Label>

What does the resource file entry look like for LabelResource2? Below are entries for LabelResource2 in the English and German resource files (in the same order):

<data name="LabelResource2.Text">
  <value xml:space="preserve">Address Line 1:</value>
</data>

<data name="LabelResource2.Text">
  <value xml:space="preserve">Adresse Linie 1:</value>
</data>

An alternative approach to using the meta:resourcekey attribute is using the new binding syntax for resource files. You may be familiar with databinding in ASP.NET 1.x, where you used an expression that looked like <%# Databinder.Eval(Container.DataItem, “FieldName”) %>. This is more of an "inline" approach. The meta:resourcekey is used for properties that declare the Localizable attribute, which identifies that the property can be the target of an assignment using local resource files. When generating a local resource file for an ASP.NET page, only the properties declaring this attribute will be added to the resource file. The binding statement, however, forces the property to accept a local resource binding. It uses a slightly different syntax, using the <%$ %> notation. An example is shown below. Note the "Resources:" prefix. In ASP.NET 2.0, you can also bind connection strings in the same manner, using this syntax.

<%$ Resources:RegularExpressionValidatorResource1.RegularExpression, "[0-9]{5,}" %>

The string entry named RegularExpressionValidatorResource1.RegularExpression was added to the local resource files, and is linked to a regular expression validation control using the expression above. The complete validator control declaration is shown below:

<asp:RegularExpressionValidator id="revZipCode" runat="server" ErrorMessage="Please enter a valid Zip Code" Display="Dynamic" ControlToValidate="txtZipCode" SetFocusOnError="True" ValidationExpression='<%$ Resources:RegularExpressionValidatorResource1.RegularExpression, "[0-9]{5,}" %>' meta:resourcekey="RegularExpressionValidatorResource1">
</asp:RegularExpressionValidator>

As you see, we've forced the regular expression validator to accept a custom validation expression based on the culture. This allows us to validate the zip code differently based on the culture.

The resources we’ve specified have been local resources so far. In addition to this type of resource, shared resources are more global to the application and are defined in the Resources folder. You can access a shared resource through the My keyword. My is a new keyword that permits quick access to common objects used in the framework. To illustrate a shared resource, let's use one named AppInfo.resx. AppInfo becomes the class name for the resource. Let's add a property named Title, which is a string. To access the value of this property, use the following code:

My.Resources.AppInfo.Title

Shared resources use the same editor and same naming scheme to vary files by culture. Shared resources can also be declared "inline", as local resources can. Shared resources add one parameter to the binding statement: the name of the class (AppInfo, in the example above). The file AppInfo.resx declares an AppInfo class, which needs to be provided in the class name section.

The following is the HTML for the controls declaring a shared resource. Note that there is three properties. The third string value is a default value that is only shown in the designer.

<asp:Localize ID="locTitle2" Runat="server" Text='<%$ Resources:AppInfo, Title, "Company Name" %>' />
<asp:HyperLink ID="lnkContact" Runat="server" ForeColor="White" NavigateUrl='<%$ Resources:AppInfo, AdminContact, "admin contact" %>'>Contact Me</asp:HyperLink>

Shared resources can also be assigned in the code-behind file, using the My keyword as shown above. In the example attached, the title declared in the site.master file is assigned in the Page_Load event handler, using the following statement. The resource file will invoke the correct language and apply it to the Localize control at runtime. The Localize control will be discussed next.

locTitle.Text = My.Resources.AppInfo.Title

The Localize control is great for static text or other binding statements within an international site. This control is designed to render text that will be localized through resource files. In the attached example, it is perfect to attach a binding expression or assign a value in the code-behind. The following is an example statement.

<asp:Localize ID="locTitle" Runat="server" />
<asp:Localize ID="locTitle2" Runat="server" Text='<%$ Resources:AppInfo, Title, "Company Name" %>' />

Using the instructions above (and also linked to below), add Spanish, German, English, and any other language to the list. Move each one to the top and view the results. The browser will need to be refreshed each time you shift the languages around.

About the Code:

Please note that I didn't debug this application; it was designed more for illustration purposes than a working example. It also does not consider most factors into application design, such as: name information, address information and layout, etc. Layouts for name/address information vary in format by culture. This example does parse the zip code properly, which is a five-digit number for the German and English languages, and a number in the format of E-XX000 for Spain. I found this resource online, so it is incorrect, please forgive my discrepancy.

For more information about languages and the HTTP headers responsible for languages, see these links:

http://www.w3.org/International/questions/qa-accept-lang-locales
http://www.w3.org/International/O-HTTP-prefs.html
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4

The standard folders used in this application will be changing when ASP.NET 2.0 beta 2 and (most likely) the final release comes out. The LocalResources folder will be renamed to Application_LocalResources, and the Resources folder will be renamed to Application_GlobalResources. It is important to know that so when the next release comes out, this example will most likely not be in working condition for only that reason.

You may download the code here.