Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dynamic Website Multi-languages Management

0.00/5 (No votes)
18 Aug 2007 3  
A smart way to manage languages on a multilingual website, using resources and globalization,localization
  • 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.

    Screenshot - GUI_Vietnamese.gif

    Screenshot - GUI_French.gif

    Screenshot - GUI_Hindi.gif

    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.

    Screenshot - resxStruct.jpg

    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:

    Screenshot - localization.jpg

    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;
    /// <summary>
    
    /// Class to manage language on the website
    
    /// </summary>
    
    public sealed class LanguageManager
    {
        /// <summary>
    
        /// Default CultureInfo
    
        /// </summary>
    
        public static readonly CultureInfo DefaultCulture = new CultureInfo("en-US");
        /// <summary>
    
        /// Available CultureInfo that according resources can be found
    
        /// </summary>
    
        public static readonly CultureInfo[] AvailableCultures;
        static LanguageManager()
        {
            //
    
            // Available Cultures
    
            //
    
            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))
            {
                //Take the cultureName from resx filename, will be smt like en-US
    
                string cultureName = Path.GetFileNameWithoutExtension(fi.Name); //get rid of .resx
    
                if (cultureName.LastIndexOf(".") == cultureName.Length - 1)
                    continue; //doesnt accept format FileName..resx
    
                cultureName = cultureName.Substring(cultureName.LastIndexOf(".") + 1);
                availableResources.Add(cultureName);
            }
            List<CultureInfo> result = new List<CultureInfo>();
            foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
            {
                //If language file can be found
    
                if (availableResources.Contains(culture.ToString()))
                {
                    result.Add(culture);
                }
            }
            AvailableCultures = result.ToArray();
            //
    
            // Current Culture
    
            //
    
            CurrentCulture = DefaultCulture;
            // If default culture is not available, take another available one to use
    
            if (!result.Contains(DefaultCulture) && result.Count>0)
            {
                CurrentCulture = result[0];
            }
        }
        /// <summary>
    
        /// Current selected culture
    
        /// </summary>
    
        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 you would like to have DefaultLanguage changes to effect all users,
    
            // or when the session expires, the DefaultLanguage will be chosen, do this:
    
            // (better put in somewhere more GLOBAL so it will be called once)
    
            //LanguageManager.DefaultCulture = ...
    
            //Change language setting to user-chosen one
    
            if (Session[SESSION_KEY_LANGUAGE] != null)
            {
                ApplyNewLanguage((CultureInfo) Session[SESSION_KEY_LANGUAGE]);
            }
        }
        private void ApplyNewLanguage(CultureInfo culture)
        {
            LanguageManager.CurrentCulture = culture;
            //Keep current language in session
    
            Session.Add(SESSION_KEY_LANGUAGE, LanguageManager.CurrentCulture);
        }
        protected void ApplyNewLanguageAndRefreshPage(CultureInfo culture)
        {
            ApplyNewLanguage(culture);
            //Refresh the current page to make all control-texts take effect
    
            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) //make sure there is a SelectedValue
    
            {
                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:

    Screenshot - resharper_plugins.png

    This is a pretty cool plugin which help you to detect and refactor all none-resource text and move them into resource files.

    Screenshot - resharper_1movetoresource.png

    Screenshot - resharper_2alreadyexistsinresource.png

    Screenshot - resharper_4newline.png

    Screenshot - resharper_menu.png

    History

  • 2007/08/16 - First try, using LanguageManager as Instance - 11.9 KB
  • 2007/08/18 - Use LanguageManager As StaticClass - 99.4 KB
  • Trung tam di dong qui tu cac cua hang lon nhat Vietnam

    Other good tutorials

  • Install a Windows Service in a smart way instead of using Windows Installer MSI package
  • Create animation in dotNet with alpha-blending PNG images by calling API
  • 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