You are on page 1of 6

3/16/13

Creating multilingual websites - Part 1 - CodeProject

Sign up for our free weekly Web Developer


Newsletter.
9,736,988 members
(38,569 online)

home

articles

quick answers

discussions

Sign in

features

community

Articles Web Development ASP.NET General

Article
Browse Code
Stats

Comments &
Discussions (82)

Search for articles, questions, tips

Prev Next

Creating multilingual websites - Part 1


By Karl Seguin, 25 Aug 2004
4.81 (75 votes)

Revisions
Alternatives

help

Download sample code - 30.6 Kb


Download sample code in VB.NET - 44.1 Kb
Download demo code - 16.6 Kb

Table of Contents
Introduction
Before we get started
Basics
Cultures
Download Dummy Application
Why not use what's available as-is
Building a better Resource Manager

About Article
Extend the existing
globalization capabilities of
.NET to create flexible and
powerful multilgual web
sites. First, create a custom
ResourceManager, and then
create custom localizedcapable server controls to
easily deploy multilingual
functionality.
Type

Article

Licence
First Posted

15 Aug 2004

Views

302,932

Bookmarked

283 times

.NET1.0 .NET1.1
VS.NET2003 C# VB , +

GetString()
GetResource()
LoadResource()
Other enhancements()
Localized Controls
ILocalized
LocalizedLiteral
Rinse, wash and repeater
Using Localized Controls
Download
I'd like to thank Jean-Claude Manoli for developing his C# Code format, which Ii used in writing this tutorial.

Introduction
Developing websites to support multiple languages can be a challenging and time-consuming process. With standard
HTML pages, this involves creating and maintaining duplicate versions of each page for each supported language as
well as having the language content embedded into the HTML, where content cant easily be edited. While the process
improved slightly with the introduction of scripting technologies such as ASP and PHP, no significant development or
maintenance time was saved. For those of you who have to develop multi-lingual interfaces and applications, youll be
glad to know that ASP.NET makes things considerably easier.
ASP.NET and the .NET framework ship with support for multilingual applications, namely in the form of Resource Files,
the Cult
ureInfoclass, and the System.Globalizationand System.Resources.ResourceManager
namespaces. Unfortunately, in its present state, localizing content in ASP.NET applications is still a tedious process. Like
everything else in .NET though, the object model and sheer power available makes extending what's already available
and developing new functionality to support better localization easy as 1 - 2 - 3.
In this first part, we'll develop a custom resource manager which avoids the limitation of .NET Assembly Resource Files
as well as extend a number of classes to easily support localization. In the second part, well spend more time talking
about creating multilingual applications, specifically looking at database implementations and techniques.
By the end of this tutorial, you should be able to create multilingual applications with a minimum of work and
maintenance, and be able to easily add new languages to it later on.

Before we get started


If you aren't familiar with localization in .NET, don't worry. This tutorial mostly skips what's available in .NET and talks
about alternatives to make the job easier. There are a couple of core principals you should know though. (Even if you
don't know the basics, you can skip this section and download the simple application I've created to showcase the core
functionality, playing with it will probably be the best way to understand).

Basics
The way localization works in .NET is fairly straightforward. Content is stored in pretty simple XML files called Resource

www.codeproject.com/Articles/7998/Creating-multilingual-websites-Part-1

1/6

3/16/13

Creating multilingual websites - Part 1 - CodeProject


The way localization works in .NET is fairly straightforward. Content is stored in pretty simple XML files called Resource
Files. You create a Resource File for each supported language (more can be added later on). When the application is
compiled, the resource files are embedded into assemblies - the default resource file is embedded in the main assembly
(.dll file); language-specific resource files are embedded into their own assemblies called satellite assemblies. Resource
files are pretty simple and look a lot like a hashtable, they have a name and a value - the name is the same for all
resource files, and the value is a language specific translation of some content. In essence, this allows you to use the
System.Resources.ResourceManagerclass to do things like:
Collapse | Copy Code

