i18n Internationalization
Introduction
This section explains how to get up and running with Aurelia-I18N to provide localization (l10n), and internationalization (i18n) features for your app.
Under the hood, it uses i18next, a generalized open-source library with an extensive set of features. By building on top of it, not only can you reuse your work across various other platforms and frameworks, but you are able to use an extensive ecosystem full of various packages and plugins.
If you are already familiar with i18next
you would know that a key and options used to manipulate the translation are used to produce the final translated output. @aurelia/i18n
also uses this concept of a key and an options object. The evaluated keys and the options are passed on to i18next
as-is. This means that any resource syntax (interpolation, context, nesting etc.), and options object schema, supported by i18next
, are also supported by @aurelia/i18n
by default.
Getting Started
Note If you have already used the
aurelia-i18n
plugin previously and are migrating your existing Aurelia app to Aurelia vNext then jump straight to the migration guide.
Install the plugin.
Register the plugin in your app.
The above example shows how to initialize the i18n plugin and, thereby, i18next with translation resources. There are alternatives ways of doing this, which are discussed later. Once registered, the plugin can be used in your view using the translation attribute t
(this is the default translation attribute name, but an alias can be configured), and the translation keys that have been registered.
In this example, Aurelia will replace the textContent
of the span
with the string "Hello I18N" at runtime, if the currently active locale is en
. If you change the locale to de
, for example, the text would have been changed to "Hallo I18N".
Registering the plugin
As explained earlier, the plugin uses i18next
under the hood. During registration, the options to initialize i18next
can be passed in. There are two ways the plugin can be registered.
Using default options.
This initializes the plugin, as well as i18next
with default options.
However, often the default settings will not suffice.
Using custom options.
The customize
function with a callback can be used to customize the initialization of i18next
. The options.initOptions
can be mutated to modify the initialization of i18next
. Every option that can be used in i18next.init
can be used here, as well.
Additionally, all i18next
plugins can be installed using the options.initOptions.plugins
array, as shown below.
Configuring translation attribute aliases
As mentioned above, Aurelia views access translation resources using the t
attribute by default (see the details here).
Where this creates a conflict (say, for example, if there is an existing custom attribute named t
in your app), or if you migrate from another framework which uses an alternate default translation attribute name, an alias of this attribute can be registered as follows.
The registered aliases can then be used in your view in place of t
.
You can mix and match any alias at the same time.
Managing translation resources
Typically, the translation resources used in i18next
are plain JSON. To give you an idea of what they look like, let's take a look at the following sample.
For this example, the key expression key
will be evaluated to 'value of key'
, whereas complex.nested.resource.key
will be evaluated to 'value of nested resource key'
. Nesting resources in this way can help to group translations for individual sections or components.
In the previous examples, the resources are defined in line. As the application starts to grow, it is likely that the translation resource will too. With that, the inline resources definition becomes tiresome.
A typical practice is to externalize the JSON resources to separate files. Two approaches to how those files can be used in the application are elaborated on in the following sections.
Bundling the resource files
The straightforward approach is to import the JSON resources to the app (depending on your build system) and use those in the customize
function. The example below shows how to do that.
This is a handy strategy for a small app with a trivial amount of translated text. However, if there are multiple languages supported in your app and the amount of translations for each locale is non-trivial, then this might not be optimal. All the translation resources get bundled with the app, even if the user doesn't use all locales. Thus, this approach will unnecessarily increase the bundle size, which may affect your app's startup time.
i18next Backend plugin
Another approach is to asynchronously load the resource file when needed. To this end, i18next
Backend plugins can be used. In general, these plugins listen to language changes in i18next
, use the registered Backend to fetch the resource files so that keys can be translated for the new locale.
Let's demonstrate this with an example. We'll use the i18next-fetch-backend
plugin that makes use of fetch
to load resource files.
With this strategy, no translation resources get bundled with the app. Instead i18next
instructs the registered Backend to load the translation resource using the loadPath
pattern '/locales/{{lng}}/{{ns}}.json'
during initialization and whenever the active locale is changed. Note that {{lng}}
, and {{ns}}
are placeholders for locale names and namespaces, respectively. The default namespace used by i18next
is translation
(which can, of course, be changed using init options). For example, for locale en
, it is expected that the a translation.json
is available under /locales/en
. Thus, you have to ensure that those translation resources are accessible under the correct path.
Recipes
This section shows some recipes to make resources available for Backend plugins.
Webpack dev server
Use copy-webpack-plugin
to copy the locales
src directory to the distribution directory. Then set the devServer.contentBase
to the distribution directory.
If you are using an earlier version than v6.0.0 of copy-webpack-plugin
, use this config.
Using the plugin
If you are familiar withaurelia-i18n
Then you know that besides the translation service, this plugin also provides a formatting service for numbers, dates, and relative time. All these features are also available in @aurelia/i18n
which are elaborated on in the following section.
Active locale
The active locale can be get
or set
by injecting an instance of the I18N
, and using getLocale()
, and setLocale()
methods. The following example shows how to manipulate the active locale.
When the active locale is changed, the I18N
class publishes the i18n:locale:changed
event and dispatches the aurelia-translation-signal
signal. The i18n value converters and binding behaviors automatically subscribe to these events and update translations. This event and signal are useful tools if you want to perform your custom locale-sensitive logic when the locale is changed.
Note, Unlike the previous version of Aurelia, in Aurelia 2, all translatable resources (marked by the out-of-the-box attributes, value converters, and binding behaviors) are updated automatically on a change of locale, without the need for any additional component or service.
Translation
The translation service provided by this plugin can be used both in view and view-model.
Translation in view
Aurelia uses an attribute pattern to replace the content or attribute values. (The default pattern is t
, which can be customized by registering aliases). For this discussion, the default attribute name is assumed.
Syntax
At a minimum, a translation-key
needs to be used as the value for t
attribute. A bracket-enclosed and comma-separated list of attributes can precede the key. When specified, the value of those attributes is replaced with the value of the translation key. Moreover, a subsequent attribute list and key pairs can also be used in the same t
attribute. The following examples explain this in more detail.
Replace textContent
This is the most common use case and the default behavior.
Given the above translation and the view, Aurelia replaces the textContent
of the span
with "Hello World". The same result can also be achieved by explicitly using the [text]
attribute like <span t="[text]key"></span>
.
Note that the key expression can also be constructed in view-model and be bound to t
using t.bind
syntax.
Replace [src] of <img>
The aforementioned t="key"
syntax behaves a bit differently for img
elements. In this case, the src
attribute of the img
is replaced instead.
The i18n
plugin transforms the img
element to <img src="/path/to/image.jpg">
.
Replace innerHTML
As mentioned above, by default, the plugin will set the textContent
property of an element.
Therefore, in the above example, the HTML tags will be escaped, and the output will be <b>bold</b>
. The [html]
attribute must be added before the translation key to allow HTML markup.
This will set the innerHTML
of the element instead of the textContent
property, so HTML-markup won't be escaped.
[append] or [prepend] translations
So far, we have seen that the contents are replaced. There are two special attributes, [append]
, and [prepend]
which can be used to append or prepend content to the element's existing content. These also support HTML content.
The example above produces <span>tic tac toe</span>
.
Attribute translation
The plugin can be used to translate HTML-element attributes.
The example sets the [title]
attribute of the span
. A useful example would be to use the attribute translation to set the [alt]
or [title]
attributes of an image. Note that the same key can also be used to target multiple attributes, for example: <img t="[title,alt]key">
.
The same syntax of attribute translation also translates @bindable
properties of custom elements.
Use the custom element as follows.
Which produces the following result.
Manipulate translations with the t-params attribute
So far, we have seen a simple key to value mapping. However, i18next
supports more complex use cases such as interpolation, context-specific translation, and more. With i18next
this is done by initializing the library with the options object. Using @aurelia/i18n
, we use the t-params
attribute pattern along with t
to the same result. The object bound to t-params
is passed on to i18next
as-is. This means that the options-object schema supported by i18next
for any particular operation will work as expected.
Let's see how this works in Aurelia with some basic examples. For further details, check out the i18next
docs.
Interpolation
The above results in <span>i18next is great</span>
.
Contextual translation
The above results in <span>Your order has been dispatched</span>
.
Pluralization
The above results in the following.
Interval specific translation
Sometimes, simple plural contexts are not enough, and another translation is required based on different intervals. Note that this use case is not supported out of the box by i18next
. For this, we need to use i18next-intervalplural-postprocessor plugin and register it with the @aurelia/i18n
as shown here. Then define the interval translation resource as follows. (Note that the example uses nesting.)
This results in the following.
Default value
If the key expression is evaluated to null
, or undefined
, a default value can be provided as follows.
The example above produces <span>foo-bar</span>
, given that exprEvaluatedToNullOrUnd
evaluates to null
, or undefined
. In the absence of defaultValue
, the result would be <span></span>
. You'll notice that the old content of the target element has been cleaned up (no additional empty text node is created).
ValueConverter and BindingBehavior
To do translations in a more declarative way from within your HTML markup, you can use the t
ValueConverter and BindingBehavior.
Combined with appropriate translation resources, the correct value will be rendered. Note that the options object that follows t
is the same one we discussed earlier. Naturally, this value is optional. As you would expect, both the ValueConverter and BindingBehavior will update translations when the active locale changes.
Translation via code
Translations via code are done by using the method I18N#tr
. You can pass in the key
as the first parameter, followed by the optional second parameter options
.
Handling missing keys
If a key is missing in the translation resources, by default, the key's name will be rendered instead of the value. This is a sensible default as it immediately shows that a translation is missing for certain keys when inspecting the app.
However, this can be overridden by the configuration option, as shown below.
Setting skipTranslationOnMissingKey
to true
instructs the plugin to return empty strings for missing keys.
Formatting numbers
The @aurelia/i18n
plugin provides number formatting using Intl
API.
Format number in view using ValueConverter and/or BindingBehavior
The nf
ValueConverter and BindingBehavior can format numbers in a declarative way from the view. Both take two optional arguments, apart from the number being formatted, which are options, and locale, respectively. The number is formatted using the default number formatting options and the currently active locale if these are omitted. A specific locale can be passed on to format the number as per that locale. If the input is not a number, then the original value is returned from these as-is.
The formatting options are used to affect how the number is formatted. A prominent use case for that is to format the number as currency. For a full list of options, look here.
Both ValueConverter and BindingBehavior update the formatted value when the active locale is changed.
Format number via code
Formatting numbers via code works by using the method I18N#nf
. You can pass in the number as its first parameter, followed by the optional parameters options
, and locales
.
Additionally, if needed, an instance of Intl.NumberFormat
can be created using the I18N#createNumberFormat
method.
This can be useful if you want to cache the Intl.NumberFormat
instance and reuse that later.
Note The
I18N#nf
in the previous version of Aurelia matches theI18N#createNumberFormat
, whereasI18N#nf
provides the formatted number instead.
Un-format number via code
Numeric strings can be converted back to a number using the I18N#uf
method. The method takes the numeric string as the first argument, followed by an optional second argument for the locale, as shown in the following example.
Formatting dates
The @aurelia/i18n
plugin provides date formatting using Intl
API.
Format date in view using ValueConverter and/or BindingBehavior
The df
ValueConverter and BindingBehavior can format dates in a declarative way from the view. Both take two optional arguments, apart from the date being formatted, which are options, and locale, respectively.
If these are omitted, the date is formatted using the default date formatting options and the currently active locale. A specific locale can be passed on to format the date as per that locale.
The formatting options are used to affect how the date is formatted. For a full list of options, look here.
The value being formatted does not strictly need to be a date object. Apart from Date
instance, both the ValueConverter and the BindingBehavior support integer, integer strings, and ISO 8601 date string as input. In case the input cannot be converted reliably to an instance of Date
, the original input is returned as-is. Integer strings or an integer input are considered as the number of milliseconds since the Unix epoch (for more details, look here).
Note that both ValueConverter and BindingBehavior update the formatted value when the active locale is changed.
Format date via code
Formatting date via code works by using the method I18N#df
. You can pass in the date as its first parameter, followed by the optional parameters options
, and locales
.
Additionally, if needed an instance of Intl.DateTimeFormat
can be created using the I18N#createDateTimeFormat
method.
This can be useful if you want to cache the Intl.DateTimeFormat
instance and reuse it later.
The I18N#df
in the previous version of Aurelia matches the I18N#createDateTimeFormat
, whereas I18N#df
provides the formatted date instead.
Relative time formatting
The @aurelia/i18n
plugin provides relative time formatting using Intl
API.
Relative time format in view using ValueConverter and/or BindingBehavior
The rt
ValueConverter and BindingBehavior can format dates in a declarative way from the view. Both take two optional arguments, options
and locale
, respectively. If these are omitted, the date is formatted using the default formatting options and the currently active locale. A specific locale can be passed on to format the date as per that locale.
The formatting options are used to affect how the date is formatted. For a full list of options, look here. The value being formatted must be an instance of Date
, otherwise, the original value is returned as-is.
Both ValueConverter and BindingBehavior update the formatted value when the active locale is changed.
Relative time format signal
The formatted value can be updated on demand by dispatching the signal 'aurelia-relativetime-signal'
. See the example below.
ValueConverter and BindingBehavior react to this signal and update the view with the formatted value.
Relative time format via code
Formatting relative dates via code work by using the method I18N#rt
. You can pass in the date as its first parameter, followed by the optional parameters options
, and locales
.
Additionally, if needed, an instance of Intl.RelativeTimeFormat
can be created using the I18N#createRelativeTimeFormat
method.
This can be useful if you want to cache the Intl.RelativeTimeFormat
instance and reuse it later.
If you have used relative time formatting with aurelia-i18n
plugin, then you have noticed that instead of full-fledged class dedicated for relative time formatting, in @aurelia/i18n
plugin it is just a couple of methods in I18N
.
A caveat
If the time difference between the target date and now can be expressed as a unit time difference, such as "1 minute" or "1 week", there is a chance that these cases are demoted to a lower time unit, such as "60 seconds", or "7 days".
The reason behind this is the delay between the target date creation time instance ('t') and the time instance when the time difference is being calculated ('now'). If the difference computation is delayed time difference between 't' and 'now' decreases, which causes the time unit demotion. To counter this problem, we use a small value named epsilon
(default to 0.01) to compensate for the delay. We found that usage of this correction produces more consistent and predictable results.
You can change the default epsilon
value using the options.initOptions.rtEpsilon
parameter in the customize
function (see this). Note that a smaller value (close to 0) denotes low tolerance to delay and stricter comparison.
Migration Guide & Breaking Changes
This section outlines the breaking changes introduced by @aurelia/i18n
as compared to the predecessor aurelia-i18n
. Fortunately, there are close to zero breaking changes on how the plugin is used in a view for translation or date and number formatting.
However, there is a small number of breaking changes in the API which will be explained below. We hope this documentation also serves as a migration guide for the well-known use cases.
The version of i18next has changed
aurelia-i18n
used i18next@14.x.x
, whereas @aurelia/i18n
uses i18next@17.x.x
. Therefore all/any of the breaking changes from i18next
also apply here.
Formatting methods return formatted numbers and strings
The formatting methods such as nf
, and df
return formatted numbers and date strings in the new version of the plugin. In the previous version of Aurelia, these methods returned an instance of Intl.NumberFormat
, and Intl.DateFormat
respectively, which would then be used to format the number or date. However, in the new plugin the methods return the formated number or date. In case you required the old methods, those are still available under the names createNumberFormat
, and createDateTimeFormat
respectively. For more details, see the section on number, and date formatting.
All value converters are now signalable
All ValueConverters in @aurelia/i18n
are now signalable as compared to aurelia-i18n
, where no i18n value converters were signalable. This means that the associated translation or the formatting will be automatically updated when the active locale is changed.
Breaking changes affecting relative time formatting
Several breaking changes affect relative time formatting. Following is a short list of breaking changes; for full details, see the section on relative time formatting.
Relative time formatting can now be done using the rt method
The relative time formatting can now be done using the rt
method in the I18N
class, as compared to the full-fledged RelativeTime
class in aurelia-i18n
. Therefore, instead of RelativeTime#getRelativeTime
, you can use I18N#rt
. The class RelativeTime
no longer exists in the new version.
Translation resources no longer need to be registered for relative time formatting
Registering translation resources for relative time formatting is no longer required, as the new API relies on Intl.RelativeTimeFormat
, which uses the locale data provided by the environment.
A minor change to relative time format
There is a minor downside that arises when we dropped support for custom translation resources for relative time. The smallest time unit supported by Intl.RelativeTimeFormat
is second
. Therefore, any time difference that is shorter than a second is approximated to one second and formatted. Such cases where formatted in aurelia-i18n
as a variant of "now", which is no longer possible in the new plugin.
A new time unit "week" has been added
There is a new time unit "week" in Intl.RelativeTimeFormat
API which was not present in the RelativeTime
class under aurelia-i18n
.
Last updated