Internationalisation

This is referring to translating the same content (such as a login form) in to a different language.

To ensure an application can be translated easily, the components and processes should be defined using keys instead of content and a version of the content in the default language must be defined (i.e. hardcoded in java script). Translations are split into blocks, with each set of related components sharing a block of translatable content.

It is advisable not to make these blocks too small as translators would have difficulty knowing the context as to where the key should be used, meaning that the grammar and sentence construction may not be logical/compatible with another language.

The language resolution is set as follows:

  1. current locale
  2. if not found, fall back to the default locale
  3. if neither found, then return the key

A tool extracts the default content and uses this as a basis for translation e.g. translators are given the default language to work on. The remaining languages are held in separate JavaScript files and are bulk loaded on demand.

The I18nService is built on IntlMessage which uses standard CLDR. It supports multiple variables in translations as well as a combination of plurals and gender in one translatable block. Some keys require basic formatting queues and the markdown in translations is parsed to a component tree to ensure that un-sanitized HTML is never output (to prevent XSS vulnerabilities).

To create a component which supports translations, follow these 3 steps:

  1. Extend the BaseComponent class (which in turn extends the React.Component class, but will contain common functionality that needs to be present for every component).
  2. The component should require LocaleAware. This offers functions like composeStyle(Style) which takes cards of switching any styles specific to a component based on the set Locale. This is particularly useful when expecting text to be written right to left rather than left to right when the locale is switched to Arabic. It also consists of a translate(key, params) function which removes the need to specify a LocalScope. If there is a need to define a locale scope other than the root of the application, then I18nService#translate(key, params, scope) should be used instead.
  3. The Translated component can be used to replace a <span> or a similar HTML element containing text, with the translated version (instead of having to call the LocaleAware#translate for every piece of text in a component view).

We can see these steps in practice by looking at the example below. This component will essentially be able to display text as a property. This text can be translated to multiple languages while having the style adjusted depending on the language being used.

const React = require('react');
const Metadata = require('ixaris-uxf').Metadata;
const LocaleAware = require('ixaris-uxf').LocaleAware;
const BaseComponent = require('ixaris-uxf').BaseComponent;
const Radium = require('radium');

class HelloWorld extends BaseComponent {
    render() {
        return require('./HelloWorld.rt.html').call(this);
    }
}

HelloWorld = LocaleAware(HelloWorld);
HelloWorld = Radium(HelloWorld);
HelloWorld = Metadata(HelloWorld, {
    documentation : 'A simple string',
    type : Metadata.ComponentType.INLINE,
    displayName : 'HelloWorld',
    props : {
        text : {
            documentation : 'The text that will be displayed',
            type : React.PropTypes.string,
            defaultValue : 'Hello World'
        }
    },
    children : {
        noChildren : {
            type : Metadata.ChildValidation.Exactly(0),
            documentation : 'Element should not have any child elements'
        }
    }
});

module.exports = HelloWorld;
<rt-require dependency="ixaris-uxf" as="UXF"/>
<rt-require dependency="./HelloWorld.rt.css" as="Style"/>
<div rt-scope="this.composeStyle(Style) as S">
    <span style={S.text}>
        <UXF.Translated translate={this.props.text}></UXF.Translated>
    </span>
</div>
.text {
   padding-left: 10px;
   font-size: 12px;
}