Localization With ASP.NET


With ASP.NET applications, localization happens in a similar way to Windows applications. Chapter 26,"ASP.NET Pages," discusses the functionality of ASP.NET applications; this section discusses the localization issues of ASP.NET applications. ASP.NET 2.0 and Visual Studio 2005 have many new featuresto support localization. The basic concepts of localization and globalization are the same as discussed before. However, some specific issues are associated with ASP.NET.

As you've already learned, with ASP.NET you have to differentiate between the user interface culture and the culture used for formatting. Both of these cultures can be defined on a Web and page level, as well as programmatically.

To be independent of the Web server's operating system, the culture and user interface culture can be defined with the <globalization> element in the configuration file web.config:

 <configuration> <system.web> <globalization culture="en-US" uiCulture="en-US" /> </system.web> </configuration> 

If the configuration should be different for specific Web pages, the Page directive allows assigning the culture:

 <%Page Language="C#" Culture="en-US" UICulture="en-US" %> 

If the page language should be different depending on the language setting of the client, the culture of the thread can be set programmatically to the language setting that is received from the client. ASP.NET2.0 has an automatic setting that does just that. Setting the culture to the value Auto sets the culture of the thread depending on the client's settings.

 <%Page Language="C#" Culture="Auto" UICulture="Auto" %> 

In dealing with resources, ASP.NET differentiates resources that are used for the complete Web site and resources that are only needed within a page.

If a resource is used within a page, you can create resources for the page by selecting the Visual Studio 2005 menu Tools Generate Local Resource in the design view. This way the subdirectory App_Local Resources is created where a resource file for every page is stored. These resources can be localized similarly to Windows applications. The association between the Web controls and the local resource files happen with a meta:resourcekey attribute as shown here with the ASP.NET Label control. LabelResource1 is the name of the resource that can be changed in the local resource file:

 <asp:Label  Runat="server" Text="Label" meta:resourcekey="LabelResource1"></asp:Label> 

For the resources that should be shared between multiple pages, you have to create a subdirectory, Application_Resources. In this directory you can add resource files, for example, Messages.resx with its resources. To associate the Web controls with these resources, you can use Expressions in the property editor. Clicking the Expressions button opens the Expressions dialog (see Figure 17-22). Here you can select the expression type Resources, add the name of the class (which is the name of the resource file — here, a strongly typed resource file is generated), and the name of the ResourceKey, which is the name of the resource.

image from book
Figure 17-22

In the ASPX file, you can see the association to the resource with the binding expressions syntax <%$:

 <asp:Label  Runat="server"  Text="<%$ Resources:Messages, String1 %>"> </asp:Label> 

A Custom Resource Reader

With the resource readers that are part of .NET Framework 2.0, you can read resources from resource files and satellite assemblies. If you want to put the resources into a different store (such as a database), you can use a custom resource reader to read these resources.

To use a custom resource reader, you also need to create a custom resource set and a custom resource manager. However, doing this is not a hard task, because you can derive the custom classes from existing classes.

For the sample application, you have to create a simple database with just one table for storing messages that has one column for every supported language. The following table lists the columns and their corresponding values.

Key

Default

de

es

fr

it

Welcome

Welcome

Willkommen

Recepcin

Bienvenue

Benvenuto

Good Morning

Good Morning

Guten Morgen

Buonas daz

Bonjour

Buona Mattina

Good Evening

Good Evening

Guten Abend

Buonas noches

Bonsoir

Buona sera

Thank you

Thank you

Danke

Gracias

Merci

Grazie

Goodbye

Goodbye

Auf Wiedersehen

Adis

Au revoir

Arrivederci

For the custom resource reader, you create a component library with three classes. The classes are DatabaseResourceReader, DatabaseResourceSet, and DatabaseResourceManager.

Creating a DatabaseResourceReader

