Editor's update: check the latest on publishing Skills to the US in these new blog posts in English or Spanish.
Today, we announced that developers can now use the Alexa Skills Kit (ASK) to build skills for Spanish speaking customers in the US using the new Spanish (es-US) language model.
If you are new to skill development, check out this detailed walkthrough to get started. If you’re an experienced Alexa developer, you can enhance your existing skill by extending it to support the new Spanish (US) language model. This tutorial will show you how you can add support for this model for your existing skills. It will also show you how you can use ASK to enable Alexa to respond based on locales.
You will learn:
1. Navigate to your existing skill on the Amazon Developer Portal.
2. Click on the language drop down on the top right of the screen and select the last option: “Language Settings.” In this example, the skill already has language models for English (US), Spanish (Spain), German and French.
3. Follow the steps below to complete the Language Settings screen:
4. Now provide the interaction model for the Spanish (US) version. You can do this by copying the interaction model from one of the English versions of our skill, and localizing the sample utterances and slot values and synonyms. In this example, we’re starting from English (US). Switch to the English version by clicking on the language dropdown in the skill builder, and choose English (US).
5. Click on JSON Editor on the left side bar. This displays the complete interaction model for the skill in JSON format.
6. Select and copy all of the JSON in the code window.
7. Switch back to Spanish (US) using the language dropdown.
8. Click on JSON Editor again, and paste the JSON into the code window, replacing the existing JSON.
9. Localize all sample utterances, slot values, and slot synonyms.
10. Click on the Save Model button.
11. Click on the Build Model button.
We now have the language model built for Spanish (US). You now need to translate the invocation name, the sample utterances, the slot values, and the synonyms.
You also must localize the skill metadata, including skill name, description, keywords and the icons, should they contain localized content, such as text or currency symbols. Skills metadata are available in the “Distribution” tab of the Alexa Developer Console.
In a typical development workflow, you will probably build the skill voice interaction model JSON document programmatically, based on different files you have with sample utterances and slot values.
If your interaction model uses any built-in slot types, you may need to make changes to ensure that the types are supported in the locale. See the Slot Type Reference for a list of slot types for each supported locale.
Once you have finished translating your interaction model for Spanish (US), you need to customize the responses your skill returns for the different locales that you support. Do this by updating your Lambda function.
Now that your skill is ready to support multiple languages, you may want to update your Lambda function to ensure that your skill provides responses translated or tailored to each supported locale.
At least, you need to translate to Spanish (US) the strings the skill is sending to Alexa to render with the voice of Alexa. You can also use this technique to use different strings for different variation of English. For instance, you may want to greet your customers with “G’day” in Australia, “Hello” in Canada and the UK, “Namaste,” in India, "Hi" in the US, “Bonjour” in France, and “Bon matin” in Quebec. You can use any localization library to help you to match strings to locale. In this article I’m using an example I made with the ASK SDK v2 for Node.js, you can replicate
To facilitate localizing your skills, you should use one of the internationalization libraries available for the language of your choosing or write your own. In this example I am using i18next in conjunction with an interceptor. To learn more about how this works, read this post: How to Localize your Alexa Skills
const i18n = require('i18next');
const sprintf = require('i18next-sprintf-postprocessor');
const LocalizationInterceptor = {
process(handlerInput) {
// Gets the locale from the request and initializes
// i18next.
const localizationClient = i18n.use(sprintf).init({
lng: handlerInput.requestEnvelope.request.locale,
resources: languageStrings,
});
// Creates a localize function to support arguments.
localizationClient.localize = function localize() {
// gets arguments through and passes them to
// i18next using sprintf to replace string placeholders
// with arguments.
const args = arguments;
const values = [];
for (let i = 1; i < args.length; i += 1) {
values.push(args[i]);
}
const value = i18n.t(args[0], {
returnObjects: true,
postProcess: 'sprintf',
sprintf: values,
});
// If an array is used then a random value is selected
if (Array.isArray(value)) {
return value[Math.floor(Math.random() * value.length)];
}
return value;
};
// this gets the request attributes and save the localize function inside
// it to be used in a handler by calling requestAttributes.t(STRING_ID, [args...])
const attributes = handlerInput.attributesManager.getRequestAttributes();
attributes.t = function translate(...args) {
return localizationClient.localize(...args);
};
},
};
All my strings follow this structure:
const enData = {
translation: {
SKILL_NAME: 'Space Facts',
GET_FACT_MESSAGE: 'Here\'s your fact: ',
HELP_MESSAGE: 'You can say tell me a space fact, or, you can say exit... What can I help you with?',
HELP_REPROMPT: 'What can I help you with?',
FALLBACK_MESSAGE: 'The Space Facts skill can\'t help you with that. It can help you discover facts about space if you say tell me a space fact. What can I help you with?',
FALLBACK_REPROMPT: 'What can I help you with?',
ERROR_MESSAGE: 'Sorry, an error occurred.',
STOP_MESSAGE: 'Goodbye!',
FACTS:
[
'A year on Mercury is just 88 days long.',
'Despite being farther from the Sun, Venus experiences higher temperatures than Mercury.',
'On Mars, the Sun appears about half the size as it does on Earth.',
'Jupiter has the shortest day of all the planets.',
'The Sun is an almost perfect sphere.',
],
},
};
I’m taking the English string as a base for my translations. The “enData” object contains all the strings used by my skill in the English language. Our interceptor coupled with i18next supports both single strings and arrays, which can be seen on “FACTS”.
Using the English strings as base, I translated them to Spanish also changing the object name from “enData” to “esusData” to reflect the new language.
const esusData = {
translation: {
SKILL_NAME: 'Curiosidades Espaciales',
GET_FACT_MESSAGE: 'Sabías que ',
HELP_MESSAGE: 'Puedes decir dime una curiosidad espacial o puedes decir salir... Cómo te puedo ayudar?',
HELP_REPROMPT: 'Cómo te puedo ayudar?',
FALLBACK_MESSAGE: 'La skill de Curiosidades Espaciales no te puede ayudar con eso. Puede ayudarte a descubrir curiosidades sobre el espacio si dices, dime una curiosidad espacial. Cómo te puedo ayudar?',
FALLBACK_REPROMPT: 'Cómo te puedo ayudar?',
ERROR_MESSAGE: 'Perdona, ha ocurrido un error.',
STOP_MESSAGE: 'Adios!',
FACTS:
[
'Un año en Mercurio es de solo 88 días.',
'A pesar de estar más lejos del Sol, Venus tiene temperaturas más altas que Mercurio.',
'En Marte, el Sol parece de aproximadamente la mitad de tamaño que desde la Tierra.',
'Jupiter tiene el día más corto de todos los planetas.',
'El Sol es una esfera casi perfecta.'
]
}
};
When initializing the i18next service during step 0 you might have noticed a reference to “languageStrings”. This is the list of supported locales and where to find them. Every language is reference by their locale code, i18next will always search for strings in the most specific match first. If for example you have all your strings available for “en” and only a specific welcome message written for “en-AU”, then requests coming from “en-AU” would receive this single welcome message while every other string would just get served from the “en”-pool.
const languageStrings = {
'de-DE': deData,
'en': enData,
'en-US': enusData,
'es': esData,
'es-ES': esesData,
'es-US': esusData
};
Here we added “es-US” to the end of our list and referenced the object we just translated.
Using our interceptor in combination with the i18next library both showed in step 0 we can easily reference strings by their IDs and automatically answering the skill request with the right locale. The selected Strings will be rendered in the language that matches the locale of the incoming request.
This handler for the "AMAZON.HelpIntent" just uses the "t" function saved in our request atributes referencing the ressource ID.
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
return handlerInput.responseBuilder
.speak(requestAttributes.t('HELP_MESSAGE'))
.reprompt(requestAttributes.t('HELP_REPROMPT'))
.getResponse();
}
That’s all that it takes to update your skill for Spanish speaking US customers. We are excited to have Alexa available in Spanish in the US, and we can't wait to see what you will build. This space facts skill localized to Spanish (US) is available for everyone to try out.
Check out our documentation to learn more about how you can use ASK to create multi-language Alexa skills.
Check out the following training resources, tutorials, and code samples to start building Alexa skills: