Chapter 5. Localization

Developers usually build their applications in the English language. Then, as the audience for the application expands, they realize the need to globalize the application. Of course, the ideal is to build the application to handle an international audience right from the start, but in many cases this may not be possible because of the extra work it requires.

With the .NET Framework 3.5, a considerable effort has been made to address the internationalization of .NET applications. Changes to the API, the addition of capabilities to the server controls, and even Visual Studio itself equip you to do the extra work required to bring your application to an international audience. This chapter looks at some of the important items to consider when building your applications for the world.

Cultures and Regions

As an example, the ASP.NET page that is pulled up in an end user's browser runs under a specific culture and region setting. When building an ASP.NET application or page, the defined culture in which it runs is dependent upon a culture and region setting specified either in the server in which the application is run or in a setting applied by the client (the end user). By default, ASP.NET runs under a culture setting defined by the server.

The world is made up of a multitude of cultures, each of which has a language and a set of defined ways in which it views and consumes numbers, uses currencies, sorts alphabetically, and so on. The .NET Framework defines cultures and regions using the Request for Comments 1766 standard definition (tags for identification of languages), which specifies a language and region using two-letter codes separated by a dash. The following table provides examples of some culture definitions:

Culture Code

Description

en-US

English language; United States

en-GB

English language; United Kingdom (Great Britain)

en-AU

English language; Australia

en-CA

English language; Canada

The examples in this table define four distinct cultures. These four cultures have some similarities and some differences. All four cultures speak the same language (English), so the language code of en is used in each culture setting. Following the language setting is the region setting. Even though these cultures speak the same language, it is important to distinguish them further by setting their region (such as US for the United States, GB for the United Kingdom, AU for Australia, and CA for Canada). These settings reflect the fact that the English used in the United States is slightly different from the English used in the United Kingdom, and so forth. Beyond language, differences exist in how dates and numerical values are represented. This is why a culture's language and region are presented together.

The differences do not break down by country only. Many countries contain more than a single language, and each area has its own preference for notation of dates and other items. For example, en-CA specifies English speakers in Canada. Because Canada is not only an English-speaking country, it also includes the culture setting of fr-CA for French-speaking Canadians.

Understanding Culture Types

The culture definition just given is called a specific culture definition. This definition is as detailed as you can possibly get, defining both the language and the region. The other type of culture definition is a neutral culture definition. Each specific culture has a specified neutral culture with which it is associated. For instance, the English language cultures shown in the previous table are separate, but they also belong to one neutral culture: EN (English). The diagram presented in Figure 5-1 illustrates how these culture types relate to one another.

From this diagram, you can see that many specific cultures belong to a neutral culture. Higher in the hierarchy than the neutral culture is an invariant culture, which is an agnostic culture setting that should be utilized when passing items (such as dates and numbers) around a network. When performing these kinds of operations, you should make your back-end data flows devoid of user-specific culture settings. Instead, apply these settings in the business and presentation layers of your applications.

In addition, pay attention to neutral culture when working with your applications. Invariably, you are going to build applications with views that are more dependent on a neutral culture than on a specific culture. For instance, if you have a Spanish version of your application, you'll probably make this version available to all Spanish speakers regardless of their regions. In many applications, it won't matter whether the Spanish speaker is from Spain, Mexico, or Argentina. In cases where it does make a difference, use the specific culture settings.

Figure 5-1

Figure 5.1. Figure 5-1

Looking at Your Thread

When the end user requests an ASP.NET page or runs a Windows Forms dialog, the item is executed on a thread from the thread pool. That thread has a culture associated with it. You can get information about the culture of the thread programmatically and then check for particular details about that culture.

To see an example of working with a thread and reading the culture information of that thread, create a Windows Forms application that is laid out as shown in Figure 5-2.

Figure 5-2

Figure 5.2. Figure 5-2

When the button on the form is pressed, the Button1_Click event is fired and the user's culture information is read and displayed in the ListBox control. The code for the form is presented here:

Imports System.Threading

Public Class MyCulture

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Dim ci As New _
          System.Globalization.CultureInfo( _
             Thread.CurrentThread.CurrentCulture.ToString())

        ListBox1.Items.Add("CURRENT CULTURE'S INFO")
        ListBox1.Items.Add("Culture's Name: " & ci.Name)
        ListBox1.Items.Add("Culture's Parent Name: " & ci.Parent.Name)
        ListBox1.Items.Add("Culture's Display Name: " & ci.DisplayName)
        ListBox1.Items.Add("Culture's English Name: " & ci.EnglishName)
        ListBox1.Items.Add("Culture's Native Name: " & ci.NativeName)
        ListBox1.Items.Add("Culture's Three Letter ISO Name: " & _
           ci.ThreeLetterISOLanguageName)
        ListBox1.Items.Add("Calendar Type: " & ci.Calendar.ToString())
    End Sub
End Class

Because this form is working with the Thread object, in order for this to work, you need to make a reference to the System.Threading namespace at the top of the form, as is done with the Imports statement. Threading is covered in Chapter 26.

This simple form creates a CultureInfo object from the System.Globalization namespace and assigns the culture from the current thread that is running using the Thread.CurrentThread.CurrentCulture.ToString call. Once the CultureInfo object is populated with the end user's culture, details about that culture can be called using a number of available properties that the CultureInfo object offers. Example results of running the form are shown in Figure 5-3.

Figure 5-3

Figure 5.3. Figure 5-3

The CultureInfo object contains a number of properties that provide you with specific culture information. The items displayed are only a small sampling of what is available from the CultureInfo object. From this figure, you can see that the en-US culture is the default setting in which the thread executes. In addition to this, you can use the CultureInfo object to get at a lot of other descriptive information about the culture. You can always change a thread's culture on the overloads provided via a new instantiation of the CultureInfo object, as shown here:

Imports System.Globalization

Imports System.Threading

Public Class MyCulture

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Thread.CurrentThread.CurrentCulture = New CultureInfo("th-TH")
        Dim ci As CultureInfo = _
           System.Threading.Thread.CurrentThread.CurrentCulture

        ListBox1.Items.Add("CURRENT CULTURE'S INFO")
        ListBox1.Items.Add("Culture's Name: " & ci.Name)
        ListBox1.Items.Add("Culture's Parent Name: " & ci.Parent.Name)
        ListBox1.Items.Add("Culture's Display Name: " & ci.DisplayName)
        ListBox1.Items.Add("Culture's English Name: " & ci.EnglishName)
        ListBox1.Items.Add("Culture's Native Name: " & ci.NativeName)
        ListBox1.Items.Add("Culture's Three Letter ISO Name: " & _
           ci.ThreeLetterISOLanguageName)
        ListBox1.Items.Add("Calendar Type: " & ci.Calendar.ToString())
    End Sub
End Class

In this example, only a couple of lines of code are changed to assign a new instance of the CultureInfo object to the CurrentCulture property of the thread being executed by the application.

The culture setting enables the CultureInfo object to define the culture you want to utilize. In this case, the Thai language of Thailand is assigned. The results produced in the ListBox control are illustrated in Figure 5-4.

Figure 5-4

Figure 5.4. Figure 5-4

From this figure, you can see that the .NET Framework provides the native name of the language used even if it is not a Latin-based letter style. In this case, the results are presented for the Thai language in Thailand, including some of the properties associated with this culture (such as an entirely different calendar than the one used in Western Europe and the United States).

Declaring Culture Globally in ASP.NET

ASP.NET enables you to easily define the culture that is used either by your entire ASP.NET application or by a specific page within your Web application, using what are termed server-side culture declarations. You can specify the culture for any of your ASP.NET applications by means of the appropriate configuration files. In the default install of ASP.NET, no culture is specified, as is evident when you look at the global web.config.comments file found in the ASP.NET 2.0 CONFIG folder (C:WINDOWSMicrosoft.NETFrameworkv2.0.50727CONFIG). This file contains a <globalization> section of the configuration document, presented here:

<globalization requestEncoding="utf-8" responseEncoding="utf-8" fileEncoding=""
 culture="" uiCulture="" enableClientBasedCulture="false"
 responseHeaderEncoding="utf-8" resourceProviderFactoryType=""
 enableBestFitResponseEncoding="false" />

Note the two attributes represented in bold: culture and uiCulture. The culture attribute enables you to define the culture to use for processing incoming requests, whereas the uiCulture attribute enables you define the default culture needed to process any resource files in the application (use of these attributes is covered later in the chapter).

Looking at the configuration declaration in the preceding code block, you can see that nothing is specified for the culture settings. One option you have when specifying a culture on the server is to define this culture in the root web.config file. This causes every ASP.NET 3.5 application on that server to adopt this particular culture setting. The other option is to specify these settings in the web.config file of the application itself, as illustrated here:

<configuration>
   <system.web>

      <globalization culture="ru-RU" uiCulture="ru-RU" />

   </system.web>
</configuration>

In this case, the culture established for just this ASP.NET application is the Russian language in the country of Russia. In addition to setting the culture at either the server-wide or the application-wide level, another option is to set the culture at the page level, as shown here:

<%@ Page Language="VB" UICulture="ru-RU" Culture="ru-RU" %>

This example specifies that the Russian language and culture settings are used for everything on the page. You can see this in action by using this @Page directive and a simple calendar control on the page. Figure 5-5 shows the output.

Figure 5-5

Figure 5.5. Figure 5-5

Adopting Culture Settings in ASP.NET

In addition to using server-side settings to define the culture for your ASP.NET pages, you also have the option to define the culture according to what the client has set as his or her preference in a browser instance.

When end users install Microsoft's Internet Explorer and some of the other browsers, they have the option to select their preferred cultures in a particular order (if they have selected more than a single culture preference). To see this in action in IE, select Tools

Adopting Culture Settings in ASP.NET

Two cultures are selected from the list of available cultures. To add any additional cultures to the list, click the Add button and select the appropriate culture from the list. After you have selected any cultures present in the list, you can select the order in which you prefer to use them. In the case of Figure 5-6, the Finnish culture is selected as the most preferred culture, whereas the U.S. version of English is selected as the second preference. A user with this setting gets the Finnish language version of the application before anything else; if a Finnish version is not available, a U.S. English version is presented instead.

After making this selection, the end user can use the auto feature provided in ASP.NET 3.5. Instead of specifying a distinct culture in any of the configuration files or from the @Page directive, you can also state that ASP.NET should automatically select the culture provided by the end user requesting the page. This is done using the auto keyword, as illustrated here:

<%@ Page Language="VB" UICulture="auto" Culture="auto" %>
Figure 5-6

Figure 5.6. Figure 5-6

With this construction in your page, the dates, calendars, and numbers appear in the preferred culture of the requester. What happens if you have translated resources in resource files (shown later in the chapter) that depend on a culture specification? Or what if you have only specific translations and therefore can't handle every possible culture that might be returned to your ASP.NET page? In this case, you can specify the auto option with an additional fallback option if ASP.NET cannot find the culture settings of the user (such as culture-specific resource files). This usage is illustrated in the following code:

<%@ Page Language="VB" UICulture="auto:en-US" Culture="auto:en-US" %>

In this case, the automatic detection is utilized, but if the culture the end user prefers is not present, then en-US is used.

Translating Values and Behaviors

In the process of globalizing your .NET application, you may notice a number of aspects that are done differently compared to building an application that is devoid of globalization, including how dates are represented and how currencies are shown. This section looks at some of these issues.

Understanding Differences in Dates

Different cultures specify dates and time very differently. For instance, take the following date as an example:

08/11/2008

Is this date August 11, 2008 or is it November 8, 2008? Again, when storing values such as date/time stamps in a database or other type of back-end system, always use the same culture (or invariant culture) for these items to avoid any mistakes. It should be the job of the business logic layer or the presentation layer to convert these items for use by the end user.

Setting the culture at the server level in ASP.NET or within a Windows Forms application, as shown in the earlier samples, enables your .NET application to make these conversions for you. You can also simply assign a new culture to the thread in which the application is running. For instance, consider the following code:

Imports System.Globalization
Imports System.Threading

Public Class Differences

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Dim dt As DateTime = New DateTime(2008, 8, 11, 11, 12, 10, 10)

        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        ListBox1.Items.Add( _
          Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
          dt.ToString())

        Thread.CurrentThread.CurrentCulture = New CultureInfo("ru-RU")
        ListBox1.Items.Add( _
          Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
          dt.ToString())

        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        ListBox1.Items.Add( _
          Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
          dt.ToString())

        Thread.CurrentThread.CurrentCulture = New CultureInfo("th-TH")
        ListBox1.Items.Add( _
          Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
          dt.ToString())
    End Sub
End Class

In this case, a Windows Forms application is used again and four different cultures are utilized in the output. The date/time construction used by the defined culture is written to the ListBox control. The result from this code operation is presented in Figure 5-7.

Figure 5-7

Figure 5.7. Figure 5-7

Clearly, the formats used to represent a date/time value are dramatically different from one another — and the Thai culture (th-TH), even uses an entirely different calendar that labels 2008 as 2551.

Understanding Differences in Numbers and Currencies

In addition to date/time values, numbers are constructed quite differently from one culture to the next. How can a number be represented differently in different cultures? Well, it has less to do with the actual number (although certain cultures use different number symbols) and more to do with how the number separators are used for decimals or for showing amounts such as thousands, millions, and more. For instance, in the English culture of the United States (en-US), numbers are represented in the following fashion:

5,123,456.00

From this example, you can see that the en-US culture uses a comma as a separator for thousands and a period for signifying the start of any decimals that might appear after the number is presented. It is quite different when working with other cultures. The following code block shows an example of representing numbers in other cultures:

Imports System.Globalization
Imports System.Threading

Public Class Differences

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Dim myNumber As Double = 5123456.0

        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("n"))

        Thread.CurrentThread.CurrentCulture = New CultureInfo("vi-VN")
ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("n"))

        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("n"))

        Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-CH")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("n"))
    End Sub
End Class

Running this example produces the results shown in Figure 5-8.

Figure 5-8

Figure 5.8. Figure 5-8

As you can see, cultures show numbers in numerous different formats. The second culture listed in the figure, vi-VN (Vietnamese in Vietnam), constructs a number exactly the opposite from the way it is constructed in en-US. The Vietnamese culture uses periods for the thousand separators and a comma for signifying decimals. Finnish uses spaces for the thousand separators and a comma for the decimal separator, whereas the French-speaking Swiss use an apostrophe for separating thousands and a period for the decimal separator. Therefore, it is important to "translate" numbers to the proper construction so that users of your application can properly understand the numbers represented.

Another scenario in which you represent numbers is when working with currencies. It is one thing to convert currencies so that end users understand the proper value of an item, but it is another to translate the construction of the currency just as you would a basic number.

Each culture has a distinct currency symbol used to signify that a number represented is an actual currency value. For instance, the en-US culture represents a currency in the following format:

$5,123,456.00

The en-US culture uses a U.S. dollar symbol ($), and the location of this symbol is just as important as the symbol itself. For en-US, the $ symbol directly precedes the currency value (with no space in between the symbol and the first character of the number). Other cultures use different symbols to represent currency and often place those currency symbols in different locations. Try changing the previous code block so that it now represents the number as a currency. The necessary changes are shown here:

Imports System.Globalization
Imports System.Threading

Public Class Differences

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Dim myNumber As Double = 5123456.0

        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("c"))
        Thread.CurrentThread.CurrentCulture = New CultureInfo("vi-VN")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("c"))
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("c"))
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-CH")
        ListBox1.Items.Add(Thread.CurrentThread.CurrentCulture.EnglishName & _
           " : " & myNumber.ToString("c"))
    End Sub
End Class

Running this example shows how these cultures represent currency values (see Figure 5-9).

Figure 5-9

Figure 5.9. Figure 5-9

Not only are the numbers constructed quite differently from one another, but the currency symbol and the location of the symbol in regard to the number are quite different as well.

When working with currencies, note that when you are using currencies on an ASP.NET page, you have provided an automatic culture setting for the page as a whole (such as setting the culture in the @Page directive). You must specify a specific culture for the currency that is the same in all cases unless you are actually doing a currency conversion. For instance, if you are specifying a U.S. dollar currency value on your ASP.NET page, you do not want to specify that the culture of the currency is something else (for example, the euro).

An exception would be if you actually performed a currency conversion and showed the appropriate euro value along with the culture specification of the currency. Therefore, if you are using an automatic culture setting on your ASP.NET page and you are not converting the currency, then you perform something similar to what is illustrated in the following code for currency values:

Dim myNumber As Double = 5123456.00
Dim usCurr As CultureInfo = New CultureInfo("en-US")
Response.Write(myNumber.ToString("c", usCurr))

Understanding Differences in Sorting Strings

You have learned to translate textual values and alter the construction of the numbers, date/time values, currencies, and more when you are globalizing an application. You should also take care when applying culture settings to some of the programmatic behaviors that you establish for values in your applications. One operation that can change based upon the culture setting applied is how .NET sorts strings. You might think that all cultures sort strings in the same way (and generally they do), but sometimes differences exist. To give you an example, the following example shows a sorting operation occurring in the en-US culture:

Imports System.Globalization
Imports System.Threading

Public Class Differences

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")

        Dim myList As List(Of String) = New List(Of String)

        myList.Add("Washington D.C.")
        myList.Add("Helsinki")
        myList.Add("Moscow")
        myList.Add("Warsaw")
        myList.Add("Vienna")
        myList.Add("Tokyo")

        myList.Sort()
For Each item As String In myList
            ListBox1.Items.Add(item.ToString())
        Next

    End Sub
End Class

For this example to work, you have to reference the System.Collections and the System.Collections.Generic namespaces because this example makes use of the List(Of String) object.

In this example, a generic list of capitals from various countries of the world is created in random order. Then the Sort method of the generic List(Of String) object is invoked. This sorting operation sorts the strings based upon how sorting is done for the defined culture in which the application thread is running. The preceding code shows the sorting as it is done for the en-US culture. The result of this operation is shown in Figure 5-10.

Figure 5-10

Figure 5.10. Figure 5-10

This is pretty much what you would expect. Now, however, change the previous example so that the culture is set to the Finnish culture:

Imports System.Globalization
Imports System.Threading

Public Class Differences

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")

        Dim myList As List(Of String) = New List(Of String)
myList.Add("Washington D.C.")
        myList.Add("Helsinki")
        myList.Add("Moscow")
        myList.Add("Warsaw")
        myList.Add("Vienna")
        myList.Add("Tokyo")

        myList.Sort()

        For Each item As String In myList
            ListBox1.Items.Add(item.ToString())
        Next

    End Sub
End Class

If you run the same bit of code under the Finnish culture setting, you get the results presented in Figure 5-11.

Figure 5-11

Figure 5.11. Figure 5-11

Comparing the Finnish culture sorting done in Figure 5-11 and the U.S. English culture sorting done in Figure 5-10, you can see that the city of Vienna is in a different place in the Finnish version. This is because in the Finnish language, there is no difference between the letter V and the letter W. Therefore, if you are sorting using the Finnish culture setting, then Vi comes after Wa, and thus Vienna comes last in the list of strings in the sorting operation.

Working with ASP.NET Resource Files

When you work with ASP.NET 3.5, all resources are handled by a resource file. A resource file is an XML-based file that has a .resx extension. You can have Visual Studio 2008 help you construct this file. Resource files provide a set of items that are utilized by a specified culture. In your ASP.NET 3.5 applications, you store resource files as either local resources or global resources. The following sections describe how to use each type of resource.

Making Use of Local Resources

You might be surprised how easily you can build an ASP.NET page so that it can be localized into other languages. In fact, the only thing you need to do is build the ASP.NET page as you normally would and then use some built-in capabilities from Visual Studio 2008 to convert the page to a format that enables you to plug in other languages easily.

To see this in action, build a simple ASP.NET page as presented here (referred to later in the chapter as the "ASP.NET page code block"):

<%@ Page Language="VB" %>

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Sample Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server"
         Text="What is your name?"></asp:Label><br />
        <br />
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>&nbsp;
        <asp:Button ID="Button1" runat="server" Text="Submit Name" /><br />
        <br />
        <asp:Label ID="Label2" runat="server"></asp:Label>
    </div>
    </form>
</body>
</html>

As you can see, there is not much to this page. It is composed of a couple of Label controls, as well as TextBox and Button controls. The end user enters his or her name into the text box, and then the Label2 server control is populated with the inputted name and a simple greeting.

The next step is what makes Visual Studio so great. To change the construction of this page so that it can be localized easily from resource files, open the page in Visual Studio and select Tools

Making Use of Local Resources

Selecting Generate Local Resource from the Tool menu causes Visual Studio to create an App_LocalResources folder in your project if you don't have one already. A .resx file based upon this ASP.NET page is then placed in the folder. For instance, if you are working with the Default.aspx page, then the resource file is named Default.aspx.resx. These changes are shown in Figure 5-12.

Figure 5-12

Figure 5.12. Figure 5-12

If you right-click on the .resx file and select View Code, notice that the .resx file is nothing more than an XML file with an associated schema at the beginning of the document. The resource file that is generated for you takes every possible property of every translatable control on the page and gives each item a key value that can be referenced in your ASP.NET page. Looking at the page's code, note that all the text values you placed in the page have been retained, but they have also been placed inside the resource file. Visual Studio changed the code of the Default.aspx page as shown in the following code block:

<%@ Page Language="C#" Culture="auto" meta:resourcekey="PageResource1"
    UICulture="auto" %>

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Sample Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="What is your name?"
         meta:resourcekey="Label1Resource1"></asp:Label><br />
        <br />
        <asp:TextBox ID="TextBox1" runat="server"
         meta:resourcekey="TextBox1Resource1"></asp:TextBox>&nbsp;
        <asp:Button ID="Button1"
         runat="server" Text="Submit Name"
         meta:resourcekey="Button1Resource1" /><br />
        <br />
        <asp:Label ID="Label2" runat="server"
meta:resourcekey="Label2Resource1"></asp:Label>
    </div>
    </form>
</body>
</html>

From this bit of code, you can see that the Culture and UICulture attributes have been added to the @Page directive with a value of auto, thus enabling this application to be localized. In addition, the attribute meta:resourcekey has been added to each of the controls, along with an associated value. This is the key from the .resx file that was created on your behalf. Double-clicking on the Default.aspx.resx file opens the resource file in the Resource Editor, shown in Figure 5-13, built into Visual Studio.

Figure 5-13

Figure 5.13. Figure 5-13

Note that a few properties from each of the server controls have been defined in the resource file. For instance, the Button server control has its Text and ToolTip properties exposed in this resource file, and the Visual Studio localization tool has pulled the default Text property value from the control based on what you placed there. Looking more closely at the Button server control constructions in this file, you can see that both the Text and ToolTip properties have a defining Button1Resource1 value preceding the property name. This is the key that is used in the Button server control shown earlier.

<asp:Button ID="Button1"
 runat="server" Text="Submit Name"
 meta:resourcekey="Button1Resource1" />

Here, a meta:resourcekey attribute has been added; and in this case it references Button1Resource1. All the properties using this key in the resource file (for example, the Text and ToolTip properties) are applied to this Button server control at runtime.

Adding Another Language Resource File

Now that the Default.aspx.resx file is in place, this is a file for an invariant culture. No culture is assigned to this resource file. If no culture can be determined, then this is the resource file that is utilized. To add another resource file for the Default.aspx page that handles another language altogether, copy and paste the Default.aspx.resx file into the same App_LocalResources folder and rename the newly copied file. If you use Default.aspx.fi-FI.resx, give the following keys the values shown to make a Finnish-language resource file:

Button1Resource1.Text    Lähetä Nimi
Label1Resource1.Text     Mikä sinun nimi on?
PageResource1.Title      Näytesivu

You want to create a custom resource in both resource files using the key Label2Answer. The Default.aspx.resx file should have the following new key:

Label2Answer            Hello

Now you can add the key Label2Answer to the Default.aspx.fi-FI.resx file as shown here:

Label2Answer            Hei

You now have resources for specific controls, and a resource that you can access later programmatically.

Finalizing the Building of the Default.aspx Page

Finalizing the Default.aspx page, you want to add a Button1_Click event so that when the end user enters a name into the text box and clicks the Submit button, the Label2 server control provides a greeting pulled from the local resource files. When all is said and done, you should have a Default.aspx page that looks like this:

<%@ Page Language="VB" Culture="auto" meta:resourcekey="PageResource1"
    UICulture="auto" %>

<script runat="server">
    Protected Sub Button1_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs)

       Label2.Text = GetLocalResourceObject("Label2Answer").ToString() & _
          " " & TextBox1.Text
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Sample Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="What is your name?"
meta:resourcekey="Label1Resource1"></asp:Label><br />
        <br />
        <asp:TextBox ID="TextBox1" runat="server"
         meta:resourcekey="TextBox1Resource1"></asp:TextBox>&nbsp;
        <asp:Button ID="Button1"
         runat="server" Text="Submit Name"
         meta:resourcekey="Button1Resource1" OnClick="Button1_Click" /><br />
        <br />
        <asp:Label ID="Label2" runat="server"
         meta:resourcekey="Label2Resource1"></asp:Label>
    </div>
    </form>
</body>
</html>

In addition to pulling local resources using the meta:resourcekey attribute in the server controls on the page to access the exposed attributes, you can also access any property value contained in the local resource file by using the GetLocalResourceObject. When using GetLocalResourceObject, you simply use the name of the key as a parameter, as shown here:

GetLocalResourceObject("Label2Answer")

You could just as easily get at any of the controls' property values from the resource file programmatically using the same construct:

GetLocalResourceObject("Button1Resource1.Text")

With the code from the Default.aspx page in place and the resource files completed, you can run the page, entering a name in the text box and then clicking the button to get a response, as shown in Figure 5-14.

Figure 5-14

Figure 5.14. Figure 5-14