1: UserNameLabel.Text = myResourceManager.GetString("Username");
2: UserNameValidator.ErrorMessage =
myResourceManager.GetString("RequiredUsername");

The resource manager will automatically load the right resource file based on the current thread's Cu
rrentCulture
value - more on this in the next section. Hopefully, you are already seeing a lot of potential. Some of the key highlights
are:
Content is separated into simple XML files
There's a separate XML file for each supported language
The code to load values is relatively simple and short
The R
esourceManagerclass automatically retrieves the content from the right XML file based on the thread's
CurrentCulturevalue
You can easily have 1 actual page, for N supported languages.

Top News
SimCity Burning: A
Warning... on The Dangers
of Always-Online DRM
Get the Insider News free each
morning.

Related Articles
Creating animations with
Dundas Chart for ASP.NET
Smarter Data Labels with
Dundas Chart SmartLabels
Understanding Chart Areas with
Dundas Chart for .NET

Cultures

Using screensavers inside the


Windows Media Player

It's important to have a good understanding of Cultures since our new code will make use of them - specifically the
System.Globalization.CultureInfoclass, and the culture name value which follows the RFC 1766 naming
standard. Basically, you create a new C
ultureInfoinstance by specifying the culture name in the constructor:

Making Sense of Geographic


Data with Dundas Map and
AJAX

Collapse | Copy Code

1: CultureInfo c = new CultureInfo("en-US");


//creates a CultureInfo instance for American English

Handling connection
notification between a desktop
machine and Windows CE
based devices
Create data-driven applications
with the Hera Application
Framework

2: CultureInfo c = new CultureInfo("en-AU");


//creates a CultureInfo instance for Australian English

Towards the self-documenting


database: extended properties

3: Cultureinfo c = new CultureInfo("he-IL");


//creates a CultureInfo instance for Israel Hebrew

Once you have a C


ultureInfoinstance, you can set the current thread's UIcultureto it, which will make your
ResourceManagerin the above code automatically fetch the content from the right XML resource file.
Collapse | Copy Code

1: CultureInfo c = new CultureInfo("en-US");


//creates a CultureInfo instance for American English
2: System.Threading.Thread.CurrentThread.CurrentCulture = c;
//Will automatically format dates and such
3: System.Threading.Thread.CurrentThread.CurrentUICulture = c;
//Used by the ResourceManager to get the correct XML File

In part 2, we'll discuss ways to figure out which culture to load, but for now, it can be as simple as passing a code in the
QueryString. For example, when lang=f is present, the French Canadian culture should be used. The other key factor is
where to do all of this. The simplest and most logical place is in the Global.Asax's B
egin_Request.

Download dummy application


The best way to understand the basics is to play with some code. I've created an extremely basic VB.NET web
application to demonstrate the basic principles. Download it and play with it. Look at the structure of the 3 resource
files, the codebehind for index.aspx, and the code in global.asax. Download sample - 16.6 Kb.

Why not use what's available as-is?


While it's certainly possible to develop a multilingual application with the tools provided with ASP.NET, there are a
number of limitations which make the task less than streamlined. Some of the key problems are:
Resource files are embedded into [satellite] assemblies
Resource files can't return strongly-typed objects
Web controls aren't easily hooked with resource files

Digital Signatures and PDF


Documents
WMP Power Hour APP
Merge Landscape and Portrait
PDFs using ASP.NET
How to conduct an SMS survey
using a cell phone connected
SMS gateway and MS Access
Using Barcodes in Documents
Best Practices
How to Retrieve EMC Centera
Cluster/Pool Capabilities
"Hey! Is That My Car? How to
Sharpen a QuickBird Satellite
Image Using DotImage"
Integrate your SharePoint
environment into the open
standards-based WebSphere
Portal platform using the Visual
Studio IDE
Retrieving and Storing Call
History
Knit - A Visual Studio Add-In
NetMX - a JMX port to the .NET
world
UITestBench, a lightweight UI
testing library
XColorDialog - an MFC color
picker control that displays a
color hexagon and a color
spectrum

