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:
-
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. -
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)
- Otherwise, a key is created in the database with the default label ("Arrival at {0}" in our example) used for the current language
-
If the text passed as a parameter is an
I18nLabelValue
object whosedefaultI18n
field contains a value for the current language, this will be used -
It is then possible to consult and modify this label in the administration interface:
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?")
}
}