What happened behind the scenes that caused this page to be constructed in this manner? First, only two resource files — Default.aspx.resx and Default.aspx.fi-FI.resx — are available. The Default.aspx.resx resource file is the invariant culture resource file, whereas the Default.aspx.fi-FI.resx resource file is for a specific culture (fi-FI). Because I requested the Default.aspx page and my browser is set to en-US as my preferred culture, ASP.NET found the local resources for the Default.aspx page. From there, ASP.NET checked for an en-US-specific version of the Default.aspx page. Because there isn't a specific page for the en-US culture, ASP.NET checked for an EN-(neutral culture)-specific page. Not finding a page for the EN neutral culture, ASP.NET was then forced to use the invariant culture resource file of Default.aspx.resx, producing the page shown in Figure 5-14.

If you now set your IE language preference as fi-FI and rerun the Default.aspx page, you see a Finnish version of the page (see Figure 5-15).

Figure 5-15

Figure 5.15. Figure 5-15

In this case, having set my IE language preference to fi-FI, I am presented with this culture's page instead of the invariant culture page presented earlier. ASP.NET found this specific culture through use of the Default.aspx.fi-FI.resx resource file.

You can see that all the control properties that were translated and placed within the resource file are utilized automatically by ASP.NET, including the page title presented in the title bar of IE.

Neutral Cultures Are Generally Preferred

When you are working with the resource files from this example, note that one of the resources is for a specific culture. The Default.aspx.fi-FI.resx file is for a specific culture — the Finnish language as spoken in Finland. Another option would be to make this file work not for a specific culture, but instead for a neutral culture. To do so, simply name the file Default.aspx.FI.resx. In this example, it doesn't make any difference because no other countries speak Finnish. It would make sense for languages such as German, Spanish, or French, which are spoken in multiple countries.

For instance, if you are going to have a Spanish version of the Default.aspx page, you could definitely build it for a specific culture, such as Default.aspx.es-MX.resx. This construction is for the Spanish language as spoken in Mexico. With this in place, if someone requests the Default.aspx page with the language setting of es-MX, that user is provided with the contents of this resource file. If the requester has a setting of es-ES, he or she will not get the Default.aspx.es-MX.resx resource file, but the invariant culture resource file of Default.aspx.resx. If you are going to make only a single translation for your site or any of your pages, construct the resource files to be for neutral cultures, not specific cultures.

If you have the resource file Default.aspx.ES.resx, then it won't matter if the end user's preferred setting is set to es-MX, es-ES, or even es-AR — that user gets the appropriate ES neutral-culture version of the page.

Making Use of Global Resources

Besides using only local resources that specifically deal with a particular page in your ASP.NET application, you also have the option to create global resources that can be used across multiple pages. To create a resource file that can be utilized across the entire application, right-click on the solution in the Solution Explorer of Visual Studio and select Add New Item. From the Add New Item dialog, select Resource file.

Selecting this option provides you with a Resource.resx file. Visual Studio places this file in a new folder called App_GlobalResources. Again, this first file is the invariant culture resource file. Add a single string resource, giving it the key of PrivacyStatement and a value of some kind (a long string).

After you have the invariant culture resource file completed, the next step is to add another resource file, but this time name it Resource.fi-FI.resx. Again, for this resource file, use a string key of PrivacyStatement and a different value altogether from the one you used in the other resource file.

The point of a global resource file is to have access to these resources across the entire application. You can access the values that you place in these files in several ways. One way is to work the value directly into any of your server control declarations. For instance, you can place the following privacy statement in a Label server control as shown here:

<asp:Label ID="Label1" runat="server"
 Text='<%$ Resources: Resource, PrivacyStatement %>'></asp:Label>

With this construction in place, you can now grab the appropriate value of the PrivacyStatement global resource, depending on the language preference of the end user requesting the page. To make this work, you use the keyword Resources followed by a colon. Next, you specify the name of the resource file. In this case, the name of the resource file is Resource, because this statement goes to the Resource.resx and Resource.fi-FI.resx files in order to find what it needs. After specifying the particular resource file to use, the next item in the statement is the key — in this case, PrivacyStatement.

Another way to achieve the same result is to use some built-in dialogs within Visual Studio. Highlight the server control you want in Visual Studio from Design view so that the control appears within the Properties window. For my example, I highlighted a Label server control. From the Properties window, click the button within the Expressions property. This launches the Expressions dialog, where you can bind the PrivacyStatement value to the Text property of the control (see Figure 5-16).

To make this work, highlight the Text property in the Bindable properties list. Then select an expression type from the drop-down list on the right-hand side of the dialog. Your options include AppSettings, ConnectionStrings, and Resources. Select Resources and you are then asked for the ClassKey and ResourceKey property values. The ClassKey is the name of the file that should be utilized. In this example, the name of the file is Resource.resx, so use the Resources keyword as a value. You are provided with a drop-down list in the ResourceKey property section, with all the keys available in this file. Because only a single key exists at this point, only the PrivacyStatement key appears in this list. Make this selection and click OK. The Label server control changes and now appears as it was presented earlier in the two-line code block.