While the list might seem small, the above three issues can be quite serious - with the first being the worst. For
example, since resource files are embedded into assemblies, it's very difficult to ship a product which provides the client
with the flexibility to change the content - a feature offered by many products. At my previous job, every time the
translation department wanted to change some text, we'd need to recompile the entire application, stop 20 web
servers, and copy the .dll into the bin folder - a frustrating process.

Building a better Resource Manager


Our first task is to build a better Resource Manager which won't cause our Resource Files to be embedded into
assemblies. This will allow Resource Files to be easily edited in a production or client environment. Our core
functionality will be located in three functions:
1. The public method G
etStringwhich is used throughout the application to access the required resource.
2. The private method G
etResourcewhich gets a HashTableeither from the cache or by calling LoadResource.
3. The private method L
oadResourcewhich parses the XML file and stores it into the cache.

www.codeproject.com/Articles/7998/Creating-multilingual-websites-Part-1

2/6

3/16/13

Creating multilingual websites - Part 1 - CodeProject

3. The private method L


oadResourcewhich parses the XML file and stores it into the cache.

GetString()
Collapse | Copy Code

1:
public static string GetString( string key) {
2:
Hashtable messages = GetResource();
3:
if (messages[key] == null){
4:
messages[key] = string.Empty;
5: #if DEBUG
6:
throw new ApplicationException("Resource" +
" value not found for key: " + key);
7: #endif
8:
}
9:
return (string)messages[key];
10:
}

The method accepts a single argument, the key of the resource we want to get. It then retrieves a H
ashTableof
content using G
etResource, which is culture-aware and returns us the correct HashTable. If the requested key
doesnt exist, well throw an exception if the application is in DEBUG mode, else well simply return an empty string.

GetResource()
Collapse | Copy Code

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:

