Adventure Documentation

5. I18n

  1. What is Internationalisation (i18n)
  2. Developing i18n
  3. Working with i18n
  4. Interpolation and Pluralisation

What is Internationalisation (i18n)

i18n is the process of preparing software so that it can support local languages and cultural settings. An internationalized product supports the requirements of local markets around the world, functioning more appropriately based on local norms and better at meeting in-country user expectations.

Internationalization is also called i18n (because of the number of letters, 18, between ā€œiā€ and ā€œnā€). Internationalization ensures your software is localizable and is typically done by software developers and engineers.

Developing i18n

All labels are stored in JSON files where each language supported by the template has its own file. The files are located in /i18n of the template project, and each JSON file is named by the two-letter code defined in the ISO 639 of the language.

When working on internationalisation it is a good idea to use one language as a reference, in praxis this means you make labels for one language and then translate to the other supported languages. This way it is easier to manage the files, and they should rarely come out of sync.

The structure of the file is a flat JSON structure with simple key value pairs.

{
    "labels.quantity": "Quantity",
    "actions.save": "Save",
    "actions.buy": "Buy",
    "actions.add-to-cart": "Add to cart"
}

Working with i18n

The content of the i18n files is exposed to the template through the platform i18n module. The i18n module is already aware of the current session language and will automatically have loaded the language that is currently active.

Labels can be accessed by using the i18n get method provided by the module, simply call the i18n get method with the key you want to display and it will return the value.

If the key supplied to the get is not available, it will just print the key.
This is done so it is very easy to see which labels have not been translated to the current language.
{$i18n = $Platform->import("i18n")}

<p>
    <span>The content of "action.save" is:</span>
    <span>{$i18n->get("actions.save")}</span>
</p>

Interpolation and Pluralisation

When working with i18n, it is a very common use case is to substitute parts of the text with a variable from a dataset. The reason we do variable interpolation and not concatenation is because languages all have different sentence structuring, which would make it impossible to support if the code decided how the sentence is structured.

We use the i18next format which supports:

  • Interpolation through the handlebars syntax {{}}.
  • Context through the context syntax _context and the context variable.
  • plural by using a plural context and a count variable.
{
    "labels.results": "{{count}} results"
}
<p>
    {* 2 Results *}
    <span>{$i18n->get("labels.results", [ "count" => 2 ])}</span>
</p>

The above example will output 2 results because the handlebars count will be replaced with the variable provided. This makes it very easy to move the placement of the count around in the label without having to do any code changes.

The avid reader will have noticed that this won't be correct if the count variable is 1 where the expected output instead would be 1 result. Our i18n module also supports pluralisation and this can be achieved by defining a pluralcontext to this specific label.

{
    "labels.results": "{{count}} result"
    "labels.results_plural": "{{count}} results"
}
<p>
    {* 0 Results *}
    <span>{$i18n->get("labels.results", [ "count" => 0 ])}</span>

    {* 1 Result *}
    <span>{$i18n->get("labels.results", [ "count" => 1 ])}</span>

    {* 2 Results *}
    <span>{$i18n->get("labels.results", [ "count" => 2 ])}</span>
</p>

The magic here is that if a count variable is supplied to the get function, it will automatically choose the right pluralcontext based on language rules and the count variable. This is denoted by having a label with the same name and then appending _plural at the end. Plural context is special in that regard, because it automatically binds to the count variable, but it is also possible to use custom defined contexts by defining the context variable.

{
    "labels.person": "Person"
    "labels.person_boy": "Boy"
    "labels.person_girl": "Girl"
}
<p>
    {* Person *}
    <span>{$i18n->get("labels.person")}</span>

    {* Boy *}
    <span>{$i18n->get("labels.person", [ "context" => "boy" ])}</span>

    {* Girl *}
    <span>{$i18n->get("labels.person", [ "context" => "girl" ])}</span> 
</p>

Search results