Website Localization with Ektron – Part 3

August 27, 2010

Setting up correct Locales, adding Lang attributes and localizing hard-coded images are among the technical steps detailed in this final blog in our 3-part series on Website Localization with Ektron CMS. We covered an overview of CMS strategy issues in part 1, Website Localization with Ektron Web CMS Part 1, and gave more technical detail and steps in Website Localization with Ektron Web CMS Part 2. In this final blog of the series we will show you how to solve some of the issues touched on in Part 2.

All of the code shown below, and an example website with the controls in place as a demo, can be downloaded as a ZIP file available on the right column.

Setting up the correct Locale

The first thing we should do is to make sure that the system responds to the user in the correct (user selected) locale. To do this, we should not only rely on the content returned by Ektron (that will be in the correct locale) but also we should tell the system to use that same locale to present any information requested outside Ektron in the same locale.

Since we are dealing with a website, and based on Microsoft best practices, the locale initialization must be done with the InitializeCulture method, by overriding it and setting the CurrentCulture and CurrentUICulture for the current thread. The Culture value determines the results of culture-dependent functions, such as the date, number, and currency formatting, and so on. The UICulture value determines which resources are loaded for the web page.

Since we would have to use the above on every template on our website and that's not only too time consuming, but also error prone (if you add a new web page and forget to add the code, some items on that web page may not work as expected), I usually create a LocalizablePage class that inherits from the System.Web.UI.Page and I override the InitializeCulture method with the initialization code. Then the only thing that I have to remember is to change the inheritance of every template from System.Web.UI.Page to LocalizablePage.

namespace GpiEktronLibrary {
    using System.Globalization;
    using System.Web.UI;

    public class LocalizablePage : Page {
        protected override void InitializeCulture() {
            //this is here just to make sure the culture is correctly initialized
            CultureInfo c = Utilities.CurrentCulture;
            base.InitializeCulture();
        }
    }
}

As you can see on the code above, I did not directly place all the initialization code on the LocalizablePage class, but I created a helper class that does all this for me. Also that class returns what is the actual user-selected locale and what's the default locale selected in Ektron CMS. These two values will come in handy for some of the next tasks.

namespace GpiEktronLibrary {
    using System.Globalization;
    using System.Threading;
    using System.Web;
    using Ektron.Cms.Controls;

    public static class Utilities {
        private static int _lastCulture;
        private static CultureInfo _lastCultureInfo;
        private static readonly object _cultureLock = new object();

        static Utilities() {
            using (LanguageAPI languageApi = new LanguageAPI()) {
                DefaultCulture = new CultureInfo(languageApi.DefaultLanguageID);
            }
        }

        public static CultureInfo DefaultCulture {
            get;
            private set;
        }

        public static CultureInfo CurrentCulture {
            get {
                using(LanguageAPI languageApi = new LanguageAPI()) {
                    if (_lastCulture != languageApi.CurrentLanguageID) {
                        HttpContext.Current.Application.Lock();
                        lock (_cultureLock) {
                            _lastCulture = languageApi.CurrentLanguageID;
                            _lastCultureInfo = new CultureInfo(_lastCulture);
                            Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = _lastCultureInfo;
                        }
                        HttpContext.Current.Application.UnLock();
                    }
                }

                return _lastCultureInfo;
            }
        }
    }
}

Adding LANG attribute to the html tag

Now that we have our system and code properly set, we need to tell the browser/spiders in which language we are serving the page. For this you can:

  • Add a Literal control inside the html tag and from the code behind, write the appropriate code for the literal to display the current locale, or
  • Use the code below to simply replace the html tag by a .net control that will render the html tag and also take care of setting the correct attribute. As you can see, this piece of code uses the CurrentCulture property of our helper class to ensure that we always use the correct user selected locale.
using System.Web.UI;

[assembly: TagPrefix(@"GpiEktronLibrary.Controls", @"GpiEkt")]
namespace GpiEktronLibrary.Controls {
    using System.Text;
    using System.Web.UI;

    [ToolboxData(@"<{0}:EktronHtmlTag runat=server />")]
    public class EktronHtmlTag : Control {
        protected override void Render(HtmlTextWriter pOutput) {
            StringBuilder strb = new StringBuilder(@"", Utilities.CurrentCulture.Name);
            pOutput.Write(strb.ToString());
            RenderChildren(pOutput);
            pOutput.Write("");
        }
    }
}

Taking care of content directionality

Again, we created another control that you can simply drop in your templates (inside the body tag); making sure it wraps all the content of your website. This control will render a DIV tag with the DIR attribute correctly set (rtl for Right-to-Left or ltr for Left-to-Right) based on the page target language. The DIR attribute is being applied to a DIV and not to the BODY tag because some speakers of languages that use right-to-left scripts prefer the directionality of the user interface to be associated with the desktop environment and not with the content of a particular document.

using System.Web.UI;

[assembly: TagPrefix(@"GpiEktronLibrary.Controls", @"GpiEkt")]
namespace GpiEktronLibrary.Controls {
    using System;
    using System.ComponentModel;
    using System.Text;
    using System.Web.UI;

    [ToolboxData(@"<{0}:EktronContentDirectionality runat=server />")]
    public class EktronContentDirectionality : Control {
        [Category("GPI Component")]
        public string ClassName { get; set; }

        protected override void Render(HtmlTextWriter pOutput) {
            StringBuilder strb = new StringBuilder("<div ");

            string langName = Utilities.CurrentCulture.TwoLetterISOLanguageName;
            string dir = (langName.Equals(@"ar", StringComparison.OrdinalIgnoreCase) ||
                langName.Equals(@"ur", StringComparison.OrdinalIgnoreCase) ||
                langName.Equals(@"fa", StringComparison.OrdinalIgnoreCase) ||
                langName.Equals(@"he", StringComparison.OrdinalIgnoreCase)) ?
                    @"rtl" :
                    @"ltr";

            if (!string.IsNullOrEmpty(ClassName)) {
                strb.AppendFormat("class=\"{0}\" ", ClassName);
            }

            strb.AppendFormat(@"dir=""{0}"">", dir);

            pOutput.Write(strb.ToString());
            RenderChildren(pOutput);
            pOutput.Write("</div>");
        }
    }
}

Localizing hard-coded images

If an image is linked on a page from the content in Ektron, it is easy to go to that page, edit the localized version and replace the image. But when dealing with images hard-coded into the templates (Logos, graphics with overprinted text, etc.) you cannot resort to the features Ektron provides. In these cases, you have to pick the correct image for the user selected locale.

The code below shows you an example of a user-control that takes care of everything for you. The control has the same set of attributes as the IMG tag. The only difference is on how the control renders the IMG tag. If the website is being displayed in the Ektron CMS default language (set on the web.config), the control will look for the image on the path/filename combination you specified on the Source attribute. But when the user changes the language of the site, the control will try to load the image from the same path as the original version, but the control will append to the name an underscore (_) and then the full name for the selected locale (es-ES for Spanish/Spain, ja-JA for Japanese/Japan, zh-CN for Simplified Chinese/China, etc). So, for example, if the original image is located under this path "/images/subfolder/header.jpg", the Spanish version will be located/named as "/images/subfolder/header_es-ES.jpg" and the Japanese version will be "/images/subfolder/header_ja-JP.jpg"

using System.Web.UI;

[assembly: TagPrefix(@"GpiEktronLibrary.Controls", @"GpiEkt")]
namespace GpiEktronLibrary.Controls {
    using System.Drawing.Design;
    using System.Web.UI;
    using System.ComponentModel;
    using System.Text;
    using System.Web;
    using System.Web.UI.Design;

    [ToolboxData(@"<{0}:EktronLocalizableImage runat=server />")]
    public class EktronLocalizableImage : Control {
        [Category("GPI Component"), DefaultValue(""), Localizable(true)]
        public string Alt {
            get;
            set;
        }

        [Category("GPI Component"), DefaultValue(""), Localizable(true)]
        public string Title {
            get;
            set;
        }

        [Category("GPI Component"), DefaultValue("")]
        public string UseMap {
            get;
            set;
        }

        [Category("GPI Component"), DefaultValue("")]
        public string ClassName {
            get;
            set;
        }

        [Category("GPI Component"), DefaultValue("")]
        public string Style {
            get;
            set;
        }

        [Category("GPI Component"), Editor(typeof (UrlEditor), typeof (UITypeEditor))]
        public string Source {
            get;
            set;
        }

        [Category("GPI Component")]
        public int Width {
            get;
            set;
        }

        [Category("GPI Component")]
        public int Height {
            get;
            set;
        }

