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.
- 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 totrue
. - 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:
- Define resource bundle entries in the skill for your message. See Create Resource Bundle Keys.
- 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 thecontext.reply()
method. - Use the
context.reply
helper method to print the translated response. For example:context.reply(translate('date.message', dateToday, dayOfWeek ));
- 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')}
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.
- Define resource bundle entries in the skill for your message. See Create Resource Bundle Keys.
- 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
.
initializeReceipt:
component: "sample.receipt.dataresponse"
properties:
dataVariable: "receipt"
purchaseId: "${purchaseId.value}"
...
printProduct:
component: "System.CommonResponse"
properties:
keepTurn: true
metadata:
responseItems:
- type: "text"
text: "${rb('receiptMessage','${receipt.value}')}"
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 totrue
. - 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:setAutoTranslate: component: "System.SetVariable" properties: variable: "autoTranslate" value: input: true output: true
- For skills developed in Visual dialog mode, set the Translate
Bot Response Message property on the skill's
Settings page to
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
andprofile.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/orprofile.languageTag
as input parameters to the component.
If both variables are set, profile.languageTag
takes precedence in the skill.