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.
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:
Description | |
---|---|
English language; United States | |
English language; United Kingdom (Great Britain) | |
English language; Australia | |
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.
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.
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.
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.
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.
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).
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.
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
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" %>
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.
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.
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.
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.
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.
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).
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))
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.
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.
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.
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.
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> <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
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.
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> <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.
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.
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 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> <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.
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).
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.
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.
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-17 shows that you have full IntelliSense for these resource values.
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.
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.
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.
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.
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.
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.
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.
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.