Globalization Architecture for ASP.NET

 

Michele Leroux Bustamante
IDesign: .NET Design and Business Solutions

Revised October 2003

Applies to:
    Microsoft® ASP.NET

Summary: Planning ahead for globalization while designing your architecture improves business agility and saves work down the line. This article provides background information on globalization, explains the deployment architecture of .NET resources, and discusses the specific challenges of ASP.NET globalization and how to architect your applications to provide the greatest development productivity and easiest maintenance*.* (15 printed pages)

Download the following code samples discussed in this article:

Contents

What Is Globalization?
.NET Resources
Globalization Architecture for the Web
What's Your Culture?
Formats and Conventions
Where to Go From Here?

While it is difficult to imagine building any application without planning ahead for future globalization, too few actually make the necessary preparations. Companies engage daily in international business over the Internet, and often hear, "Can your application support our global branches as well?" Although a company may not have the business infrastructure (yet) to specifically target other cultures and languages, there are those who still wonder why it should be so hard to do a mere translation of site content, not realizing that the demands on the 1.0 release timeframe prevent the team from exploring application architecture to support future globalization. Customers are likely to have satellite locations in other countries, and large corporations are often evaluating third-party applications as part of a "build or buy" decision—it is important to be able to fulfill the requirements. Lack of preparation can impact business agility. These problems are easily avoided if you design your architecture with globalization in mind up front.

Assuming I've perked your interest in planning ahead, read on and this article will give you some background information on globalization, explain the deployment architecture of .NET resources, and then dive into the specific challenges of Microsoft® ASP.NET globalization and how you should architect your applications, shooting for the greatest development productivity and easiest maintenance.

What Is Globalization?

Globalization is a simple concept, but for the sake of review, let me summarize the basics. Globalization includes the process of first internationalizing the application code, followed by localizing the application to other languages and cultures. The internationalization process makes it possible to translate, store, retrieve, and present application content for any locale, preferably using the same application code base. Locale is the combination of both language and cultural environment, including the format of dates, times, currencies, telephone numbers, and so on. This implies isolating locale-dependent from locale-independent content and also preparing code to dynamically format that content according to locale. The end result of application internationalization supports its localization. Localization means adapting your application to other locales by translating and formatting content according to culture, hopefully without touching the code. This localization process should be relatively painless for the development team if internationalization was part of the initial development cycle. However, the first time around this usually unveils any bugs introduced by internationalization, such as accidentally translated file or field names.

NOTE:    The combined steps of internationalization and localization are often referred to as application globalization, but you will sometimes see the terms "world-ready" and "localizability" used to describe an application that has been internationalized. In addition, the term "culture" is used interchangeably with "locale," where a culture is made up of both language and region-specific preferences.

The ideal goals of internationalization are to:

  • Remove all content from the presentation layer and from the code base. The idea is to have a single code base and a single presentation layout that will present content for any culture.
  • Place content in a location that can be easily translated and that will be programmatically accessible to populate the presentation layer.
  • Store content and user input in a format that can be presented with integrity for the appropriate culture.

Following these steps, the localization process for a particular culture would include:

  • Translation of all content by a third party. How easy this is depends largely on where content is stored and how tightly integrated it is with other parts of the application.
  • Integration of translated content with the application. If not tightly coupled to the presentation layer or code base, this should have minimal impact on developers and user interface designers.
  • Deployment and QA for new cultures.

The rest of this article will present architectural scenarios in support of ASP.NET globalization, while explaining related features of .NET.

.NET Resources

Before getting into the ASP.NET-specific architecture, I'll discuss resources in .NET. I'll begin with how they are used in Windows Forms applications since the Visual Studio.NET IDE has rich support for resources. Later I'll explain the appropriate way to leverage the same concepts in your ASP.NET applications.

Resources for Windows Forms

When you create a Windows application, every form automatically gets an XML resource file (*.resx).

Aa478974.aspnet-globalarch-01(en-us,MSDN.10).gif

Figure 1. Form1.resx is automatically generated for Form1

When you design your form and enter content, if you set the Localizable property of the form to true, this indicates your intention to translate resources. Therefore the IDE modifies your code to load localizable properties from resources to the form's *.resx file.

Aa478974.aspnet-globalarch-02(en-us,MSDN.10).gif

