# Internationalisation

This package leverages [parts of the Infusion framework that support localisation](http://docs.fluidproject.org/infusion/development/LocalizationInThePreferencesFramework.html),
and uses them to provide a Handlebars helper that can replace a message key with localised/internationalised text.  This
page covers the basic usage, and the steps required to use this helper from Node or a browser.

## The `{{messageHelper}}` Helper

The `gpii.handlebars.helper.messageHelper` grade is designed to be wired into the [renderer](renderer.md) provided by
this package.  See below for examples of how this works in each environment.  Once this helper is available, you can
refer to it in your templates, and use it to display localised/internationalised messages.  So, let's say that you have
a message bundle like the following:

```json
{
    "my-message-key": "Hello, world",
    "my-variable-key": "Displaying %items.length %type(s)."
}
```

The following handlebars template content would display the above key.

```handlebars
{{messageHelper "my-message-key"}}
```

### Variable Substitution

Variable substitution is also supported.  Let's say you passed the following context to the renderer:

```json
{
    "type": "cat",
    "items": ["house cat", "wild cat"],
    "subRecord": {
        "type": "dog",
        "items": ["junkyard dog", "wild dog"]
    },
    "message-keys": {
        "static": "my-message-key",
        "variable": "my-variable-key"
    }
}
```

The following handlebars template content would make use of the full context:

```handlebars
{{messageHelper "my-variable-key"}}
```

The result would be `Displaying 2 cat(s)`.  Note that, like [`fluid.stringTemplate`](https://docs.fluidproject.org/infusion/development/CoreAPI.html#fluidstringtemplatetemplate-terms),
you can reference deep material using an EL Path.  So, `%items.length` resolves to the `length` property of the
`items` array.

You can also optionally specify which part of the context to use for variable substitutions, as in this handlebars
template content:

```handlebars
{{messageHelper "my-variable-key" subRecord}}
```

The result would be `Displaying 2 dog(s)`.  In both these examples, the message key has been a literal string (hence the
quotes).  You can also use any variable in the context.  So, the following handlebars template content results in the
same output as the previous example:

```handlebars
{{messageHelper keys.variable subRecord}}
```

## `gpii.handlebars.helper.messageHelper` Component Options

The component that provides the above helper provides the following configuration options:

| Option            | Type       | Description |
| ----------------- | ---------- | ----------- |
| `defaultLocale`   | `{String}` | The default locale to use if no translation is available for a user's preferred locale/language. |
| `messages`        | `{Object}` | A map of message keys and string templates. Generally assembled from one or more files (see below). |

## Node Usage

By default, the `{{messageHelper}}` helper is wired into [`gpii.handlebars.standaloneRenderer`](renderer.md),
which means it is available both through the Express view engine and in rendering content outside of Express (such as
generating emails based on templates).

### Message Bundles

As was done in [earlier work on the preferences framework](http://docs.fluidproject.org/infusion/development/LocalizationInThePreferencesFramework.html),
the localisation and internationalisation support used in this package consists of "message bundles", JSON files
that consist of keys and string templates, as in:

```json
{
    "my-motto": "Through hardships to the stars"
}
```

Let's assume this is our English-language wording, and that it is saved to a file called `myPackageMessages-en.json`.
If I  want to provide a Dutch translation of the same content, I could save a file `myPackageMessages-nl.json` in the
same directory that contains:

```json
{
    "my-motto": "Naar de sterren door moelijkheiden"
}
```

(Thanks to [Wikipedia](https://nl.wikipedia.org/wiki/Per_aspera_ad_astra) for that translation.)  The `nl` and `en`
suffixes in the previous examples indicate the language used in each file.  We might also choose to use a locale, i.e.
a language and country to distinguish `nl_NL` (Dutch as used in The Netherlands) from `nl_BE` (Flemish Dutch as used in
Belgium).  It is recommended to provide not only variations, but a default for the language, for example, by using the
filename `myPackageMessages-nl.json` for Netherlands Dutch and `myPackageMessages-nl_BE.json` for Flemish.  In this
manner, we can work with either a full locale (`nl_BE`) or simply a language prefix (`nl`).

### `gpii.handlebars.i18n.messageBundleLoader`

As there are multiple components that need access to the same "bundle of message bundles", a common component is
provided that loads all bundles from the filesystem.  This component supports the following configuration options:

| Option          | Type       | Description |
| --------------- | ---------- | ----------- |
| `defaultLocale` | `{String}` | The locale to use if the user does not provide one in their request headers.  Defaults to `en_us`. |
| `messageDirs`   | `{Array}`  | An array referencing one or more directories that contain message bundle files.  Paths may either be absolute or package-relative paths. |

The `messageDirs` option supports an array of values.  Each directory will have its contents scanned and all bundles
will be categorised by language and/or locale.  Message bundles with the same name in multiple directories will be
merged.  If both bundles have the same message key, the right-most file wins, as is the case with
[options merging](https://docs.fluidproject.org/infusion/development/OptionsMerging.html#fluidmerge-signature).

Let's say that we are extending someone else's work, we may want to reuse and selectively override their message bundle
content.  Their package is registered as `package1` using [`fluid.module.register`](https://docs.fluidproject.org/infusion/development/NodeAPI.html#fluidmoduleregistername-basedir-modulerequire),
and has its message bundles located in `%package1/src/messageBundles`.  Let's say that that directory contains a file
named `myModuleMessages-en.json`, with the following content:

```json
{
    "my-component-title": "My Generic Tool",
    "my-component-description": "This tool does neat things."
}
```

If we want to extend their work and localise their wording, you might save the following content to the file
`src/messageBundles/myModuleMessages.json` and register your own package as `myPackage`:

```json
{
    "my-component-title": "My Rebranded Tool"
}
```

If our `messageDirs` option is set to `["%package1/src/messageBundles","%myPackage/src/messageBundles"]`, the merged
message bundle would look something like:

```json
{
    "en": {
        "my-component-title": "My Rebranded Tool",
        "my-component-description": "This tool does neat things."
    }
}
```

This is how the component keeps track of all available message bundles by language and/or locale.  Files that lack a
language or locale suffix will be saved under a key that matches the value of `options.defaultLocale` (see above).

This is an intermediate format that allows the bundling middleware to respond to requests in any supported language.
An individual request can include `Accepts-Language` headers indicating what languages are desired.  Let's say we have
the following combined set of message bundles:

```json
{
    "en_US": {
        "football-title": "soccer"
    },
    "en_GB": {
        "elevator-title": "lift"
    },
    "en": {
        "football-title": "football",
        "elevator-title": "elevator"
    }
}
```

Message resolution fails over from most to least specific, i.e. first messages are resolved against the locale, and then
against the language, and then against the language that corresponds to `options.defaultLocale`.  So, a request for
a locale of `en_GB` would receive a compiled message bundle like:

```json
{
    "elevator-title": "lift",
    "football-title": "soccer"
}
```

In this example, `elevator-title` is pulled from the locale, `football-title` is inherited from the language. By
comparison, a request for `en_US` would receive a compiled message bundle like:

```json
{
    "elevator-title": "elevator",
    "football-title": "soccer"
}
```

In the second case, `football-title` is determined by the locale, and `elevator-title` is inherited from the language.
A request for a locale for which we have neither locale or language data receives the same as if we had requested
the default language.  So, if we receive a request for `fr_FR`, the compiled message bundled would be the same as in
the previous example.  If we have some but not all messages localised, the message bundle fails over to the default
language.  Let's say our combined set of message bundles looks like:

```json
{
    "fr_FR": {
        "daily-greeting": "bon jour"
    },
    "fr": {
        "daily-greeting": "bon soir"
    },
    "en_US": {
        "daily-greeting": "good day",
        "thanks-response": "thanks"
    },
    "en": {
       "daily-greeting": "good night",
       "thanks-response": "cheers"
    }
}
```

If `options.defaultLocale` is set to `en_US` and we request `fr_FR`, the compiled message bundle looks like:

```json
{
    "daily-greeting": "bon jour",
    "thanks-response": "thanks"
}
```

Since both are locales, they both fail over from locale to language.  This can only happen if we have locale data. If
`options.defaultLocale` is set to `en` and we request `fr`, the compiled message bundle looks like:

```json
{
    "daily-greeting": "bon soir",
    "thanks-response": "cheers"
}
```

### `gpii.handlebars.i18n.inlineMessageBundlingMiddleware`

To simplify the browser side usage, a piece of `gpii.express` middleware is provided that starts with the standard
[language headers provided by the browser request](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language),
and follows the failover strategy outlined above.  You are expected to populate this component's `messageBundles` member
with individual message bundles, keyed by language or locale.  In most cases you will want to use the
`gpii.handlebars.i18n.messageBundleLoader` grade described above to populate this.

## Browser Usage

The base [`gpii.handlebars.renderer.standalone`](renderer.md) grade is adequate if you wish to include all message
bundles and handlebars templates as part of your grade definition.  See those docs for details.

For more complex setups, the [`gpii.handlebars.renderer.serverMessageAware`](renderer.md) grade is more suitable.  This
communicates with two pieces of middleware to retrieve its templates and message bundles.