One nice feature is that the resources provided via global resources are available in a strongly typed manner. For instance, you can programmatically get at a global resource value by using the construction presented in the following example:

Label1.Text = Resources.Resource.PrivacyStatement.ToString()
Figure 5-16

Figure 5.16. Figure 5-16

Figure 5-17 shows that you have full IntelliSense for these resource values.

Figure 5-17

Figure 5.17. Figure 5-17

Resource Files in Windows Forms

Just as with ASP.NET, you can also work with resource files (.resx) right from Visual Studio. To see how to localize a Windows Forms application, create a new form in your Localization project called UsingResx.vb.

Like the ASP.NET form described earlier in this chapter (and identified as "ASP.NET page code block"), this Windows Form dialog contains a couple of Label controls, a Button, and a TextBox control. Ultimately, your form should look like the one shown in Figure 5-18.

Figure 5-18

Figure 5.18. Figure 5-18

Note that the controls were just laid out on the form and no text was added yet. Before you get started, let's turn on localization features for the form. You can also do this to a form that is already completed if you are later converting a form to deal with more than one language.

Highlighting the form in the designer, change the Localizable property to True. This enables you to apply more than one language to a form and have the elements for a particular culture stored in a resource file. After you have set the Localizable property to True, you can then provide values to the Label and the Button controls on the form. The properties that you assign to the controls are done under the (Default) language setting. You will find this setting within the Language property of the form, as shown in Figure 5-19.

Figure 5-19

Figure 5.19. Figure 5-19

Setting the text values to the (Default) property setting means that if a culture is undetermined or a resource file is not available for this culture, then the default one will be utilized. From there, change the Language property of the form to Finnish. You can then change the values of the three controls as follows:

Button1.Text    Lähetä Nimi
Label1.Text     Mikä sinun nimi on?
Label2.Text     Hei

Double-clicking on the form's button will enable you to create a Button1_Click event. The code for this page is as follows:

Imports System.Globalization
Imports System.Threading

Public Class UsingResx

    Sub New()

        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        Thread.CurrentThread.CurrentUICulture = New CultureInfo("en-US")

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Label2.Visible = True
        Label2.Text += TextBox1.Text
    End Sub
End Class

This assigns the CurrentCulture and the CurrentUICulture properties to en-US; the form it produces is shown in Figure 5-20.

Figure 5-20

Figure 5.20. Figure 5-20

Now change the code so that it works with a Finnish culture setting:

Imports System.Globalization
Imports System.Threading

Public Class UsingResx

    Sub New()
Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        Thread.CurrentThread.CurrentUICulture = New CultureInfo("fi-FI")

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click

        Label2.Visible = True
        Label2.Text += TextBox1.Text
    End Sub
End Class

Running the form with this change in place now produces the window shown in Figure 5-21.

Figure 5-21

Figure 5.21. Figure 5-21

So where are all the translations stored? Just like ASP.NET, they are stored in the resource file for this form. Looking in the Solution Explorer, you will now find a UsingResx.resx file and a UsingResx.fi.resx file, as shown in Figure 5-22.

Figure 5-22

Figure 5.22. Figure 5-22

Opening the UsingResx.resx file will cause Visual Studio to open the file in a manner that enables you to directly edit the values it stores. The default resource file stores some type references as well as other properties of the controls on the form, as shown in Figure 5-23.

Opening the UsingResx.fi.resx file instead shows only the three different properties that you changed. The rest of the properties are read from the default resource file. The contents of the Finnish resource file are presented in Figure 5-24.

Figure 5-23

Figure 5.23. Figure 5-23

Figure 5-24

Figure 5.24. Figure 5-24

Visual Studio 2008 provides an editor for working with resource files. You have already seen some of the views available from the Resource Editor. Resources are categorized visually according to the data type of the resource. This chapter has covered only the handling of strings, but other categories exist (such as images, icons, audio files, miscellaneous files, and other items). These options are shown in Figure 5-25.

Figure 5-25

Figure 5.25. Figure 5-25

Summary

It is hoped that you see the value in localizing your .NET applications so that they can handle multiple cultures. This chapter described some of the issues you face when localizing your applications, and described some of the built-in tools provided via both Visual Studio and the .NET Framework to make this process easier for you.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset