i18n
Internationalization (short: i18n
) involves the translation of Jovo app content into different locales. It also comes with the benefit of separating content from logic.
Introduction
In a Jovo app, i18n
means that content is stored either in distinct language files (e.g. en.json
or en-US.json
) or a CMS instead of inside handlers or output classes. The app then retrieves the right content depending on the current request's language/locale.
There are multiple benefits that come with this approach:
- It makes an app work across languages (e.g. English
en
and Germande
) without having to touch the core logic. - It offers the possibility to create localized content, even in the same language (e.g US
en-US
and UKen-UK
English). - Even for apps that only support one language, it separates content from logic by storing all strings in a separate, isolated file.
Jovo uses a package called i18next
to support internationalization. You can find its documentation here..
With Jovo and i18next
, output that is dependent on a locale can get outsourced into language resource files:
// src/i18n/en.json { "translation": { "hello": "Hello World!" } }
In the handlers, the hard-coded string would then be replaced by the $t()
method:
// Without i18n return this.$send('Hello World!'); // With i18n return this.$send(this.$t('hello'));
The integration can also be used in output classes:
// Without i18n build() { return { message: 'Hello World!', } } // With i18n build() { return { message: this.$t('hello'), } }
Learn more about the different ways of returning output in the output docs.
Configuration
Add an i18n
object to your app configuration, for example in app.ts
:
import { App } from '@jovotech/framework'; // ... const app = new App({ // ... i18n: { // Configuration }, });
You can add any configuration that is available for i18next
. You can find all configuration options here. The default is:
{ i18n: { interpolation: { // https://www.i18next.com/translation-function/interpolation escapeValue: false, skipOnVariables: false, // Added for backwards compatibility, see https://www.i18next.com/misc/migration-guide#skiponvariables-true }, returnObjects: true, // https://www.i18next.com/translation-function/objects-and-arrays compatibilityJSON: 'v3', // Added for backwards compatibility, see https://www.i18next.com/misc/migration-guide#json-format-v4-pluralization } }
The most relevant option is the resources
object that contains references to all language files. The example below imports a file for the en
locale and adds it as resource:
import { App } from '@jovotech/framework'; import en from './i18n/en.json'; // ... const app = new App({ // ... i18n: { resources: { en, }, }, });
Import all language resource files as shown in the example above and add them to the resources
object. Any locale key like en
, en-US
, de-DE
can be used:
{ i18n: { resources: { localeKey: localeResources, // localeKey: en, 'en-US', 'de-DE', ... }, }, // ... }
Content Structure
i18n
content can usually be found in a folder called i18n
or languageResources
in your app's src
folder. Each locale has its own file (e.g. en.json
) that includes all the content wrapped in a translation
object:
{ "translation": { "hello": "Hello World!", "goodbye": "See you soon." } }
i18next
now supports a new v4
JSON format. For backwards compatibility, Jovo projects use the v3
format by default (see all default options in the configuration section):
{ i18n: { compatibilityJSON: 'v3', // Added for backwards compatibility, see https://www.i18next.com/misc/migration-guide#json-format-v4-pluralization // ... } }
i18next
provides many features to structure your content. You can find more information in their official documentation about interpolation, formatting, plurals, and more.
In the next sections, we'll look specifically into parameters, randomization, nested objects, and platform specific translations.
Parameters
You can pass parameters to the $t()
method and reference them like this:
{ "translation": { "hello": "Hello {{name}}!" } }
For example, the following method call would return Hello Sam!
:
this.$t('hello', { name: 'Sam' });
See content access for more information.
Arrays and Randomization
The $t()
method can also return arrays and objects if the returnObjects
configuration is set to true
, which is the default in the Jovo i18next
integration.
{ "translation": { "hello": ["Hello!", "Hi", "Howdy!"] } }
Arrays of strings can be used to randomize content for message
and reprompt
elements of an output template. Only one of the strings gets randomly selected. This allows for variation in your app's content without making any code modifications.
By default, $t()
is expected to return a string. This is why you need to typecast to a different type if you're returning something else, for example:
this.$t<string[]>('hello'); // Alternative this.$t('hello') as string[];
Nested Objects
i18n
also supports nested objects:
{ "translation": { "hello": { "message": "Hello World! What's your name?" } } }
The message above could be referenced like this:
this.$t('hello.message');
The $t()
method can also return full objects. This allows for some interesting and flexible ways how to structure your content. For example, i18n
resources could be structured in the same way as Jovo output templates:
{ "translation": { "hello": { "message": "Hello World! What's your name?", "reprompt": "Could you tell me your name?" } } }
In an output class, the object could then be returned like this:
build() { return this.$t('hello'); }
By default, $t()
is expected to return a string. This is why you need to typecast to a different type if you're returning something else, for example:
this.$t<{ message: string; reprompt: string }>('hello');
This feature requires the returnObjects
configuration to be set to true
, which is the default in the Jovo i18next
integration.
Platform Specific Translations
For platform specific translations, you can add an object with the platform name (in camel case, for example alexa
, googleAssistant
) that includes translations that override the default translations:
{ "translation": { "hello": { "message": "Hello World! What's your name?" } }, "alexa": { "translation": { "hello": { "message": "Hello World on Alexa! What's your name?" } } } }
Jovo automatically returns the right string depending on the current platform.
Content Access
You can access the content both using this.$t()
. Learn more about all options
in the official i18next documentation.
this.$t(key, options); // Example without parameters this.$t('hello'); // Example with parameters this.$t('hello', { name: 'Sam' }); // Example with nested object this.$t('hello.message');
Usually this is done either inside a handler:
// Without i18n return this.$send({ message: 'Hello World!' }); // With i18n return this.$send({ message: this.$t('hello') });
Or inside an output class:
// Without i18n build() { return { message: 'Hello World!', } } // With i18n build() { return { message: this.$t('hello'), } }
TypeScript
By default, the $t
method introduced in the content access section is expected to return a string. This is why you need to typecast to a different type if you're returning something else, for example:
this.$t<string[]>('hello'); // Alternative this.$t('hello') as string[];
To get hints for the i18n keys when using $t
, you can add the following to your app.ts
file:
declare module '@jovotech/framework/dist/types/I18Next' { interface I18NextResources { // Reference resources here en: typeof en; } }