Figure 2. If set to true, localizable properties are automatically added to the form's *.resx file

A predetermined set of localizable properties for any control on the form, such as those related to position, size, and display values, are loaded from the *.resx file when the controls are initialized. The IDE not only places values in the *.resx, but also generates code in the InitializeComponent() method to create a ResourceManager to access those resources. For example, the following code creates an instance of Form1 resources and initializes some of the localizable properties for Label1:

  System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
this.label1 = new System.Windows.Forms.Label();
this.label1.Location = ((System.Drawing.Point)(resources.GetObject("label1.Location")));
   this.label1.Size = ((System.Drawing.Size)(resources.GetObject("label1.Size")));
         this.label1.TabIndex = ((int)(resources.GetObject("label1.TabIndex")));
         this.label1.Text = resources.GetString("label1.Text");
         this.label1.TextAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("label1.TextAlign")));

The IDE modifies the appropriate form's resources and code base as you add, remove, and edit control or form properties from the Properties Window. You can also add your own resource entries to a *.resx file, using the Resource Editor directly.

Instead of hard-coding this message:

  DialogResult result = MessageBox.Show("Are you sure you want to exit?", "Exit Application?", MessageBoxButtons.YesNo);

you would add the strings to an XML resource file, and write code to access the resources by name:

  System.Resources.ResourceManager resources = new 
   System.Resources.ResourceManager(typeof(Form1));
DialogResult result = MessageBox.Show(resources.GetString("quitapplication"), 
            resources.GetString("quit"), MessageBoxButtons.YesNo);

In fact, you'll probably instantiate the ResourceManager only once for each resource type when the form is constructed, saving it to an instance member of the form. In the example thus far, only a Form1 resource type exists for the one and only project Form.

The Resource Editor is accessible by double-clicking on the *.resx file in the Solution Explorer. You can edit in data or XML view, but it is currently somewhat cumbersome. You can easily create new string resources without specifying a type, but if you want to create other strongly typed resources you have to manually enter the full type name (Case-sensitive), including its assembly.

Aa478974.aspnet-globalarch-03(en-us,MSDN.10).gif

Figure 3. Create new string resource entries in the Resource Editor

So, from what I've explained thus far, you can see that resources embedded in the main assembly can be accessed, including new resource entries that you add. Next I'll discuss culture-specific resources and satellite assemblies.

Satellite Assemblies

A satellite assembly will be generated for each and every culture that your application supports. To determine what cultures you will support, it helps to understand the difference between neutral and specific cultures. Every culture has a neutral culture that does not specify a region—for example, "en" is the ISO code for the neutral English culture and "es" is the code for the neutral Spanish culture. When you provide a neutral culture translation, it acts as a default resource for the culture in the event that you do not provide a specific translation for every possible region. Specific cultures couple both language and region—for example, "en-CA" for English (Canada) and "en-US" for English (United States), or "es-EC" for Spanish (Ecuador) and "es-SP" for Spanish (Spain). Localization by region is not always necessary but may be important to your clients, since this can address subtle differences among regions. You should always provide resources for the neutral culture of each language you translate, including English, to provide a catch-all for the language.

NOTE:   Culture codes follow ISO 3166 naming conventions and are discussed with CultureInfo class in the .NET Framework Class Library.

For Windows applications, you can use the IDE to generate form resources for different cultures by setting the form's Language property to the culture you want to support. If you set the Language property to English (Figure 4) a new resource for the neutral English culture will be created (Figure 5).

Aa478974.aspnet-globalarchitect-04(en-us,MSDN.10).gif

Figure 4. Set the language in the Properties dialog box

Aa478974.aspnet-globalarch-05(en-us,MSDN.10).gif

Figure 5. New form resources are generated

