The Lang framework

The Lang framework is one of possible ways to achieve i18n aware UIs - UIs that can be rendered in multiple human languages. It provides following features:

  1. Translation of static texts and labels;
  2. Formatting of dynamic values including pluralizations of various kinds and custom rules.
  3. One time and on-the-fly translation of the UI to supported languages.
  4. Translation for particualr language is defined in single file instead of spreading it among various code, style and markup files. So translation of given UI to some new language is a matter of creating single file with predefined set of items.

Basic principles

UI of the application is presented as one or set of HTML files. These files are the same for all supported languages.

Each supported language gets so called translation map file (TMF).

Each TMF file is a tiscript file that defines mapping of translatable item ID to its textual representation in given human language.

Translation map file structure

TMF is an ordinary script file that gets eval() 'ed upon loading.

Result of TMF file shall be an object - map of "translatable ID" to either another string (st) or a function that provides translated/composed string (e.g. pluralized text of some value).

Example of simple translation map file for English language:

({
  "Matchboxes":"Match boxes",        // static text entry
  "There are # matches in the box" : // simple pluralization entry
      function(n) {
        return  n == 0 ? "The matchbox is empty"
              : n == 1 ? "There is just one match in the box"
              : String.printf("There are %d matches in the box",n);
  },
  ...
})

As you see the value of such map file is a simple map containing items to be translated for the given language.

Translation aware markup structure

To attach the Lang framework you just need to include lang.css from sciter-sdk/+lang/ folder in your styles. For example as this:

<style>
   @import url(../lang.css);
</style>

Inclusion of  language specific translation maps:

the Lang assumes that translation map files are included as special <link> elements in the head of html document:

<html lang="en">
  <head>
    <!-- these two links define two translation files for en and ru languages. -->
    <link href="lang/ru.tis" hreflang="ru" rel="translation" >
    <link href="lang/en.tis" hreflang="en" rel="translation" >
    ...

At load time the Lang will pickup one of TMFs that matches <html lang="..."> attribute. So the UI will be presented in defult language defined by lang attribute on <html> element.

Marking translation "points" in html

Static texts

Portions of (static) text that needs to be translated in your UI can be marked using one of these ways:

  1. Using <dfn> elements, example:
    <dfn>Text to be translated</dfn>
    If your translation map contains entry like:
     "Text to be translated" : "Translated text"
    then user will see Translated text rather than original content of the <dfn> element;
  2. Using <label> elements. <label> elements are treated exactly as <dfn> elements by the Lang;
  3. Assigning dfn class to any other elements containing text only, for example <li class="dfn">Text to be translated</li>;
  4. Defining Lang.Label aspect to any other element by using arbitrary CSS selector as:
    ul#options > li  { aspect:"Lang.Label"; }

Dynamic texts - subjects of pluralization and language specific formatting

There are cases when text needs to be formatted or composed for correct representation. For example consider pluralization task: "1 cactus" but "2 cactii", "1 match" but "2 matches", etc. Pluralization rules are very language specific and in general cannot be generailzed well. So instead of trying to cover all cases for any language in current use by generic routines the Lang propose to deal with pluralization on case by case basis.

Portions of text that are subject of language specific formatting can be marked in your markup in one of these ways:

  1. Using <var> elements, example:
    <var>There are # matches in the box</var>.
    The <var> element is getting value property redefined by the Lang. So in code if you assign some numeric value to such <var> element the Lang will call corresponding formatting function defined in the map. For an example see translation map file sample above.
  2. Assigning var class to any other elements containing text only, for example <li class="var">There are # matches in the box</li>;
  3. Defining Lang.Formatter aspect to any other element by using arbitrary CSS selector as:
    ul#options > li  { aspect:"Lang.Formatter"; }

Explicit translation: Lang.x() function.

Lang namespace defines Lang.x(text[,value]) function that can be used in code to explicitly translate the text.  That function uses the same translation table and rules as <dfn>s and <var>s. If text maps to function defined in translation file and value is provided then that function gets called for the translation.

Automatic translation map generation and maintainance

I'd suggest to use following sequence of actions:

0. Create initial en.tis translation file with this content:

({ })

and save it as, say, /lang/en.tis file. For now it contains empty translation table.

1. Design your UI in your default language.

Let's assume that it will be English for now. Here is how your markup with Lang attached may look like:

<html lang="en">
  <head>
    <link href="lang/en.tis" hreflang="en" rel="translation" >

    <style>
      @import url(../lang.css); /* enabling the Lang */
    </style>

    <script type="text/tiscript">
      include "../lang.tis";       // the Lang itself.
      include "../lang-tool.tis";  // the Lang tool - translation map generator.
    </script>

  </head>
  <body>

    <label>MATCHES</label>
    <var id="nmatches">MATCHES-PLURALIZED</var></p>

  </body>
</html>

Note include "../lang-tool.tis";  statement above . It includes special translate map generator tool.

2. Load markup in your application and run map tool

After loading the UI press CTRL+SHIFT+F11 inside your application. It will invoke translation map generator that will place its report into the clipboard.

Paste clipboard content into some file. With empty translation map and on markup above you should get content like this:

// items not found:
{
 "MATCHES-PLURALIZED":"function (v){ return \"?\" }",
 "MATCHES":"?"
}
// OK, no unused items

First section will give you full list of items that need to be translated and appear in your final translation map:

({
 "MATCHES-PLURALIZED": function (v){ return ... },
 "MATCHES": "Matches"
})

You can repeat this procedure time to time in app development life cycle - some items will be added, some removed (you will see them in second section). Review these items before moving all this to QA/testing.

Then ready to production just comment out that map generation tool invocation:  // include "../lang-tool.tis"; so it will not take space and keyboard shortcut handler.

Done.