You want to support multiple languages in your application without developing multiple versions of each page.
Use resource files to provide the text for each user interface element in the languages you wish to support, and then set the text to use at runtime based on the language setting in the browser.
You don’t need to make any changes to the
.aspx
files of your application for them to
output text in multiple languages.
In the code-behind class for each page that needs to support multiple languages, use the .NET language of your choice to:
Determine the user’s preferred language from the collection of acceptable languages returned with the page request.
Create a CultureInfo
object from the
user’s language.
Set the CurrentCulture
and the
CurrentUICulture
to the user’s
culture object (for controlling the formatting of dates and numbers
and locating the appropriate resource file for the selected culture,
respectively).
Create a ResourceManager
object that will be used
to obtain the strings to be displayed in the user’s
language.
Set the text for the individual controls using the
GetString
method of the
ResourceManager
.
Set the values of the language, current date, and sample currency.
Example 14-1 through Example 14-3 show the .aspx
file and
VB and C# code-behind files for an application we’ve
written to demonstrate this solution. The output is shown in Figure 14-1 (English) and Figure 14-2
(German).
ASP.NET provides extensive support for internationalizing applications. With just a few lines of code and the appropriate resource files, an application can display text in the language set in the browser and can format dates, times, and currency as the user would expect to see them.
In the example we have provided to demonstrate multiple language support, the code determines the user’s preferred language when the page is requested. Using the preferred language, a welcome message, the language setting, the current date, and a currency sample are displayed.
The .aspx
file in our example contains nothing
specific to support outputting text in multiple languages. Several
ASP literal controls are used to display the information in the
user’s preferred language.
The Page_Load
method in the code-behind provides
all of the code required to display the form data in the
user’s preferred language. Our first step is to get
the user’s preferred language from the collection of
acceptable languages returned with the page request. The language
collection can contain zero or more languages, so we must make sure
there is at least one language in the collection. Because the
language collection usually lists the languages in the order
preferred by the user, selecting the first language in the list is
generally acceptable.
In a production application, you may want to verify that your application supports the first language in the collection, and if it does not, continue through the collection looking for a supported language.
Your next step is to create a CultureInfo
object
whose culture value is set to the user’s language.
You’ll use this information to set the
CurrentCulture
and
CurrentUICulture
properties of the
CurrentThread
. CurrentCulture
cannot be set to a neutral culture value (one without the subculture
specified), so the CreateSpecificCulture
method is
used to add the subculture information as required. This is necessary
because Internet Explorer and other browsers allow the user to select
a language, such as German, without specifying the specific country
or locale in which the user resides.
Now we set the CurrentCulture
and the
CurrentUICulture
to the user’s
culture object. The CurrentCulture
is used to
format dates and numbers, while the
CurrentUICulture
is used by the resource manager
to locate the appropriate resource file for the selected culture.
Next, you must create a ResourceManager
object to
gather the strings to be displayed in the user’s
language. The constructor for the ResourceManager
requires you to pass it the root name of the resource files for the
application (resource file naming is described later) and a reference
to the main assembly for the resources. The root name must be fully
qualified with the default namespace name you use for the project. In
our example, the default namespace in the project properties has been
left blank so only the root name of the resource file
“Strings” is used. In this example,
the root resource file is compiled into the main assembly, so a
reference to the currently executing assembly can be obtained using
the GetExecutingAssembly
method.
Now that you have set the current culture and UI culture and a
ResourceManager
is available, you can set the text
for the individual controls using the GetString
method of the ResourceManager
. The values you pass
to the GetString
method are the keys you use
identify the desired text strings in the resource files.
The last thing to do is to set the values of the language, current date, and sample currency. These are done in exactly the same manner as a single-language application.
The ResourceManager
handles all of the dirty work
of finding the resource file for the user’s
language, as well as defaulting to the root resource file if a
language is requested that is not supported by your application. It
is able to find the appropriate resource file because of a very
strict naming convention used for the files.
The root resource file can be named most anything, but it is best to
keep it simple and avoid using names that include additional periods
and dashes. In our example, the root filename is
Strings
.
The names for additional language files must include the root name
combined with the culture and subculture information in the format
shown next, where Rootname
is the root
filename, <languagecode>
defines the
two-letter language code derived from ISO-639-1, and
<country/regioncode>
defines the
two-letter country or region code from ISO-3166. The
languagecode
should be in lowercase and
the country/regioncode
in uppercase.
Rootname.<languagecode>-<country/regioncode>
For instance, the resource file containing text in German for our
example is named Strings.de-DE
, where
de
indicates the German language and
DE
designates the German localization of the
language (as opposed to Austrian, Swiss, and others).
To find the required resource file, the resource manager uses the root name of the resource file then appends the UI Culture name to create the name of the resource file applicable to the user’s language. The resource manager then attempts to locate the resource file using a fairly complex set of rules. For our example, let’s keep it simple and just say that if the specific resource file is not found on a first attempt, then the root resource file will be used instead. (Refer to “Packaging and Deploying Resources” in the MSDN documentation for the full set of rules defining where the .NET runtime looks for resource files.)
Resource files can be created in many different ways. If you are
using Visual Studio .NET, you can simply add a new
“Assembly Resource File” to your
project. Visual Studio will create an empty XML file
(.resx
) to contain your resources. You can
double-click on the file in your project and start typing the names
of the keys and the values (text to display). Visual Studio will take
care of the creation and placement of the compiled resource files in
the bin
directory. The English (root) resource
file for our example is shown here, with the in-line schema deleted
for clarity:
<?xml version="1.0" encoding="utf-8"?> <root> ...<data name="txtWelcome">
<value>Welcome to Localization</value>
</data>
<data name="txtLanguageSetting">
<value>Language Setting</value>
</data>
<data name="txtCurrencySample">
<value>Currency Sample</value>
</data>
<data name="txtDateSample" mimetype=" ">
<value>Date Sample</value>
</data>
</root>
If you are not using Visual Studio, you can create text files containing the names of the keys and the required values. The root text file and the German text file for our example are shown here:
After creating text resource files, you need to compile them into .NET resource files by entering the following in a command prompt:
The resulting Strings.resources
and
Strings.de-DE.resources
files must be copied to
the bin
directory of our application.
Testing your application for the supported languages can be performed by changing the preferred language in Internet Explorer. You can change the language by selecting Tools→ Internet Options from the IE menu. Next, click the Languages button and the Language Preference dialog box will be displayed, as shown in Figure 14-3. Add the languages you need to test your application. To test a specific language, move it to the top of the list of languages.
Using resource files in an international application provides a very cost-effective method of implementing the needed support for multiple languages. One thing to keep in mind when designing your application is the performance impact caused by “looking up” every string at runtime. This will result in longer rendering times for the pages. Your international application may require a more powerful web server than you would generally specify for a domestic application.
“Packaging and Deploying Resources” in the MSDN documentation for the full set of rules defining where the .NET runtime looks for resource files
Example 14-1. Multiple language support (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH14InternationalCultureVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH14InternationalCultureVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Internationalization - Culture and UI Culture</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmInternationalization" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td align="center"> </td> </tr> <tr> <td align="center" class="PageHeading"> Internationalization - Culture and UI Culture (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td> <table width="60%" align="center" border="0"> <tr> <td align="center" colspan="2"><asp:Literal id="litWelcome" Runat="server" />
</td> </tr> <tr> <td align="right" width="50%"><asp:Literal id="litLanguageSettingLabel" Runat="server" />
: </td> <td width="50%"><asp:Literal id="litLanguageSetting" Runat="server" />
</td> </tr> <tr> <td align="right" width="50%"><asp:Literal id="litDateLabel" Runat="server" />:
</td> <td width="50%"><asp:Literal id="litDate" Runat="server" />
</td> </tr> <tr> <td align="right" width="50%"><asp:Literal id="litCostLabel" Runat="server" />:
</td> <td width="50%"><asp:Literal id="litCost" Runat="server" />
</td> </tr> </table> </td> </tr> </table> </form> </body> </html>
Example 14-2. Multiple language support code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH14InternationalCultureVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH14InternationalCultureVB.aspx page ' '***************************************************************************** Imports System.Resources Imports System.Globalization Imports System.Reflection Imports System.Threading Namespace ASPNetCookbook.VBExamples Public Class CH14InternationalCultureVB Inherits System.Web.UI.Page 'controls on the form Protected litWelcome As System.Web.UI.WebControls.Literal Protected litLanguageSettingLabel As System.Web.UI.WebControls.Literal Protected litLanguageSetting As System.Web.UI.WebControls.Literal Protected litCostLabel As System.Web.UI.WebControls.Literal Protected litCost As System.Web.UI.WebControls.Literal Protected litDateLabel As System.Web.UI.WebControls.Literal Protected litDate As System.Web.UI.WebControls.Literal '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '-------------------------------------------------------------------------Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Const sampleValue As Single = 12345.67
Dim locRM As ResourceManager
Dim userLanguage As String
Dim culture As CultureInfo
'make sure at least one language is set in the browser
If (Request.UserLanguages.Length > 0) Then
'select the first language in browser's accept language list
userLanguage = Request.UserLanguages(0)
'create a culture object from the user's language by using the
'CreateSpecificCulture method to ensure subculture information
'is included. Culture cannot be set to a neutral culture.
culture = CultureInfo.CreateSpecificCulture(userLanguage)
'set the culture and UICulture
Thread.CurrentThread.CurrentCulture = culture
Thread.CurrentThread.CurrentUICulture = culture
End If
'create a new resource manager from the currently executing assembly
locRM = New ResourceManager("Strings", _
System.Reflection.Assembly.GetExecutingAssembly( ))
'set the labels on the form with the strings obtained through the
'resource manager from the applicable resource file
litWelcome.Text = locRM.GetString("txtWelcome")
litLanguageSettingLabel.Text = locRM.GetString("txtLanguageSetting")
litDateLabel.Text = locRM.GetString("txtDateSample")
litCostLabel.Text = locRM.GetString("txtCurrencySample")
'set the control displaying the browser culture setting
litLanguageSetting.Text = userLanguage
'set the sample date to the current date
litDate.Text = Date.Now.ToShortDateString( )
'set the sample currency value
litCost.Text = sampleValue.ToString("C")
End Sub 'Page_Load
End Class 'CH14InternationalCultureVB End Namespace
Example 14-3. Multiple language support code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH14InternationalCultureCS.aspx.cs // // Description: This module provides the code behind for the // CH14InternationalCultureCS.aspx page // //**************************************************************************** using System; using System.Resources; using System.Globalization; using System.Reflection; using System.Threading; using System.Web.UI.WebControls; namespace ASPNetCookbook.CSExamples { public class CH14InternationalCultureCS : System.Web.UI.Page { protected System.Web.UI.WebControls.Literal litWelcome; protected System.Web.UI.WebControls.Literal litLanguageSettingLabel; protected System.Web.UI.WebControls.Literal litLanguageSetting; protected System.Web.UI.WebControls.Literal litCostLabel; protected System.Web.UI.WebControls.Literal litCost; protected System.Web.UI.WebControls.Literal litDateLabel; protected System.Web.UI.WebControls.Literal litDate; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------private void Page_Load(object sender, System.EventArgs e)
{
const double sampleValue = 12345.67;
ResourceManager locRM =null;
String userLanguage = null;
CultureInfo culture = null;
// make sure at least one language is set in the browser
if (Request.UserLanguages.Length > 0)
{
// select the first language in browser's accept language list
userLanguage = Request.UserLanguages[0];
// create a culture object from the user's language by using the
// CreateSpecificCulture method to ensure subculture information
// is included. Culture cannot be set to a neutral culture.
culture = CultureInfo.CreateSpecificCulture(userLanguage);
// set the culture and UICulture
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
} // if (Request.UserLanguages.Length > 0)
// create a new resource manager from the currently executing assembly
locRM = new ResourceManager("Strings",
System.Reflection.Assembly.GetExecutingAssembly( ));
// set the labels on the form with the strings obtained through the
// resource manager from the applicable resource file
litWelcome.Text = locRM.GetString("txtWelcome");
litLanguageSettingLabel.Text = locRM.GetString("txtLanguageSetting");
litDateLabel.Text = locRM.GetString("txtDateSample");
litCostLabel.Text = locRM.GetString("txtCurrencySample");
// set the control displaying the browser culture setting
litLanguageSetting.Text = userLanguage;
// set the sample date to the current date
litDate.Text = DateTime.Now.ToShortDateString( );
// set the sample currency value
litCost.Text = sampleValue.ToString("C");
} // Page_Load
} // CH14InternationalCultureCS }