Each time the Language property is changed, a new, empty resource file is generated for that language (if one doesn't exist) and for its neutral culture (if a specific culture was selected). All changes made to form and control properties are saved to the selected Language resources. You can also edit your own resource entries (not tied to form elements) using the Resource Editor.

I mentioned that invariant resources are embedded in the main assembly, so what happens to these culture-specific XML resources? They are compiled by the IDE into satellite assemblies. Satellite assemblies contain only resources, no code, and are deployed in subdirectories named for their culture's ISO code.

Therefore you will notice the following naming patterns:

  1. The source of the resource file is named resourcename.culture.resx (that is Form1.en.resx, Form1.en-CA.resx).
  2. These source resources are compiled into intermediate resource files with the naming convention assemblyname.resourcename.culture.resources (that is ColorPicker.Form1.en.resources).
  3. These intermediate resource files are later embedded into their respective satellite assemblies. The satellite assemblies are all named assemblyname.resources.dll (that is ColorPicker.resources.dll), and are deployed to a subdirectory named for the culture's ISO code (that is \en, \en-CA).
  4. The embedded resources are identified in each assembly manifest as assemblyname.resourcename.culture.resources, following #2. The satellite assembly also has in its manifest a culture attribute matching the ISO code for its culture.

The main assembly should always include the invariant culture resources, identified in the manifest as assemblyname.resourcename.resources.

Accessing Resources

So, assuming you have received your translated resources and are ready to build, what do you have to do to run the application for a specific culture? Put another way, how do you access resources for a specific culture?

In fact you can request resources for a particular culture with ResourceManager.GetResourceSet(). This code requests the Spanish (Ecuador) resources by providing a CultureInfo object for that culture (identified by ISO code "es-EC"):

  System.Resources.ResourceManager rm = 
   new System.Resources.ResourceManager(typeof(Form1));

System.Resources.ResourceSet resources = rm.GetResourceSet(new 
   System.Globalization.CultureInfo("es-EC"), true,true);

DialogResult result = MessageBox.Show(resources.GetString("quitapplication"), 
   resources.GetString("quit"), MessageBoxButtons.YesNo);

This works only if your goal is to access "es-EC" above all else. Usually, the desired outcome would be to "fall back" to the neutral culture "es" if the requested resource cannot be found for "es-EC". This is precisely what the ResourceManager is built for. It is capable of falling back from a specific resource to a neutral resource and then to the invariant resource if a neutral resource is not provided (remember that you should provide a full translation in the neutral resource for each culture).

The ResourceManager bases its resource lookup on the current thread's UI culture. After determining the appropriate culture for your current user, you can set the current thread's culture. Otherwise, it will default to the system settings where the code is running.

  System.Threading.Thread.CurrentThread.CurrentUICulture = 
   new System.Globalization.CultureInfo("es-EC");

As long as you modify the CurrentUICulture property prior to accessing resources through the ResourceManager, you will see the correct results.

NOTE:   For more information on resource fallback see the .NET Framework Developer's Guide on Locating and Using Resources for a Specific Culture.

Shared Resources

Most applications have some common resources that are shared among components and/or other applications. Common resources may include error messages, application messages, and dynamically populated text not specific to one form or that may potentially be reused. Even for a particular application, it doesn't make sense to lump resources common to all forms into a specific form's *.resx file. Instead, these resources may be placed into a common string resource.

You can add resources to the project by selecting Assembly Resource File from the Add New Item dialog box. By default, the build action on this resource will embed this resource in the main assembly, which is what you want for this invariant version of the resource. Now, to have the Visual Studio environment build satellite assemblies for this new resource, you must also create a new resource for each culture you want to localize. Though not well documented, it is very easy to do. Simply add new resources to the same project, using the same naming convention used by the form resources. In this example, strings.es.resx was added to hold Spanish translations for my string resources. The IDE will compile this into the Spanish satellite assembly for this project and deploy it to the "es" subdirectory. This new satellite assembly holds two types of resources: ColorPicker.Form1.es.resources and ColorPicker.strings.es.resources. Figure 6 shows the manifest of the \es directory’s satellite assembly when using ILDASM.EXE.

Aa478974.aspnet-globalarch-06(en-us,MSDN.10).gif

Figure 6. The manifest of the \es directories satellite assembly when using ILDASM.EXE

To access these custom resources, create another ResourceManager for the resource type "strings". This time, however, use the constructor that allows you to specify the string name for the resource type, including namespace, and the loaded assembly containing the resource:

  System.Resources.ResourceManager resources = 
   new System.Resources.ResourceManager("ColorPicker.strings",
   System.Reflection.Assembly.GetExecutingAssembly());

Now, if you want to share resources between applications or components, you can take this one step further and create a new class library that contains only resources. For example, I added two XML resources to my CommonRes project: errors.resx and errors.es.resx. When this project is compiled, a resource-only assembly is created (CommonRes.dll), and a satellite assembly for Spanish is generated beneath the \es directory (CommonRes.resources.dll). To use these resources, you can deploy the main and satellite assemblies to the destination application's directory structure. You'll have to load the assembly before accessing its resource items:

  System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom
(String.Format("{0}\\CommonRes.dll", 
   Application.StartupPath));

System.Resources.ResourceManager resources = 
   new System.Resources.ResourceManager("CommonRes.errors",asm);

MessageBox.Show(resources.GetString("InvalidColor"));

You could also deploy shared resources to the global assembly cache (GAC) if they will be shared by applications deployed to the same machine.

ASP.NET Resources

Now that you've seen how the ResourceManager works with satellite assemblies, let's apply these concepts to resources in ASP.NET applications. Fundamentally the concepts are the same, with these few minor differences:

  • By default each Web form gets its own XML resource, as does the Global.asax, but naming conventions are slightly different. Web form resource files are named with the convention webformname.aspx.resx, and the global resource is named Global.asax.resx. Embedded resources still follow the same naming conventions discussed earlier.
  • You must manually add culture-specific resources to the project, since there is currently no IDE support for this. In order to have them compiled into satellite assemblies you must still follow naming conventions discussed earlier. In other words, name your Spanish XML resources webformname.es.resx and Global.es.resx, rather than webformname.aspx.es.resx and Global.asax.es.resx, as one might expect. The name of the *.resx file is mapped directly to the embedded resource name in the assembly manifest, and any deviation will break the ResourceManager's ability to find the resource.
  • The default deployment model for Web applications places the main component assembly beneath the \bin directory of the application, which means satellite assembly subdirectories will be placed beneath \bin (\bin\en, \bin\en-CA, \bin\es, and so on).

At first you might be disappointed by the lack of IDE support to generate Web form resources. However you will realize that it is unlikely you'll want to use individual resources for each Web form, given the number of pages that comprise a Web application, and given that the majority of content on a Web page is not going to be stored in resources. We'll get into the specific challenges of Web application globalization next, and in the process discuss in what ways you will find resources useful in Web deployments.

Globalization Architecture for the Web

Localizing a Windows application makes heavy use of resources for user interface layout and fixed content. Web applications complicate this primarily because there are so many different skill sets involved in the development of a Web application. When you add translation to the mix, things are further complicated. Let's consider the challenges in terms of each of these functional roles: content provider, Web designer, application developer, and translator.

There may be several types of content providers contributing to an application. Some provide glossary terms for menus, page sections, and other marketing language. Others may provide database content describing products and services, company and employee information, whitepapers and articles, subscriber agreements, registration forms, and more.

Web designers are tasked with making content look good. That means they have to have some idea what the content will look like, and have some idea how it will be grouped on each page.

Developers are responsible for turning Web designer output, which may be HTML, graphics and content, into ASP.NET pages. They must determine structure for content storage, design databases, and handle access to content at runtime.

Translators are responsible for translating content, which means they need access to a multitude of data sources. They may work with resource editors and XML editors, and must also find a way to modify database content, either through restricted access to content servers or the classic flat-file exchange.

Of course we must also consider the role of the project manager. Coordinating all of this work output is complex. Apart from coordination issues, there is a common thread that ties all of these roles together, and that is the application content. Where it is stored and how it is accessed affect how each participant in this mess executes their job. The decision driving where content is stored should be made based on some combination of the following trade-offs:

  • Make it easy for content developers and translators to access and modify content
  • Make it easy for Web designers to modify their page layout as content changes and adjustments are necessary
  • Make it possible to support new cultures without modifying Web interface, application components, or the database structure

The Duplicate Site

Traditionally, Web content was embedded in HTML or ASP, with the possibility of some dynamically generated content being served up from a database. It has always been a challenge to translate Web applications for the simple reason that content always came from various sources. When Web content is stored as static HTML, translation is performed directly in the HTML page, and a copy of the Web site is usually stored beneath a localized directory structure. Translators use their own proprietary tools to separate content from HTML tags in order to make their job easier and less error-prone.

The architecture of this approach would usually be to place a complete copy of the site in a subdirectory named for its culture, using the appropriate ISO code. This way, when the application determines the current user's culture, the ISO code can be mapped into the redirection URL to insure the user stays in the correct version of the site.

Figures 7 shows a root application directory that has an \articles subdirectory, as do each of the translated subdirectories.

Aa478974.aspnet-globalarch-07(en-us,MSDN.10).gif

Figure 7. This root application directory has an \articles subdirectory, and each of the translated subdirectories also have \articles subdirectories.

For different cultures, users can be redirected to the default page under the appropriate ISO-named subdirectory. From there, relative links will work if things are well designed. But you must use relative paths carefully. For example, ./ and / will take you to the root of the Web server, which will put you back in the root application directory.

NOTE:   For more information about how ASP.NET handles relative paths, see my article Organization Strategies: Handling Relative Paths in ASP.NET.

One of the code samples supplied with this article, GlobalWebContent, illustrates the translation of site content duplicating the site map for each supported culture:

  • All pages are duplicated beneath culture-specific subdirectories named for the appropriate ISO code.
  • The page hierarchy is completely duplicated with the exception of common images, and a common user control supplying the user interface so select culture.
  • In order to properly format the date displayed on each page, the current culture for each page is specified.

In this scenario, functionality and content changes require modifications to a duplicate set of pages (and possibly code base). Translators have to make their way through HTML tags to modify content, and the Web designer can't work in parallel while the translator is at work without merging difficulties. It is also more likely that the design of the site will shift between translations, simply because the layout is not in a central template. Oh, and let's not forget that defensive QA cycle for each change!

The reality is that many companies still translate page content in this manner. Some say they do this because their site changes so infrequently that it is not worth spending time re-architecting to support an alternative. Others are waiting for better tools for Web designers and developers to work together on a single code base. If you are building a corporate application or portal of significance, chances are you’ll need to globalize. The question is of when you will invest the time. A version 1.0 architectural investment will save you time in subsequent releases.

The Single Code Base

Ideally, if you can architect the site for single code base globalization you will save yourself a lot of headaches in the long term. One of the challenges comes from the disparate sources of Web content. Although plenty of content may be stored in the Web page, there can be plenty of dynamically loaded content from database, XML, or other configurable data sources.

Applications must be designed with a strategy that will dynamically suck content sources into the ASPX, ASCX, and other UI presentation layers. That means making tough decisions about the location of different content. Some content, particularly product information and other possibly changing and significant content, is best stored in a searchable database. This means that for a Web designer to see the page, the query must be executed. Other content, lightweight, infrequently modified, and easily shared, can be stored in XML resources. This may include menu titles, button captions, labels, company tag lines, and mission statements. There may also be XML content that is appropriate to store in XML files stored on the file system that are transformed into a user presentation layer using XSLT.

When content is separated into external resources, XML files, or database tables, the consistent theme of application code would be to select the appropriate resource, file, or table based on the user's culture, by ISO code as I mentioned earlier. In the following sections I'll discuss how you might architect your site to handle disparate sources of content.

Resources

Since the majority of content will be placed in a database or in XML files, how are resources useful in ASP.NET applications? Well, you can leverage the satellite resource fallback process to reference the correct database tables, XML, or other files that populate the interface. For files and tables that are not translated, you simply do not provide a resource entry in the culture-specific satellite resource. In the sample project, GlobalWebData, string.resx includes an entry called “datafile” which indicates the XML file where article content is stored. Following my own advice earlier in this article, strings.en.resx supplies the same information for the neutral English culture. The neutral Spanish resource, strings.es.resx specifies a different XML file. Looking at the following table, if the current culture is set to en-CA or en-US, the framework will roll back to en. For any Spanish culture, es values will be used.

Table 1. Resource Source and Values

Culture Resource Source Value for datafile
Invariant strings.resx data/articles.xml
en strings.en.resx data/articles.xml
en-CA strings.en-CA.resx NA - roll back to en
en-US NA - roll back to en NA
es strings.es.resx data/articles.es.xml

The beauty of using resources to specify filenames for site content is that you can always omit the entry from a specific culture’s resource, if the default resource is suitable. Resources are also useful in Web applications for menu captions, glossary items, and error messages.

This brings us to an interesting point about the distribution of these resource entries. You'll want to separate the translatable from the non-translatable resources to insure that your translator doesn't translate database table names, connection strings, queries, and filenames.

To populate a page with resource content, you would access resources through the ResourceManager (like above) and either bind control properties to the appropriate entries or populate them from the code-behind. The following listing is taken from the hdr.ascx user control, which binds menu items from resource entries. The code behind file instantiates the ResourceManager member m_rm:

Listing from file: hdr.ascx.cs

  public class hdr: System.Web.UI.UserControl
{
   System.Resources.ResourceManager m_rm = null;
   private void Page_Load(object sender, System.EventArgs e)
   {
      if (!this.IsPostBack) this.DataBind();
   }
   private void hdr_DataBinding(object sender, System.EventArgs e)
   {
      m_rm = new System.Resources.ResourceManager(“GlobalWeb.strings”,
         System.Reflection.Assembly.GetExecutingAssembly());
   }
}

Listing from file: hdr.ascx

  <a href=”default.aspx”><%# m_rm.GetString(“home”) %>

In order for the ResourceManager to return the correct resources, the current thread’s UI culture must be set to the user’s culture preference for each HTTP request. The sample application stores each user’s culture preference in the Session object, so in the Application_AcquireRequestState event, that culture setting is retrieved and assigned to the current thread’s CurrentUICulture and CurrentCulture properties.

Listing from file: Global.asax.cs

  protected void Application_AcquireRequestState(object sender, EventArgs e)
{
   CultureInfo ci = (CultureInfo)this.Session[“culture”];
   if (ci != null)
   {
      System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
      System.Threading.Thread.CurrentThread.CurrentCulture =
         CultureInfo.CreateSpecificCulture(ci.Name);
}
}

If the user has made a culture selection from the Web interface, the Session object will have an entry, and each request the code retrieves this entry.

By the way, for ASP.NET applications all assemblies, including the main assembly and satellite assemblies, are shadow-copied to the GAC to enable XCOPY deployment of version updates. This makes it possible to easily add languages or update a particular resource, without restarting the application or Web server.

XML and Database Content

Here is where some of the tough trade-offs come into play. Let's assume we're leveraging resources only for small snippets of content, references to external files, and data sources. Now we get to decide where large amounts of dynamic and static content are stored.

XML is a nice storage medium because you can easily pass XML to a translator and you can use XSLT to present XML in your pages, making it possible for Web designers to see the content in the page while modifying layout. But it is not very useful for complex relational data, and you have to keep an eye on the file IO activity on a busy production site. Furthermore, working with XSLT is an art form of its own, and without a resident expert you may find it a non-trivial process, even if it does benefit the design process.

Of course database content can be highly relational and dynamically queried, and any IO penalty is lumped on the database tier, which is hopefully deployed to a piece of hardware that's built to scale accordingly. Typically translators will either be given a copy of the database tables that should be translated, be provided with secure access to those tables remotely, or may even work with good old flat file extractions. A bigger challenge, but one that is tough to avoid, is the process of working with the Web designer to design layout for content. They are unable to see content layout from the Web designer, since the content is populated at runtime!

My recommendation to overcome the Web designer challenge is to design a prototype of the Web interface, including a high level site map, and use XML to populate this prototype. It is so incredibly easy now to slap together a few lines of code to load an XML file and populate a DataGrid that there's just no reason not to prototype up front. If the Web designer and ASP.NET expert are not the same person (usually they are not), quite a bit of communication will be necessary as the Web designer recommends layout using HTML and/or images, and the ASP.NET developer prototypes reusable controls, user controls, and general architecture for the page workflow.

The benefit of taking these steps is that you gain a better idea of which content should be stored in resources, XML, or the database. In the meantime, content developers are working furiously and component and database developers are building the back-end.

What's Your Culture?

In order to select the right content you need to know the user's culture preference. There are a variety of ways to let your users indicate their preferred culture with Web applications. But it all has to start with that first visit. If your site has a registration page you might collect their preferred culture setting there. If not, you might default to English and let them select a new culture through the UI. Another trick that you can use is to default to the user's language selection in Internet Explorer.

Figures 8 and 9 demonstrate selecting the Language tab in IE and placing a preferred language at the top of the list.

Aa478974.aspnet-globalarch-08(en-us,MSDN.10).gif

Figure 8. Click Languages in the Internet Options dialog box

Aa478974.aspnet-globalarch-09(en-us,MSDN.10).gif

Figure 9. Move your preferred language to the top of the list

The language settings are passed with the request in the Accept-Language HTTP header, in the order specified:

  GET WebForm1.aspx HTTP/1.1
Connection: Keep-Alive
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-us;q=0.8,es-ec;q=0.6,fr-ca;q=0.4,de;q=0.2; ja
Host: localhost
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)