With the class DatabaseResourceReader, you define two fields: the data source name dsn that is needed to access the database and the language that should be returned by the reader. These fields are filled inside the constructor of this class. The field language is set to the name of the culture that is passed with the CultureInfo object to the constructor:

 public class DatabaseResourceReader : IResourceReader { private string dsn; private string language; public DatabaseResourceReader(string dsn, CultureInfo culture) { this.dsn = dsn; this.language = culture.Name; } 

A resource reader has to implement the interface IResourceReader. This interface defines the methods Close() and GetEnumerator() to return an IDictionaryEnumerator that returns keys and values for the resources. In the implementation of GetEnumerator(), create a Hashtable where all keys and values for a specific language are stored. Next, you can use the SqlConnection class in the namespace System.Data.SqlClient to access the database in SQL Server. Connection.CreateCommand() creates a SqlCommand() object that you use to specify the SQL SELECT statement to access the data in the database. If the language is set to de, the SELECT statement is SELECT [key], [de] FROM Messages. Then you use a SqlDataReader object to read all values from the database, and put it into a Hashtable. Finally, the enumerator of the Hashtable is returned.

Note

For more information about accessing data with ADO.NET, see Chapter 19, "Data Access with .NET."

 public System.Collections.IDictionaryEnumerator GetEnumerator() { Hashtable dict = new Hashtable(); SqlConnection connection = new SqlConnection(dsn); SqlCommand command = connection.CreateCommand(); if (language == "") language = "Default"; command.CommandText = "SELECT [key], [" + language + "] " +  "FROM Messages"; try { connection.Open(); SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) { if (reader.GetValue(1) != System.DBNull.Value) dict.Add(reader.GetString(0), reader.GetString(1)); } reader.Close(); } catch   // ignore missing columns in the database { } finally { connection.Close(); } return dict.GetEnumerator(); } public void Close() { } 

Because the interface IResourceReader derives from IEnumerable and IDisposable, the methods GetEnumerator(), which returns an IEnumerator interface, and Dispose() must be implemented, too:

 IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } void IDisposable.Dispose() { } } 

Creating a DatabaseResourceSet

The class DatabaseResourceSet can use nearly all implementations of the base class ResourceSet. You just need a different constructor that initializes the base class with our own resource reader, DatabaseResourceReader. The constructor of ResourceSet allows passing an object by implementing IResourceReader; this requirement is fulfilled by DatabaseResourceReader:

 public class DatabaseResourceSet : ResourceSet { internal DatabaseResourceSet(string dsn, CultureInfo culture) : base(new DatabaseResourceReader(dsn, culture)) { } public override Type GetDefaultReader() { return typeof(DatabaseResourceReader); } } 

Creating a DatabaseResourceManager

The third class you have to create is the custom resource manager. DatabaseResourceManager derives from the class ResourceManager, and you only have to implement a new constructor and override the method InternalGetResourceSet().

In the constructor, create a new Hashtable to store all queried resource sets and set it into the field ResourceSets defined by the base class:

 public class DatabaseResourceManager : ResourceManager { private string dsn; public DatabaseResourceManager(string dsn) { this.dsn = dsn; ResourceSets = new Hashtable(); } 

The methods of the ResourceManager class that you can use to access resources (such as GetString() and GetObject()) invoke the method InternalGetResourceSet() to access a resource set where the appropriate values can be returned.

In the implementation of InternalGetResourceSet(), check first if the resource set for the culture queried for a resource is already in the hash table; if it already exists, return it to the caller. If the resource set is not available, create a new DatabaseResourceSet object with the queried culture, add it to the hash table, and return it to the caller:

 protected override ResourceSet InternalGetResourceSet( CultureInfo culture, bool createIfNotExists, bool tryParents) { DatabaseResourceSet rs = null; if (ResourceSets.Contains(culture.Name)) { rs = ResourceSets[culture.Name] as DatabaseResourceSet; } else { rs = new DatabaseResourceSet(dsn, culture); ResourceSets.Add(culture.Name, rs); } return rs; } } 

Client Application for DatabaseResourceReader

How the class ResourceManager is used from the client application here does not differ a lot from the previous use of the ResourceManager class. The only difference is that the custom class DatabaseResourceManager is used instead of the class ResourceManager. The following code snippet demonstrates how you can use your own resource manager.

A new DatabaseResourceManager object is created by passing the database connection string to the constructor. Then you can invoke the GetString() method that is implemented in the base class as you did earlier, passing the key and an optional object of type CultureInfo to specify a culture. In turn, you get a resource value from the database, because this resource manager is using the classes DatabaseResourceSet and DatabaseResourceReader.

 DatabaseResourceManager rm = new DatabaseResourceManager( "server=localhost;database=LocalizationDemo;trusted_connection=true"); string spanishWelcome = rm.GetString("Welcome",  new CultureInfo("es-ES")); string italianThankyou = rm.GetString("Thank you",  new CultureInfo("it")); string threadDefaultGoodMorning = rm.GetString("Good Morning"); 




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

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