Internationalize and Localize Custom Component Responses
If you have custom components that return content in conversations, you'll also want
to ensure that they return that content in the user's target language.
There are several ways to accomplish this:
Create resource bundle entries in the skill and reference them
directly from the custom component. This approach enables you to handle
translations of custom component messages in the same place as you do for other
messages from the skill.
Use a system component and resource bundle entries for assembling
the translatable strings that incorporate the data output of the custom
component. This approach enables you to handle translations of custom component
messages in the same place as you do for other messages from the skill while
fostering a looser coupling between the custom component and that particular
skill.
If you want to use the skill's translation service to translate the
component's responses, set the custom component's translate
property to true.
If your component retrieves and returns backend data that needs to
be incorporated into a message and you want to use the skill's translation
service to translate the component's responses, store that returned data in a
variable in the dialog flow. You can then reference this variable in a system
component.
Reference Resource Bundles from the Custom Component π
Just as you can use resource bundles for messages in built-in components, answer intents, etc., you can use resource bundles for your custom components as well. To do so, you:
Using the Bots Node SDK's context.translate() method, reference the resource bundle keys from the custom component code.
The context.translate() takes a specified resource bundle key name (and any parameters specified in the resource bundle entry) and generates the appropriate FreeMarker template required to load the named resource bundle language string when the conversation is sent back to the user via the context.reply() method.
Use the context.reply helper method to print the translated response. For example:
Document all of the resource bundle keys that the custom component references as well as the expected default strings. (Since the custom component directly references the resource bundle key within the skill, there needs to be a high degree of coordination between the developer of the custom component and those building out the skill to ensure that the referenced keys are valid within the skill).
In this example, date.message is a resource bundle key, dateToday and dayOfWeek are variables, and a FreeMarker expression like the following is returned:
${rb('date.message', 'Monday', 'July 12, 2021')}
Note
The context.translate() method only supports resource bundle values that have no parameters or that use positional (numbered) parameters. For example, in the case of the example date.message key, it's value might be something like βToday is {0}, {1}β. Named parameters and complex message formats are not supported.
Use a System Component to Reference a Resource Bundle π
You can use a system component to assemble messages using resource bundle entries
and data that has been returned from a custom component. You define the base message strings
in resource bundle entries. The bundle entries might include parameters for data (such as
numbers and dates) that are output from the custom component. Since the base message strings
are defined in the dialog flow, this approach ensures that custom components are not dependent
on specific implementation code and remain reusable.
Here are the general steps:
For the custom component, include a required input parameter for the name of the context variable to store the returned data in.
Since the custom component developer and dialog flow developer may not be the same person or even on the same team, carefully document what data the custom component returns in that variable and make the information available to any custom component consumers so that they understand how to present the returned data to the user in a message.
In the dialog flow, create a variable to store the custom component's returned data
and pass its name in the required input parameter.
In the dialog flow, reference the resource bundle entry and fill in any required parameters.
The following sample from a skill developed in YAML dialog mode references a custom
component in the initializeReceipt state and passes the name of the variable
(receipt) that holds the component response and purchaseId
as input parameters. The printProduct state then incorporates the
receipt value as a parameter in a reference to the resource bundle entry
named receiptMessage.
The custom code for accessing these input parameters might look something like the following code:
module.exports = {
metadata: () => ({
name: 'myComponent',
properties: {
dataVariable: { required: true, type: 'string' },
purchaseId: { required: true, type: 'string' },
},
...
// Retrieve the value of the 'dataVariable' component property.
const { dataVariable } = context.properties();
if (!dataVariable) {
context.transition();
done(new Error('The state is missing the dataVariable property.'));
}
...
// Retrieve the value of the 'purchaseId' component property.
const { purchaseId } = context.properties();
if (!purchaseId) {
context.transition();
done(new Error('The state is missing the purchaseId property.'));
}
...
context.setVariable(dataVariable, data);
context.transition();
done();
}
}
Send Responses Directly to the Translation Service π
If you don't have a way of knowing what the component's response text will be (e.g. if it is queried from a remote backend), you can use the skill's translation service to translate the responses. To do so:
Make sure the component is set up to have its output sent to the translation service by defining the translate property on the component and setting it to true.
In the custom component, use the context.reply helper method to return the response.
This approach only works with skills that are set up in the Translation Service language mode.
Use a System Component to Pass the Message to the Translation Service π
Custom components that query backend services might return data in a complex format like an object or an array of objects. If you are using a translation service, these data objects can't be sent to the translation service as is. Instead, you need to form a message that references any necessary attributes of the data object individually.
For the custom component, include a required input parameter for the name of the
dialog flow variable to store the returned data in.
Since the custom component developer and dialog flow developer may not be the same person or even on the same team, carefully document what data the custom component returns in that variable and make the information available to any custom component consumers so that they understand how to present the returned data to the user in a message.
In the dialog flow, create a variable to store the custom component's returned data
and pass its name in the required input parameter.
Using the information in the variable, assemble the response in a system component,
like Common Response.
Make sure that the skill is configured for auto-translation.
For skills developed in Visual dialog mode, set the Translate
Bot Response Message property on the skill's
Settings page to true.
For skills developed in YAML dialog mode, you can handle this globally in
the skill by setting the autoTranslate context variable. For
example:
In the following example from a Common Response component's
Metadata property, the variable is dialogVar. The
data object thatβs passed from the custom component to this variable is {product: "an
apple", type: "fruit", origin: "Spain"
}.
responseItems:
- type: "text"
text: "The product in your cart is a ${dialogVar.value.type}. It is
${dialogVar.value.product} from ${dialogVar.value.origin}"
The custom code for accessing this input parameter might look something like the following code:
module.exports = {
metadata: () => ({
name: 'myComponent',
properties: {
dialogVar: { required: true, type: 'string' },
},
...
// Retrieve the value of the 'dialogVar' component property.
const { dialogVar } = context.properties();
if (!dialogVar) {
context.transition();
done(new Error('The state is missing the dialogVar property.'));
}
...
context.setVariable(dialogVar, data);
context.transition();
done();
}
}
Detect the User Language in a Custom Component π
If the custom component needs the user's language to do things like provide correct date formats, you can provide it to the component in one of these ways:
Access the profile.locale and profile.languageTag variables from the custom component code as shown in the following example:
//detect user locale. If not set, define a default
const locale = context.getVariable('profile.locale') ?
context.getVariable('profile.locale') : 'en-AU';
//Make sure locale is returned with hyphen, not underscore. JavaScript requires a hyphen.
const jsLocale = locale.replace('_','-');
//when profile languageTag is set, use it. If not, use profile.locale
const languageTag = context.getVariable('profile.languageTag')?
context.getVariable('profile.languageTag') : jslocale;
Pass the values of profile.locale and/or profile.languageTag as input parameters to the component.
Note
If both variables are set, profile.languageTag takes precedence in the skill.