You can access the Accept-Language HTTP header through the HttpRequest object and set the current thread's CurrentCulture and CurrentUICulture like this:

  Thread.CurrentThread.CurrentCulture = 
   CultureInfo.CreateSpecificCulture(this.Request.UserLanguages[0]);
   Thread.CurrentThread.CurrentUICulture = 
               new CultureInfo(this.Request.UserLanguages[0]);

Setting CurrentUICulture will dictate how the ResourceManager will perform its resource discovery and fallback process. The CurrentCulture property relates to how data is formatted for the current thread. CreateSpecificCulture is a static method that generates a specific culture from the culture code. An attempt to set the CurrentCulture to a neutral culture such as “es”, would trigger an exception, since a specific region is required for formatting. CreateSpecificCulture will generate a specific culture for non-specific cultures, which means it will return “es-ES” if “es” is the supplied culture code. Otherwise, the requested specific culture object is generated.

This is one way to determine the user’s preferences if you do not gather this information through some form of registration process, but make sure you provide them a way to change these settings!

Formats and Conventions

Although this article deals mostly with architecture, it is important to note that content translation is not the only consideration for presentation. We must also consider how dates and times, calendars, numbers, and currency are formatted and how sorting and string comparison are executed. Fortunately, this is handled seamlessly by the .NET framework's built-in formatter classes when you set the current thread's CurrentCulture property as mentioned above.