private static Hashtable GetResource() {


string currentCulture = CurrentCultureName;
string defaultCulture =
LocalizationConfiguration.GetConfig().DefaultCultureName;
string cacheKey = "Localization:" +
defaultCulture + ':' + currentCulture;
if (HttpRuntime.Cache[cacheKey] == null){
Hashtable resource = new Hashtable();
LoadResource(resource, defaultCulture, cacheKey);
if (defaultCulture != currentCulture){
try{
LoadResource(resource, currentCulture, cacheKey);
}catch (FileNotFoundException){}
}

}
return (Hashtable)HttpRuntime.Cache[cacheKey];

The G
etResource()method is slightly more complicated. Its goal is to retrieve a HashTablewhich can be looked up
by a key to retrieve a value. The method will first look to see if the H
ashTablehas already been loaded and cached
[line: 5]. If so, it simply returns the value from the cache. Otherwise, it will use L
oadResource()to parse the
appropriate XML file [lines: 6-13]. Something worthy of noting is that the "appropriate XML file" is actually a mix of
the XML file for the current culture as well as the one for the default culture. The default culture is specified in the
configuration file [line: 3], and the current culture is retrieved from the current thread's current culture [line: 2].
First, the default culture is loaded [line: 8], and then the current culture is loaded [line: 11]. This means if a key is
defined in both XML files (which most should be), the default value will be overridden by the culture-specific value. But
if it doesnt exist in the culture-specific value, the default value will be used.

LoadResource()
Collapse | Copy Code

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:

private static void LoadResource(Hashtable resource,


string culture, string cacheKey) {
string file =
LocalizationConfiguration.GetConfig().LanguageFilePath +
'\\' + culture + "\\Resource.xml";
XmlDocument xml = new XmlDocument();
xml.Load(file);
foreach (XmlNode n in xml.SelectSingleNode("Resource")) {
if (n.NodeType != XmlNodeType.Comment){
resource[n.Attributes["name"].Value] = n.InnerText;
}
}
HttpRuntime.Cache.Insert(cacheKey, resource,
new CacheDependency(file), DateTime.MaxValue, TimeSpan.Zero);
}

LoadResourceloads the XML file [line: 4] (it gets the root path from our configuration file [line: 2]) and simply
parses it while loading the values into our H
ashTable[line: 5 - 9]. Finally, the HashTableis stored in the Cache [line:
10].

Other enhancements
Wrappers
There are a number of minor enhancements which can be done to our Resource Manager class. For example, I build
bilingual webpages in English and French. Annoyingly, in English, a colon is always glued to the word it follows, but in
French there has to be a space. For example:
Collapse | Copy Code

Username: //English
Nom d'utilisateur : //French

This means, the colon needs to be localized. Instead of using the Ge


tString()method, we can simply build a
wrapper:

www.codeproject.com/Articles/7998/Creating-multilingual-websites-Part-1

3/6

3/16/13

Creating multilingual websites - Part 1 - CodeProject


Collapse | Copy Code

1:
2:
3:

public static string Colon {


get { return GetString("colon"); }
}

In our English resource file, the colon would simply be ':', while in the French one, it would have a space ' :'.
Strongly-typed resources
The reason we use H
ashTableinstead of a NameValueCollectionis because the Resource Manager class can be
expanded to return strongly-typed objects. For example, you might have localized help content which is more than just
a single value. It might have a title, an example, and the help text. While exploring this is beyond the scope of this
article (perhaps a part 3??), the capability exists.

Localized Controls
Our next goal is to make our life easier when developing a website by expanding existing server controls (literals, labels,
buttons) to be localization-aware. We begin by creating a very simple interface our new controls will implement.

ILocalized
Collapse | Copy Code

1:

2:
3:
4:

public interface ILocalized{


string Key {get; set; }
bool Colon {get; set; }
}

ILocalizeddefines a Keyproperty which will be passed to our ResourceManager's GetString()method. In order


to show how you can expand these classes to fit your own needs, I've also included a C
olonproperty as a boolean,
which will tell our controls if they should append a colon at the end of their value.

LocalizedLiteral
Collapse | Copy Code

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:

public class LocalizedLiteral : Literal, ILocalized {


#region fields and properties
private string key;
private bool colon = false;
public bool Colon {
get { return colon; }
set { colon = value; }
}
public string Key {
get { return key; }
set { key = value; }
}
#endregion

protected override void Render(HtmlTextWriter writer) {


base.Text = ResourceManager.GetString(key);
if (colon){
base.Text += ResourceManager.Colon;
}
base.Render(writer);
}

The first web control that we'll look at making localization-aware is the oft-used
System.Web.UI.WebControls.Literal. First, we make our class inherit from the Literalcontrol and inherit our
ILocalizedinterface [line: 1]. Next, we implement the Keyand Colonproperties as defined in the ILocalized
interface [line: 3 - 14]. Finally, we override the R
endermethod of our base Literalclass and use the
ResourceManager's GetString() method and Colonproperty to fully localize our control [line: 19 - 22]. Don't
forget to call the base class' R
ender()method afterwards to let it work its magic [line: 23].

Rinse, wash and repeater


You can copy and paste the same code over and over again and simply change the name of the class and what it
inherits from; for example, let's do a localized button:
Collapse | Copy Code

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:

public class LocalizedButton : Button, ILocalized {


#region Fields and Properties
private string key;
private bool colon = false;
public string Key {
get { return key; }
set { key = value; }
}
public bool Colon {
get { return colon; }
set { colon = value; }
}
#endregion
protected override void Render(HtmlTextWriter writer) {

www.codeproject.com/Articles/7998/Creating-multilingual-websites-Part-1

4/6

3/16/13

Creating multilingual websites - Part 1 - CodeProject


20:
21:
22:
23:
24:
25:
26:

base.Text = ResourceManager.GetString(key);
if (colon){
base.Text += ResourceManager.Colon;
}
base.Render(writer);

Notice that only the two bolded words have changed.


When desired, you can expand the functionality. For example, it isn't uncommon to have a Li
nkButtonwhich pops up
a JavaScript confirmation box when deleting something. We can easily achieve this by creating a 2nd key property:
Collapse | Copy Code

1: using System.Web.UI;
2: using System.Web.UI.WebControls;
3:
4: namespace Localization {
5:
public class LocalizedLinkButton : LinkButton, ILocalized {
6:
#region Fields and Properties
7:
private string key;
8:
private bool colon;
9:
private string confirmKey;
10:
11:
public string ConfirmKey {
12:
get { return confirmKey; }
13:
set { confirmKey = value; }
14:
}
15:
public string Key {
16:
get { return key; }
17:
set { key = value; }
18:
}
19:
public bool Colon {
20:
get { return colon; }
21:
set { colon = value; }
22:
}
23:
#endregion
24:
25:
protected override void Render(HtmlTextWriter writer) {
26:
if(key != null){
27:
Text = ResourceManager.GetString(key);
28:
if (colon) {
29:
Text += ResourceManager.Colon;
30:
}
31:
}
32:
if (confirmKey != null) {
33:
Attributes.Add("onClick", "return confirm('" +
ResourceManager.GetString(confirmKey).Replace("'",
"\'") + "');");
34:
}
35:
36:
base.Render(writer);
37:
}
38:
39:
}
40: }

Using Localized Controls


You use the localized controls like any other server control. First, register the control on the page:
Collapse | Copy Code

1: <%@ Register TagPrefix="Localized"


Namespace="Localization" Assembly="Localization" %>

Then, without having to write any code, you can simply add the control either by drag and dropping it in the designer,
or in the HTML mode by typing:
Collapse | Copy Code

1: <Localized:LocalizedLiteral id="passwordLabel"
runat="server" Key="password" Colon="True" />
2: <Localized:LocalizedButton id="login"
runat="server" colon="false" Key="login" />

Download
The best thing to do now is to play a bit with some code. I've again created a sample site (similar to the previous one),
but this time using our new Resource Manager class and Localized controls. You might need to change the
web.config's l
anguageFilePathproperty to point to the right folder. Download sample site code - 30.6 Kb.

Contact
Karl Seguin karlseguin@hotmail.com - 8/14/2004.

License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files
themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here

About the Author

Karl Seguin
www.codeproject.com/Articles/7998/Creating-multilingual-websites-Part-1

5/6

3/16/13

Creating multilingual websites - Part 1 - CodeProject

Karl Seguin
Canada
Member

No Biography provided

Article Top

Like

Tw eet

Sign Up to vote Poor

Excellent

Vote

Comments and Discussions


Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum
Profile popups Spacing Relaxed

Noise Very High

Go

Layout Normal

Per page 10

Update

First Prev Next

multilingual problem

sajidzafar

25 Mar '12 - 0:11

VS compatibility

kshitij verma

23 Mar '12 - 2:06

My vote of 5

BSRK

17 Feb '12 - 12:20

My vote of 5

BSRK

17 Feb '12 - 12:18

Still apply with .net 4.0?

BangkokBoy

12 Apr '11 - 17:12

Error Rendering Control and Error Creating Control

jocelynn

Why 20 web servers

Soumini Ramakrishnan

21 May '08 - 18:23

tungsir

25 Apr '08 - 22:43

Naveen Kumar M

14 Jun '07 - 19:42

11 Mar '09 - 1:15

hava an error,can u help me


I want to use multiple files per culture how can i...?
Re: I want to use multiple files per culture how can i...?
Last Visit: 31 Dec '99 - 18:00
General

News

Permalink | Advertise | Privacy | Mobile


Web02 | 2.6.130311.1 | Last Updated 26 Aug 2004

Last Update: 16 Mar '13 - 3:21


Suggestion

Question

Karl Seguin
Refresh

Bug

Answer

Layout: fixed | fluid

www.codeproject.com/Articles/7998/Creating-multilingual-websites-Part-1

Joke

16 Jun '07 - 3:08


1 2 3 4 5 6 7 8 9 Next

Rant

Admin
Article Copyright 2004 by Karl Seguin
Everything else Copyright CodeProject, 1999-2013
Terms of Use

6/6

You might also like