Skip to content

Developing a multilingual bot (i18n)

The Multilingual bot page of the user documentation presents the basics of internationalization (i18n) to build bots with Tock: prerequisites, Locale, etc.

This page completes this documentation with elements specific to development.

Prerequisites

To activate 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 a valid code whether the module is activated or not.

However, at runtime, the behavior differs significantly.

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

  1. A key will be generated from the text passed as a parameter, based on the namespace (the organization of the bot creator) and the story in which this label is requested. In the above case, this should look like app_arrivals_Arrival at {0} where app is the namespace and arrivals the main intention of the story.

  2. Tock then checks if this key is already present in the database. * If this is the case, it uses the label present in the database for the requested language in order to find the most appropriate translation (the connector or the interface type can also be taken into account)

  3. Otherwise, a key is created in the database with the default label ("Arrival at {0}" in our example) used for the current language
  4. If the text passed as a parameter is an I18nLabelValue object whose defaultI18n field contains a value for the current language, this will be used

  5. It is then possible to consult and modify this label in the administration interface:

Internationalization

Message format

The supported format is that of Java's i18n support, in particular that of the class 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)

Additionally, Tock provides a by extension for dates that allows 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 some practices, although intuitive in Kotlin, should be banned under penalty of unpleasant surprises.

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

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

but poses a problem if it is enabled. Indeed, a new label will be created for each different value of the variable nb!

If it is necessary to send "not to be translated" 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 TO FOLLOW
  • The risk of collision between two labels is low since the main intention of the story is part of the key. If you want to avoid any risk, however, you can use the i18nKey method:
send(i18nKey("my_unique_key", "There are {0} files", nb))

Specifying localizations programmatically

It is possible to define default values ​​for multiple localizations 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 set via TOCK Studio) when the defaultI18n setting 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).

Testing internationalization

A sample test device is available in the source code of the sample bot It is necessary to extend the test extension to then indicate the label correspondence 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("when do you want to leave?")
        }
    }
Chat with Tock
×