        protected override void Render(HtmlTextWriter pOutput) {
        StringBuilder strb = new StringBuilder(@"<img");
        if (Utilities.CurrentCulture.LCID == Utilities.DefaultCulture.LCID) {
            strb.AppendFormat(@" src=""{0}""", Source);

        } else {
            int dot = Source.LastIndexOf('.');
            strb.AppendFormat(@" src=""{0}_{2}.{1}""",
                    Source.Substring(0, dot),
                    Source.Substring(dot + 1),
                    Utilities.CurrentCulture.Name);
        }

        if (!string.IsNullOrEmpty(Alt)) {
            strb.AppendFormat(" alt=\"{0}\"", HttpUtility.HtmlAttributeEncode(Alt));
        }

        if (!string.IsNullOrEmpty(Title)) {
            strb.AppendFormat(" title=\"{0}\"", HttpUtility.HtmlAttributeEncode(Title));
        }

        if (!string.IsNullOrEmpty(UseMap)) {
            strb.AppendFormat(@" usemap=""{0}""", HttpUtility.HtmlAttributeEncode(UseMap));
        }

        if (!string.IsNullOrEmpty(ClassName)) {
            strb.AppendFormat(" class=\"{0}\"", HttpUtility.HtmlAttributeEncode(ClassName));
        }

        if (!string.IsNullOrEmpty(Style)) {
            strb.AppendFormat(" style=\"{0}\"", HttpUtility.HtmlAttributeEncode(Style));
        }

        if (Width > 0) {
            strb.AppendFormat(" width=\"{0}\"", Width);
        }

        if (Height > 0) {
            strb.AppendFormat(" height=\"{0}\"", Height);
        }

        strb.Append("/>");
        pOutput.Write(strb.ToString());
    }
    }
}

Localizing hard-coded text

You have two options here:

  1. Remove all the hard-coded text from the templates, placing them in resource files (.resx) and then use the Literal asp.net control to place it back on the templates. The resource file will have to be sent to the translation company as part of the localization process, along with the content exported from Ektron. If you are using the LocalizablePage class as the base class for all your templates, the system will automatically pick-up the correct resource file once the translated resources are back in place.
  2. Ektron Setup Remove all the hard-coded text from the templates, placing them in smart-forms within Ektron, and then use the standard ContentBlock control from Ektron to place the text back into the templates. You will have to also provide the control with an XSLT file that the control will use to render the content, and the content ID for the text you want to display. This method has the advantages of letting the Website administrator edit/import/export the text from within Ektron's Workarea.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" />

    <xsl:template match="/root">
        <xsl:value-of select="DictionaryEntry"/>
    </xsl:template>
</xsl:stylesheet>

And there you have it… If you correctly implement the techniques mentioned above, you will have a "bullet-proof" base of templates that will be localization-friendly and that may require minimal adjustments to be done over the QA phase.

I hope the "Website Localization with Ektron CMS" 3-part series of articles has provided you with useful information on how to correctly author multilingual websites under the Ektron CMS.

Further GPI Resources on Ektron, CMS and Website Development

Globalization Partners International is a certified Ektron Partner and has previously published several detailed presentations and case studies about using content management systems, like Ektron, to design, develop and deploy multilingual websites. You may find many of the following links useful:

Further Information on Localization Resources

Globalization Partners International (GPI) frequently assists customers with multilingual website development. A suite of globalization tools developed by GPI can empower you to achieve your multilingual project goals. GPI also offers  web localization and internationalization, and can evaluate your application for global readiness. Consider partnering with GPI prior to localizing or translating your application: this will help you avoid many mistakes and save considerable time and money on your localization projects.

Contact GPI for more information via e-mail at info@globalizationpartners.com, by phone at (866) 272-5874, or by requesting a free web development and web localization quote on your project.

Category:
Content Management Systems
Tags:
localization, CMS, Ektron, multilingual content, translation, website

Website Localization using Ektron Web CMS Part 2Why are Multilingual Keywords important for your international websites?

Comments

Currently, there are no comments. Be the first to post one!

Contact Us FREE Globalization eBooks Request Demo Request Quote


Federico has over 12 years' experience as a globalization engineer managing a wide range of software and website globalization projects (internationalization I18n + localization L10n). His expertise spans software and website internationalization and localization processes, standards and tools as well as locale specific SEO. Federico has completed hundreds of successful globalization engagements serving as lead I18n architect involving different programming languages. He is a certified developer in several content management systems and helps clients create world-ready applications, utilizing development practices that are faster, more economical, and more localization-friendly.

Downloads