Download SampleCode - 99.4 KB
Introduction
A website which can support many languages and change according to the culture of a specific geographical location is not a new topic. Though there are many articles which explain the subject well, I still see that many of them are complicated to "beginners", and none of them provide a dynamic way to manage available languages on the website. This article aims to approach a more dynamic way to manage languages regarding globalization/localization concepts.
Background
Using resource and globalization/localization may not a best choice in providing an ability to support multiple languages in the website. However, the resource has an advantage that binany objects can also be stored (image for instance), and ASP.Net provides a way to map controls on the webpage with resources by using Expression.
Therefore, and since this article was written to target on "beginners" rather than experienced developers, I would appreciate to listen to all open valuable suggestions. Thanks for your interests and supports.
Now, back again to requirements that most of developers or customers may ask for:
1. Website has to be able to change to other languages. In the scope of this article, resource
will be used.
And the change should only effect the current visitor browser. You may think of SESSION
, sure.
2. Sometimes French in France and French which spoken in Canada are different, same to English-British and English-US. ASP.Net introduces a concept called CultureInfo
.
3. What happens if language file (resource file) in a specific language (that visitor chose) doesn't have text value which needed to display on a specific position on the website? Luckily, ASP.Net introduces a concept called FallBack
. There is a resource file as a backup (or default) for the others. If no key is found in a chosen resource, ASP.Net will take the value from there.
4. Developers would like to put language files (resources) into an easy-managed hierachy folders structure but somehow the website should be able to detect what languages are currently available (by looking at available language files) and it should understand which resource file is a language file and which not. How? The following section will go more in details to explain how to approach that, together with an easy way to create a multilingual website.
Using resources
First, let create resource files (*.resx) and put in different folders, but those should be inside App_GlobalResources
folder in ASP.Net 2.0.
Because languages also depend upon the geographical location. For e.g.: Linguistically speaking of Canadian French is quite different from French spoken in France. Therefore the language needs to be associated with the particular region where it is spoken, and this is done by using locale (language + location). Our resource files therefore have to be in the format of PrefixName.language-location.resx. For e.g.: fr
is the code for French language. fr-FR
means French language in France. So fr specifies only the language whereas fr-FR is the locale. Similarly fr-CA
defines another locale implying French language and culture in Canada. Here we have lang.fr-FR.resx.
Then, we need a way to load them at runtime when the user changes culture dynamically. Fortunately implementing this in ASP.NET 2.0 is quite easy. See the code below:
String welcome = Resources.lang.Welcome;
But the recommended way to localize the UI controls on a page is to set this using the VS IDE. Select the label control, go to Properties
window, select Expressions->Text
. Then choose Resources
from the drop down and enter the class name and the Resource key
:
Detect and load available languages
Now we need a smart way to detect available language files we are having on our website and when the visitor change to use another language, text in all webpages should also changed.
We build a class to manage them, call it LanguageManager
:
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
public sealed class LanguageManager
{
public static readonly CultureInfo DefaultCulture = new CultureInfo("en-US");
public static readonly CultureInfo[] AvailableCultures;
static LanguageManager()
{
List<string> availableResources = new List<string>();
string resourcespath = Path.Combine(System.Web.HttpRuntime.AppDomainAppPath, "App_GlobalResources");
DirectoryInfo dirInfo = new DirectoryInfo(resourcespath);
foreach (FileInfo fi in dirInfo.GetFiles("*.*.resx", SearchOption.AllDirectories))
{
string cultureName = Path.GetFileNameWithoutExtension(fi.Name);
if (cultureName.LastIndexOf(".") == cultureName.Length - 1)
continue;
cultureName = cultureName.Substring(cultureName.LastIndexOf(".") + 1);
availableResources.Add(cultureName);
}
List<CultureInfo> result = new List<CultureInfo>();
foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
{
if (availableResources.Contains(culture.ToString()))
{
result.Add(culture);
}
}
AvailableCultures = result.ToArray();
CurrentCulture = DefaultCulture;
if (!result.Contains(DefaultCulture) && result.Count>0)
{
CurrentCulture = result[0];
}
}
public static CultureInfo CurrentCulture
{
get { return Thread.CurrentThread.CurrentCulture; }
set
{
Thread.CurrentThread.CurrentUICulture = value;
Thread.CurrentThread.CurrentCulture = value;
}
}
}
Now we can add as much as posible resource files and don't have to worry how many languages we had in our website. The class will help to detect that.
And because this class should be used in all webpages, we dont want to repeat the code which uses it in every page, we create a PageBase
class. Here Session will be used to save chosen language state:
using System.Globalization;
using System.Web.UI;
public class PageBase : Page
{
private const string SESSION_KEY_LANGUAGE = "CURRENT_LANGUAGE";
protected override void InitializeCulture()
{
base.InitializeCulture();
if (Session[SESSION_KEY_LANGUAGE] != null)
{
ApplyNewLanguage((CultureInfo) Session[SESSION_KEY_LANGUAGE]);
}
}
private void ApplyNewLanguage(CultureInfo culture)
{
LanguageManager.CurrentCulture = culture;
Session.Add(SESSION_KEY_LANGUAGE, LanguageManager.CurrentCulture);
}
protected void ApplyNewLanguageAndRefreshPage(CultureInfo culture)
{
ApplyNewLanguage(culture);
Response.Redirect(Request.Url.AbsoluteUri);
}
}
All pages which inherit from PageBase don't have to care about what language visitor chosing. InitializeCulture
method was called very earlier in the page life-cycle. Whenever a visitor select a new language, we just have to call the protected method ApplyNewLanguageAndRefreshPage
which was implemented in the base class.
if (ddlLanguages.Items.Count > 0)
{
ApplyNewLanguageAndRefreshPage(new CultureInfo(ddlLanguages.SelectedValue));
}
We are ready with our globalization framework. Now the only thing left is the adding of resource specific data in the resource files. For each culture we need to have a separate (and appropriately named) resource file. This process is localization. In my web.config file I have used the following properties:
<globalization responseEncoding"=utf-8" requestEncoding="utf-8"
fileEncoding="utf-8" />
Note the encoding attributes: utf-8
(8 bit Unicode Transformation Format) is used since
it is variable length character encoding and can represent languages such as Vietnamese (in this article sample code), Greek, Arabic etc., beside ASCII compatible. For more info, see this link:
http://en.wikipedia.org/wiki/UTF-8
Points of Interest
If you are using Resharper, you can download a plugin which support localization:
This is a pretty cool plugin which help you to detect and refactor all none-resource text and move them into resource files.
History