Recipe 17.3. Providing Multiple Language Support


Problem

You want to support multiple languages in your application without developing multiple versions of each page.

Solution

Use resource files to provide the text for each user interface element in the languages you wish to support, add attributes to the static controls in your .aspx files to set their text automatically from the resource files at runtime, set the culture and uiCulture attributes of the <globalization> element in web.config to auto, and set the values for dynamic controls in the code-behind class.

Use Visual Studio 2005 to create the root resource file and add the resource attributes to the controls in your .aspx files as follows:

  1. Open the .aspx file that you want to localize.

  2. Switch to Design mode.

  3. Select Tools Generate Local Resource from the menu. Visual Studio 2005 will add a meta:resourcekey attribute to all server controls in the file and create a root resource file with entries for each of the server controls.

  4. Set the culture and uiCulture attributes of the <globalization> element in web.config to auto:

     <globalization culture="auto" uiCulture="auto" /> 

In the code-behind class for each page that needs to support multiple languages, use the .NET language of your choice to:

  1. Set the values of controls used for date and currency.

  2. Set the text of any controls that need to be set programmatically.

Examples 17-1, 17-2 through 17-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 Figures 17-1 (English) and 17-2 (German). Examples 17-4 and 17-5 show the English and German resource files used in this example.

Figure 17-1. Multiple language outputEnglish


Figure 17-2. Multiple language outputGerman


Discussion

ASP.NET 1.x provides extensive support for internationalizing applications; however, it requires you to write a lot of code to set the text or values of every control that needs to be localized. In addition, the creation of the required resource files is tedious. ASP.NET 2.0 and Visual Studio 2005 have made the task of internationalizing an application much easier.

First, Visual Studio 2005 provides the ability to create the root resource file automatically for an ASPX page. Resource files for a specific ASPX page are referred to as local resource files. These .resx files contain the localization information for a single ASPX page.

To create the root resource file, open the desired .aspx file, switch to Design mode, and then select Tools Generate Local Resources from the menu. Visual Studio will create a resource file and add an item for each server control on the page. The resource file will be named the same as the page with a Second, during the process of creating the resource file, Visual Studio adds a meta:resourcekey attribute to each of the server controls. This attribute is used by ASP.NET at runtime to set the value of the control automatically with the appropriate value from the resource file. You no longer need to provide code for every item on a page that must be localized. The only code you have to write is for setting the value of controls that do not contain static text, such as dates, time, and other values that must be set programmatically.

In the example we have provided to demonstrate multiple language support, the ASPX page displays a welcome message, the language setting from the browser request, the current date, a currency sample, and an example of a control that is set programmatically, as shown in Figures 17-1 (English) and 17-2 (German). The welcome message and the labels for each of the displayed items are set without writing any code.

The Page_Load method in the code-behind provides the code required to set the values of the controls. To set the Language Setting, we identify 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 ensure at least one language is 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; if it does not, continue through the collection looking for a supported language.


Next, we set the values of the current date and sample currency. No special code is required. Set the values as you would in a single language application. The framework will take care of localizing the date and currency values using the culture and UI culture for the user's preferred language.

Finally, we set the value of the Programmatically Set Value control to demonstrate how you can programmatically retrieve an entry from the resource file. To retrieve a value from the local resource file, use the GetLocalResourceObject method of the current page (MyClass or Me in VB, this in C#) passing the name of the required resource.

In ASP.NET 1.x, you had to handle the creation of the ResourceManager as well as setting the Culture and UI Culture of the current thread to support a localized application. ASP.NET 2.0 does all of this work for you.

The ResourceManager will handle all 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 for local resources (resources for a specific page) is named the same as the page with a .resx extension. In our example, the root resource is named CH17InternationalCultureVB.aspx.resx.

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 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 CH17InternationalCultureVB.aspx.resx.de-DE, where de indicates the German language and DE designates the German localization of the language (as opposed to Austrian, Swiss, etc.).

To find the required resource file, the resource manager uses the root name of the resource file and 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 say that if the specific resource file is not found on a first attempt, 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.)

Visual Studio 2005 provides a new resource file editor that makes creating and editing resource files easier than in previous versions, providing the ability to manage image resources and string resources.

To add a local resource file, select the App_LocalResources folder in the Solution Explorer, right-click, select Add New Item, and then select Resource File. The file must be named as described earlier. Figures 17-3 and 17-4 show the English and German resource files in the resource editor for our example.

Figure 17-3. English Resource File (CH17InternationalCultureVB.aspx.resx)


Figure 17-4. German Resource File (CH17InternationalCultureVB.aspx.de-DE.resx)


You can test your application for the supported languages by changing the preferred language in Internet Explorer. Select 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 17-5. 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 cost-effective method for 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 single language application.


Figure 17-5. Setting language preference in IE


This example shows how to use local resource files for an individual page. In many applications, this results in duplication of resource data used on multiple pages. ASP.NET 2.0 supports global resources in addition to local resources. Recipe 17.3 provides an example of using global resources as well as overriding the UI Culture when currency must always be displayed in a specific currency format.

See Also

Recipe 17.3 and "Packaging and Deploying Resources" in the MSDN documentation for the full set of rules defining where the .NET runtime looks for resource files

Example 17-1. Multiple language support (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master"       AutoEventWireup="false"   CodeFile="CH17InternationalCultureVB.aspx.vb"  Inherits="ASPNetCookbook.VBExamples.CH17InternationalCultureVB"  Title="Internationalization - Culture and UI Culture"  meta:resourcekey="PageResource1" Culture="auto" UICulture="auto" %> <asp:Content  runat="server" ContentPlaceHolder>       <div align="center" >            <asp:Localize  runat="server"                            meta:resourcekey="locHeadingResource1"                 Text="Internationalization - Culture and UI Culture (VB)" />      </div>  <table width="60%" align="center" border="0" >           <tr>                <td align="center" colspan="2">                     <asp:Literal  Runat="server"                                     Text="Welcome to Localization"  meta:resourcekey="litWelcomeResource1" />                </td>           </tr>   <tr>                <td align="right" width="50%">                      <asp:Literal  Runat="server"                                      meta:resourcekey="litLanguageSettingLabelResource1" />                     :&nbsp;                </td>                <td width="50%">                     <asp:Literal  Runat="server"                                     meta:resourcekey="litLanguageSettingResource1" />                </td>           </tr>   <tr>                <td align="right" width="50%">                     <asp:Literal  Runat="server"                                     Text="Date Sample"  meta:resourcekey="litDateLabelResource1" />                     :&nbsp;                </td>    <td width="50%">                     <asp:Literal  Runat="server"                                 meta:resourcekey="litDateResource1" />                </td>           </tr>   <tr>                <td align="right" width="50%">                     <asp:Literal  Runat="server"                                     Text="Currency Sample"  meta:resourcekey="litCostLabelResource1" />                     :&nbsp;                </td>    <td width="50%">                     <asp:Literal  Runat="server"                 meta:resourcekey="litCostResource1" />                </td>   </tr>   <tr>                <td align="right" width="50%">                     <asp:Literal  Runat="server"                                     Text="Programmatically Set Value"  meta:resourcekey="litProgrammaticallySetLabelResource1" />                     :&nbsp;                </td>    <td width="50%">                     <asp:Literal  Runat="server" />                </td>    </tr>   </table>  </asp:Content> 

Example 17-2. Multiple language support code-behind (.vb)

 Option Explicit On  Option Strict On Imports System Namespace ASPNetCookbook.VBExamples    ''' <summary>    ''' This class provides the code-behind for    ''' CH17InternationalCultureVB.aspx    ''' </summary>    Partial Class CH17InternationalCultureVB     Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the page load event. It ''' is responsible for initializing the controls on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub Page_Load(ByVal sender As Object, _                           ByVal e As System.EventArgs) Handles Me.Load       Const sampleValue As Single = 12345.67   'set the control displaying the browser culture setting   If ((Not IsNothing(Request.UserLanguages)) AndAlso _           (Request.UserLanguages.Length > 0)) Then         litLanguageSetting.Text = Request.UserLanguages(0)       Else         litLanguageSetting.Text = "None"       End If       'set the sample date to the current date   litDate.Text = DateTime.Now.ToShortDateString()   'set the sample currency value   litCost.Text = sampleValue.ToString("C")   'set a control programmatically from the local resource file    litProgrammaticallySet.Text = _      CStr(MyClass.GetLocalResourceObject("ProgrammaticallySetValue"))     End Sub 'Page_Load    End Class 'CH17InternationalCultureVB  End Namespace 

Example 17-3. Multiple language support code-behind (.cs)

 using System; namespace ASPNetCookbook.CSExamples {    /// <summary>    /// This class provides the code-behind for    /// CH17InternationalCultureCS.aspx    /// </summary>    public partial class CH17InternationalCultureCS : System.Web.UI.Page    {     ///*********************************************************************** /// <summary> /// This routine provides the event handler for the page load event. /// It is responsible for initializing the controls on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) {       const double sampleValue = 12345.67;   // set the control displaying the browser culture setting   if ((Request.UserLanguages != null) &&           (Request.UserLanguages.Length > 0))       {         litLanguageSetting.Text = Request.UserLanguages[0];       }   else   {         litLanguageSetting.Text = "None";       }   // set the sample date to the current date   litDate.Text = DateTime.Now.ToShortDateString();   // set the sample currency value   litCost.Text = sampleValue.ToString("C");   // set a control programmatically from the local resource file    litProgrammaticallySet.Text =          (String)(this.GetLocalResourceObject("ProgrammaticallySetValue"));    } // Page_Load    } // CH17InternationalCultureCS  } 

Example 17-4. English resource file

 <?xml version="1.0" encoding="utf-8"?>  <root>    <xsd:schema  xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"                  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">     <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:element name="root" msdata:IsDataSet="true">       <xsd:complexType>         <xsd:choice maxOccurs="unbounded">           <xsd:element name="metadata"> <xsd:complexType>               <xsd:sequence>                 <xsd:element name="value" type="xsd:string" minOccurs="0" />                </xsd:sequence>                <xsd:attribute name="name" use="required" type="xsd:string" />    <xsd:attribute name="type" type="xsd:string" />    <xsd:attribute name="mimetype" type="xsd:string" />    <xsd:attribute ref="xml:space" />             </xsd:complexType>           </xsd:element>             <xsd:element name="assembly">               <xsd:complexType>                  <xsd:attribute name="alias" type="xsd:string" />  <xsd:attribute name="name" type="xsd:string" />               </xsd:complexType>             </xsd:element>             <xsd:element name="data">               <xsd:complexType>                  <xsd:sequence>                    <xsd:element name="value" type="xsd:string" minOccurs="0"                                    msdata:Ordinal="1" />                    <xsd:element name="comment" type="xsd:string" minOccurs="0"                                   msdata:Ordinal="2" />                  </xsd:sequence>                  <xsd:attribute name="name" type="xsd:string" use="required"                                   msdata:Ordinal="1" />                  <xsd:attribute name="type" type="xsd:string"                                    msdata:Ordinal="3" />                  <xsd:attribute name="mimetype" type="xsd:string"                                    msdata:Ordinal="4" />                  <xsd:attribute ref="xml:space" />               </xsd:complexType>             </xsd:element> <xsd:element name="resheader">               <xsd:complexType>                  <xsd:sequence>                    <xsd:element name="value" type="xsd:string" minOccurs="0"                                   msdata:Ordinal="1" />                  </xsd:sequence>                  <xsd:attribute name="name" type="xsd:string" use="required" />               </xsd:complexType>             </xsd:element>           </xsd:choice>         </xsd:complexType>       </xsd:element>     </xsd:schema>     <resheader name="resmimetype">       <value>text/microsoft-resx</value>     </resheader>     <resheader name="version">       <value>2.0</value> </resheader> <resheader name="reader">       <value>System.Resources.ResXResourceReader, System.Windows.Forms,               Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089       </value>     </resheader> <resheader name="writer">       <value>System.Resources.ResXResourceWriter, System.Windows.Forms,               Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089       </value>     </resheader>     <data name="litCostLabelResource1.Text" xml:space="preserve">       <value>Currency Sample</value>     </data>     <data name="litDateLabelResource1.Text" xml:space="preserve">       <value>Date Sample</value>      </data>      <data name="litLanguageSettingLabelResource1.Text" xml:space="preserve">       <value>Language Setting</value>      </data>      <data name="litProgrammaticallySetLabelResource1" xml:space="preserve">       <value>Programmatically Set</value> </data> <data name="litWelcomeResource1.Text" xml:space="preserve">       <value>Welcome to Localization</value> </data> <data name="locHeadingResource1.Text" xml:space="preserve">   <value>Internationalization - Culture and UI Culture (VB)</value> </data> <data name="PageResource1.Title" xml:space="preserve">       <value>Internationalization - Culture and UI Culture</value> </data>     <data name="ProgrammaticallySetValue" xml:space="preserve">       <value>Just a Test</value>      </data>  </root> 

Example 17-5. German resource file

 <?xml version="1.0" encoding="utf-8"?>  <root>    <xsd:schema  xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"                  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">     <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />     <xsd:element name="root" msdata:IsDataSet="true">       <xsd:complexType>         <xsd:choice maxOccurs="unbounded">           <xsd:element name="metadata">             <xsd:complexType>               <xsd:sequence>                 <xsd:element name="value" type="xsd:string" minOccurs="0" />                </xsd:sequence>    <xsd:attribute name="name" use="required" type="xsd:string" />    <xsd:attribute name="type" type="xsd:string" />    <xsd:attribute name="mimetype" type="xsd:string" />    <xsd:attribute ref="xml:space" />             </xsd:complexType>           </xsd:element>   <xsd:element name="assembly">             <xsd:complexType>                <xsd:attribute name="alias" type="xsd:string" />   <xsd:attribute name="name" type="xsd:string" />             </xsd:complexType>           </xsd:element>   <xsd:element name="data">             <xsd:complexType>                <xsd:sequence>                  <xsd:element name="value" type="xsd:string" minOccurs="0"                                  msdata:Ordinal="1" />                  <xsd:element name="comment" type="xsd:string" minOccurs="0"                                 msdata:Ordinal="2" />                </xsd:sequence>    <xsd:attribute name="name" type="xsd:string" use="required"                                 msdata:Ordinal="1" />                <xsd:attribute name="type" type="xsd:string"                                  msdata:Ordinal="3" />                <xsd:attribute name="mimetype" type="xsd:string"                                  msdata:Ordinal="4" />                <xsd:attribute ref="xml:space" />             </xsd:complexType>           </xsd:element>   <xsd:element name="resheader">             <xsd:complexType>                <xsd:sequence>                  <xsd:element name="value" type="xsd:string" minOccurs="0"                                 msdata:Ordinal="1" />                </xsd:sequence>                <xsd:attribute name="name" type="xsd:string" use="required" />             </xsd:complexType>           </xsd:element>         </xsd:choice>             </xsd:complexType>        </xsd:element>     </xsd:schema>   <resheader name="resmimetype">     <value>text/microsoft-resx</value>   </resheader>   <resheader name="version">     <value>2.0</value>   </resheader>   <resheader name="reader">     <value>System.Resources.ResXResourceReader, System.Windows.Forms,              Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089     </value>   </resheader>   <resheader name="writer">     <value>System.Resources.ResXResourceWriter, System.Windows.Forms,              Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089     </value>   </resheader>   <data name="litCostLabelResource1.Text" xml:space="preserve">     <value>Währung-Probe</value>   </data>   <data name="litDateLabelResource1.Text" xml:space="preserve">     <value>Datum-Probe</value>    </data>    <data name="litLanguageSettingLabelResource1.Text" xml:space="preserve">     <value>Spracheneinstellung</value>    </data> <data name="litProgrammaticallySetLabelResource1" xml:space="preserve">     <value>Programmatically Satz</value>   </data>   <data name="litWelcomeResource1.Text" xml:space="preserve">     <value>Willkommen zur Lokalisation</value>   </data>   <data name="locHeadingResource1.Text" xml:space="preserve">     <value>Internationalisierung - Kultur und UI Kultur</value>   </data>   <data name="PageResource1.Title" xml:space="preserve">     <value>Internationalisierung - Kultur und UI Kultur</value>   </data>   <data name="ProgrammaticallySetValue" xml:space="preserve">     <value>Nur ein Test</value>    </data>  </root> 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net