If you use the DateTime or Calendar classes, they automatically respect the CurrentCulture setting for the thread. That is why the following code properly formats the date for each culture in both code samples:

  <%=DateTime.Now.ToLongDateString() %>

Similarly, when formatting numeric values, including currency, the NumberFormatInfo class is leveraged by the framework (seamlessly) to respect the CurrentCulture.

The GlobalWebData sample project handles this by setting the current thread’s CurrentCulture property, but in the first sample project, GlobalWebContent, it is handled by hard-coding the CurrentCulture settings for each culture-specific subdirectory. This can be done by setting the CultureInfo attribute of either the @ Page directive, or of the particular directory’s <globalization> element in the Web.config file. The sample code demonstrates setting this in a number of different ways.

Aside from formatting, a more basic issue is likely to arise with data formats and storage. I've frequently come across databases that were designed to handle five-character zip codes and 10 digit phone numbers, for example. Accommodating international postal codes and phone numbers causes a serious problem. There aren't ISO standards for everything, but you must insure field sizes can accommodate worst-case scenarios.

Where to Go From Here?

This article has focused mostly on some high-level architectural decisions that drive the plans behind your globalization efforts. When it comes to implementation, you must also consider the finer details such as file encoding, request and response encoding, fonts, keyboard input and more.

To demonstrate the topics discussed in this article, I have mentioned a number of code samples (GlobalWebContent.msi, GlobalWebDataSample.msi, and GlobalWinAppSample.msi). You can also view additional code samples on the .NET Dashboard Web site. Another great overview resource for some of the topics not discussed in this article is Microsoft’s Global Development and Computing Portal.

In the meantime, if you take but one message with you, it is that even if you cannot foresee the need to prepare for globalization today, the pain of re-architecting your application later to support globalization is something you should avoid. When you least expect it, and need to turn around support for a new language, this will pay off in spades and provide you with agility not possible when your team is forced to make intrusive changes to the application architecture.

About the Author

Michele Leroux Bustamante is an Associate of IDesign Inc., a Microsoft Regional Director, a member of the International .NET Speakers Association (INETA) and a published author. At IDesign, Michele contributes her diverse background to .NET training and high-end corporate consulting. She focuses on the C# language, .NET Framework architecture, ASP.NET, and Web Services; and she also provides guidance to technology executives. She knows enough Java to be very dangerous and to advise the Web Services program at UCSD Extension. Contact her at mlb@idesign.net or visit IDesign: .NET Design and Business Solutions to find out more. Also, visit .NET Dashboard to subscribe to her monthly .NET newsletter.

© Microsoft Corporation. All rights reserved.