Skip to content

Developing a multilingual bot (i18n)

The Multilingual bot page in the user documentation covers the basics of internationalization (i18n) for building bots with Tock: prerequisites, Locale, etc.

This page completes the documentation with elements specific to development.

Prerequisites

To enable Internationalization in Tock, programmatically or not, see Multilingual bot.

Principles

The code does not change once internationalization is activated. For example:

     send("Arrival at {0}", time)

is valid whether the module is enabled or not.

At runtime, however, behavior differs significantly.

If internationalization is enabled, the following operations will be performed:

  1. A key will be generated from the text passed in parameter, according to the namespace (the bot creator’s organization) and the story in which the text is requested. In the case above, this should look like app_arrivals_Arrival at {0} where app is the namespace and arrivals is the story’s main intent.

  2. Tock then checks whether this key is already present in the database.
    • If so, it uses the wording present in the database for the requested language to find the most appropriate translation (the connector or interface type may also be taken into account).
    • If not, a key is created in base with the default label ("Arrival at {0}" in our example) used for the current language.
      • If the passed in text is an I18nLabelValue object which defaultI18n field holds a value for the current language, the latter is used instead
  3. This label can then be viewed and modified in the administration interface:

Internationalisation

Message Format

The supported format is that of i18n support in java, specifically the one from MessageFormat in java. This includes support for ChoiceFormat :

    send("There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.", 2)  

Tock also provides a by extension for dates, allowing you to specify a format in the parameters:

    send("Departure at {0}", departureDateTime by timeFormat) 

User locale

See Multilingual bot.

Points of attention

Tock’s internationalization module is efficient, but certain practices, however intuitive in Kotlin, should be avoided, or you’ll be in for a nasty surprise.

For example, this code works perfectly well with the i18n module deactivated.

send("There are $nb files") //DANGER!

but causes problems when activated. A new label will be created for each different value of the nb variable!

If you need to send “do not translate” responses, use the BotBus.sendRaw, BotBus.endRaw or String.raw methods.

    send("There are $nb files".raw) //CORRECT 
    send("There are {0} files", nb) //FORMAT A SUIVRE 
  • The collision risk between two labels is small as the story’s main intent is made part of the key. If however you want to avoid any risk, you can use the i18nKey method:
    send(i18nKey("my_unique_key", "There are {0} files", nb)) 

Specifying several localizations

Default values can be defined for several locales in a bot’s code:

    send(i18nKey("departure", "Departure at {0}", setOf(I18nLocalizedLabel(Locale.FRENCH, textChat, "Départ à {0}")), nb))

By default, these default values will only be used when the key is used for the first time. To overwrite existing values (including those defined via TOCK Studio) when the default location is changed, set the configuration value tock_i18n_reset_value_on_default_change to true (either as an environment variable, or as a system property).

Test internationalization

An example of a test device is available in the example bot source code It is necessary to extend the test extension and then specify the label match to be tested.

All that remains is to indicate the desired locale:


    @Test
    fun `search story asks for departure date WHEN there is a destination and an origin but no departure date in context`() {
        ext.newRequest("Recherche", search, locale = Locale.FRENCH) {
            destination = lille
            origin = paris

            run()

            firstAnswer.assertText("Quand souhaitez-vous partir?")
        }
    }