My Blog: Going International

10 December 2016

This is how I make a website in multiple languages in Orckestra CMS.

Making a website that can be populated with text in multiple languages isn't as difficult as it sounds. The Orckestra CMS has localization functionality [sic] to simplify the process, and aid page translation. Take a moment to read through their documentation, as it covers most of the basics, including:

On a practical level, this I how I went about implementing localisation in the University Package website.

Choose a Language

When adding a language to your website, there are some important considerations to make. Firstly, do you need to localise beyond the language you are adding? If your website is going to be used in both the UK and The USA, you might like to consider adding two separate localisations for each version of English (localisation vs. localization, etc.). You might also have different currency and date formats to use for each country your website will be read in.

Once you know the territories that you will be targetting, you can find the language codes you'll need to add. At their most simple, language codes are two characters long and are set by the International Organization for Standardization (ISO) as ISO 639-1. Examples are:

  • Danish: da
  • English: en
  • French: fr

These can be sub-divided for territories where there are localisation issues to contend with, as defined by the Internet Engineering Task Force (IETF) Language Tags. For example:

  • American English: en-us
  • British English: en-gb

The Orckestra CMS supports both two-character ISO language codes and IETF language tags, so pick the ones most suitable to your website. For the University Package, I'm going to keep things simple by using just en (English) and fr (French).

A Word About Translation...

The Institute of Translation & Interpreting (ITI) recommend that you should only translate into your "mother tongue" or native language:

Subject to Principle 2 Clauses 5 and 8 below, members shall translate only into a language that is either (i) their mother tongue or language of habitual use, or (ii) one in which they have satisfied the Institute that they have equal competence. They shall translate only from those languages in which they can demonstrate they have the requisite skills.

Unfortunately I'm not a native French speaker, so I'm going to rely on machine translation initially. Google Translate is now considered usable for most literal translations (but I wouldn't recommend it for something permanent, like a tattoo).

Add the Language

The Orckestra CMS documentation shows how to add a language, but it neglects to mention one issue you might run into after adding one... some of your installed functions will break.

Before adding a language to the website via the CMS, you will need to check for pre-existing Resource files and create new ones for your target language. For example, if you have installed the Composite.News add-on package and open the folder at:

  • ~/App_GlobalResources/Composite/News

... you will find two files in there:

  • ~/App_GlobalResources/Composite/News/News.resx (English)
  • ~/App_GlobalResources/Composite/News/News.ru-ru.resx (Russian)

If you're adding a French version to the CMS, you'll need to duplicate the News.resx file and rename the copy as News.fr.resx before opening it in Visual Studio to translate its contents into French.

  • ~/App_GlobalResources/Composite/News/News.fr.resx (French)

This done for all applicable Resource files, You can now safely add the French language to the CMS and start translating web-pages.

Create the Resource Files

For the issues stated above, it's a really good idea to create resource files for your own / Orckestra / Composite functions before you add a new language. If you have a website that already has multiple languages and you're adding a new function, consider creating the Resource files at the beginning of the process.

When building a new function, start by creating a mock-up of the final HTML code in your favourite editor (mine is Microsoft Expression Web). Whenever you show things to the user, like titles or table column headings, these will need to be added to your resource files as text strings.

Take your time and concentrate not just on the outward appearance, but also your "behind the scenes". For example, <table> tags benefit from the addition of a summary attribute, like this: <table summary="A table of fee band costs">. The text "A table of fee band costs" will need to be added to your resource file as a text string, as it will be presented to your audience using accessibility tools, such as JAWS. Likewise, add text strings for error messages and alerts.

Example French Resource file for the functions relating to Fees.

It's a good idea to get into the habit of populating the Comment field with the original text from the origin language, just in case you need to employ a translator in the future. They can work on the individual files, rather than having access to your entire project codebase.

Once you've got the mock-up just the way you like it, have moved all of the required text strings to your Resource files, and translated them into your target languages, you can begin creating your new function.

Add Resource Strings to Functions

When adding the test strings to your XSLT function, use this format:

<lang:string key="Resource, Resources.[Resource File Name].[String Name]" />

For .NET Razor, use:

@Resources.[Resource File Name].[Name]

For C#, use:

HttpContext.GetGlobalResourceObject("[Resource File Name]", "[Name]")

What About JavaScript Functions?

When working with JavaScript files, you need to output messages and alerts in the target language. This is often overlooked by novice web-developers. There are generally three approaches to localising JavaScript, so I'll critique them:

  1. Separate JavaScript files for each language - It's a nice idea to serve just the files required for the target language, but there will be an overhead maintaining the code as you add new functions... you'll need to duplicate code across multiple files and translate messages as you go.
  2. Store the stings in a separate JavaScript file - Using an array or JSON file to store your strings might work for sites with only two or three languages, but when you get to ten or twenty languages, that file is going to get large. You also have your translations in two places for each language, the .js file and the .resx file. This also adds an additional HTTP Get request to your page load, which could slow things down.
  3. Store the strings in the Resource file and access them from the HTML document using DOM lookup - This approach reduces the number of HTTP Get requests, keeps the stings in just the .resx files, and is my preferred option.

For the University Package, I've created a TextStrings function, which outputs XHTML <div> tags containing data attributes:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:in="http://www.composite.net/ns/transformation/input/1.0" xmlns:lang="http://www.composite.net/ns/localization/1.0" xmlns:f="http://www.composite.net/ns/function/1.0" xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="xsl in lang f">
	<xsl:template match="/">
		<html>
			<head />
			<body>
				<div class="hidden" id="university-name">
					<xsl:attribute name="data-store"><lang:string key="Resource, Resources.Pages.WebsiteName" /></xsl:attribute>
				</div>
				<div class="hidden" id="table-responsive">
					<xsl:attribute name="data-store"><lang:string key="Resource, Resources.Pages.TableResponsive" /></xsl:attribute>
				</div>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>

The CMS rendering engine will merge this into the containing page.

<html xmlns="http://www.w3.org/1999/xhtml">
    <head/>
    <body>
        <div class="hidden" id="university-name" data-store="Bailey University"/>
        <div class="hidden" id="table-responsive" data-store="Scrollable table « »"/>
    </body>
</html>

You'll note that the DIVs are hidden by a Bootstrap framework class="hidden" value, and so are only visible to the JavaScript JQuery functions:

$(document).ready(function () {
    // Status bar message:
    window.status = $('div#university-name').data('store');
});

comments powered by Disqus