Skip to content

Internationalize #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 90 additions & 55 deletions lambda/custom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
/* eslint-disable no-console */

const Alexa = require('ask-sdk-core');
const i18n = require('i18next');
const sprintf = require('i18next-sprintf-postprocessor');

const languageStrings = {
'en': require('./languages/en.js'),
};

/* INTENT HANDLERS */

Expand All @@ -34,9 +40,13 @@ const LaunchRequestHandler = {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak('Welcome to Decision Tree. I will recommend the best job for you. Do you want to start your career or be a couch potato?')
.reprompt('Do you want a career or to be a couch potato?')
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

return responseBuilder
.speak(requestAttributes.t('WELCOME_MESSAGE'))
.reprompt(requestAttributes.t('WELCOME_REPROMPT'))
.getResponse();
},
};
Expand All @@ -45,12 +55,16 @@ const CouchPotatoIntent = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;

return request.type === 'IntentRequest'
return request.type === 'IntentRequest'
&& request.intent.name === 'CouchPotatoIntent';
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak('You don\'t want to start your career? Have fun wasting away on the couch.')
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

return responseBuilder
.speak(requestAttributes.t('COUCH_POTATO_RESPONSE'))
.getResponse();
},
};
Expand All @@ -69,12 +83,16 @@ const InProgressRecommendationIntent = {

for (const slotName of Object.keys(handlerInput.requestEnvelope.request.intent.slots)) {
const currentSlot = currentIntent.slots[slotName];
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

if (currentSlot.confirmationStatus !== 'CONFIRMED'
&& currentSlot.resolutions
&& currentSlot.resolutions.resolutionsPerAuthority[0]) {
if (currentSlot.resolutions.resolutionsPerAuthority[0].status.code === 'ER_SUCCESS_MATCH') {
if (currentSlot.resolutions.resolutionsPerAuthority[0].values.length > 1) {
prompt = 'Which would you like';
prompt = requestAttributes.t('DECISION_PROMPT');
const size = currentSlot.resolutions.resolutionsPerAuthority[0].values.length;

currentSlot.resolutions.resolutionsPerAuthority[0].values
Expand All @@ -84,17 +102,17 @@ const InProgressRecommendationIntent = {

prompt += '?';

return handlerInput.responseBuilder
return responseBuilder
.speak(prompt)
.reprompt(prompt)
.addElicitSlotDirective(currentSlot.name)
.getResponse();
}
} else if (currentSlot.resolutions.resolutionsPerAuthority[0].status.code === 'ER_SUCCESS_NO_MATCH') {
if (requiredSlots.indexOf(currentSlot.name) > -1) {
prompt = `What ${currentSlot.name} are you looking for`;
prompt = requestAttributes.t(UNMATCHED_SLOT_PROMPT, currentSlot.name);

return handlerInput.responseBuilder
return responseBuilder
.speak(prompt)
.reprompt(prompt)
.addElicitSlotDirective(currentSlot.name)
Expand All @@ -104,7 +122,7 @@ const InProgressRecommendationIntent = {
}
}

return handlerInput.responseBuilder
return responseBuilder
.addDelegateDirective(currentIntent)
.getResponse();
},
Expand All @@ -122,19 +140,17 @@ const CompletedRecommendationIntent = {
const filledSlots = handlerInput.requestEnvelope.request.intent.slots;

const slotValues = getSlotValues(filledSlots);
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

const key = `${slotValues.salaryImportance.resolved}-${slotValues.personality.resolved}-${slotValues.bloodTolerance.resolved}-${slotValues.preferredSpecies.resolved}`;
const occupation = options[slotsToOptionsMap[key]];
const occupation = requestAttributes.options[slotsToOptionsMap[key]];


const speechOutput = `So you want to be ${slotValues.salaryImportance.resolved
}. You are an ${slotValues.personality.resolved
}, you like ${slotValues.preferredSpecies.resolved
} and you ${slotValues.bloodTolerance.resolved === 'high' ? 'can' : "can't"
} tolerate blood ` +
`. You should consider being a ${occupation.name}`;

return handlerInput.responseBuilder
.speak(speechOutput)
return responseBuilder
.speak(requestAttributes.t('COMPLETED_RECOMMENDATION_MESSAGE', slotValues.salaryImportance.resolved, slotValues.personality.resolved, slotValues.preferredSpecies.resolved, (slotValues.bloodTolerance.resolved === 'high' ? 'can' : "can't"), occupation.name))
.getResponse();
},
};
Expand All @@ -143,13 +159,17 @@ const HelpHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;

return request.type === 'IntentRequest'
return request.type === 'IntentRequest'
&& request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak('This is Decision Tree. I can help you find the perfect job. You can say, recommend a job.')
.reprompt('Would you like a career or do you want to be a couch potato?')
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

return responseBuilder
.speak(requestAttributes.t('HELP_MESSAGE'))
.reprompt(requestAttributes.t('HELP_REPROMPT'))
.getResponse();
},
};
Expand All @@ -163,8 +183,12 @@ const ExitHandler = {
|| request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak('Bye')
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

return responseBuilder
.speak(requestAttributes.t('EXIT_MESSAGE'))
.getResponse();
},
};
Expand All @@ -188,9 +212,13 @@ const ErrorHandler = {
handle(handlerInput, error) {
console.log(`Error handled: ${error.message}`);

return handlerInput.responseBuilder
.speak('Sorry, I can\'t understand the command. Please say again.')
.reprompt('Sorry, I can\'t understand the command. Please say again.')
const attributesManager = handlerInput.attributesManager;
const responseBuilder = handlerInput.responseBuilder;
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();

return responseBuilder
.speak(requestAttributes.t('ERROR_MESSAGE'))
.reprompt(requestAttributes.t('ERROR_MESSAGE'))
.getResponse();
},
};
Expand Down Expand Up @@ -233,30 +261,6 @@ const slotsToOptionsMap = {
'very-extrovert-high-people': 5,
};

const options = [
{ name: 'Actor', description: '' },
{ name: 'Animal Control Worker', description: '' },
{ name: 'Animal Shelter Manager', description: '' },
{ name: 'Artist', description: '' },
{ name: 'Court Reporter', description: '' },
{ name: 'Doctor', description: '' },
{ name: 'Geoscientist', description: '' },
{ name: 'Investment Banker', description: '' },
{ name: 'Lighthouse Keeper', description: '' },
{ name: 'Marine Ecologist', description: '' },
{ name: 'Park Naturalist', description: '' },
{ name: 'Pet Groomer', description: '' },
{ name: 'Physical Therapist', description: '' },
{ name: 'Security Guard', description: '' },
{ name: 'Social Media Engineer', description: '' },
{ name: 'Software Engineer', description: '' },
{ name: 'Teacher', description: '' },
{ name: 'Veterinary', description: '' },
{ name: 'Veterinary Dentist', description: '' },
{ name: 'Zookeeper', description: '' },
{ name: 'Zoologist', description: '' },
];

/* HELPER FUNCTIONS */

function getSlotValues(filledSlots) {
Expand Down Expand Up @@ -299,7 +303,37 @@ function getSlotValues(filledSlots) {
}, this);

return slotValues;
}
};

const LocalizationInterceptor = {
process(handlerInput) {
const localizationClient = i18n.use(sprintf).init({
lng: handlerInput.requestEnvelope.request.locale,
resources: languageStrings,
});
localizationClient.localize = function localize() {
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 (Array.isArray(value)) {
return value[Math.floor(Math.random() * value.length)];
}
return value;
};
const attributes = handlerInput.attributesManager.getRequestAttributes();
attributes.t = function translate(...args) {
return localizationClient.localize(...args);
};
},
};


exports.handler = skillBuilder
.addRequestHandlers(
Expand All @@ -309,7 +343,8 @@ exports.handler = skillBuilder
CompletedRecommendationIntent,
HelpHandler,
ExitHandler,
SessionEndedRequestHandler,
SessionEndedRequestHandler,
)
.addRequestInterceptors(LocalizationInterceptor)
.addErrorHandlers(ErrorHandler)
.lambda();
45 changes: 45 additions & 0 deletions lambda/custom/languages/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright <YEAR> Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Licensed under the Amazon Software License
// http://aws.amazon.com/asl/

module.exports = {
translation: {
WELCOME_MESSAGE: 'Welcome to Decision Tree. I will recommend the best job for you. Do you want to start your career or be a couch potato?',
WELCOME_REPROMPT: 'Do you want a career or to be a couch potato?',
COUCH_POTATO_RESPONSE: 'You don\'t want to start your career? Have fun wasting away on the couch.',
DECISION_PROMPT: 'Which would you like, ',
UNMATCHED_SLOT_PROMPT: `What %s are you looking for?`,
COMPLETED_RECOMMENDATION_MESSAGE: `So you want to be %s. You are an %s, you like %s and you %s tolerate blood. You should consider being a %s.`,
EXIT_MESSAGE: 'Goodbye!',
ERROR_MESSAGE: 'Sorry, I can\'t understand the command. Please say again.',
GET_ANSWER_ABBREVIATION: `The %s of %s is <say-as interpret-as='spell-out'>%s</say-as>.`,
GET_CURRENT_SCORE: `Your current score is %s out of %s.`,
GET_FINAL_SCORE: `Your final score is %s out of %s.`,
REPROMPT_SPEECH: `Which other state or capital would you like to know about?`,
HELP_MESSAGE: 'This is Decision Tree. I can help you find the perfect job. You can say, recommend a job.',
HELP_REPROMPT: 'Would you like a career or do you want to be a couch potato?',
},
options: [
{ name: 'Actor', description: '' },
{ name: 'Animal Control Worker', description: '' },
{ name: 'Animal Shelter Manager', description: '' },
{ name: 'Artist', description: '' },
{ name: 'Court Reporter', description: '' },
{ name: 'Doctor', description: '' },
{ name: 'Geoscientist', description: '' },
{ name: 'Investment Banker', description: '' },
{ name: 'Lighthouse Keeper', description: '' },
{ name: 'Marine Ecologist', description: '' },
{ name: 'Park Naturalist', description: '' },
{ name: 'Pet Groomer', description: '' },
{ name: 'Physical Therapist', description: '' },
{ name: 'Security Guard', description: '' },
{ name: 'Social Media Engineer', description: '' },
{ name: 'Software Engineer', description: '' },
{ name: 'Teacher', description: '' },
{ name: 'Veterinary', description: '' },
{ name: 'Veterinary Dentist', description: '' },
{ name: 'Zookeeper', description: '' },
{ name: 'Zoologist', description: '' },
],
};
4 changes: 3 additions & 1 deletion lambda/custom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"main": "index.js",
"dependencies": {
"ask-sdk-core": "^2.0.0",
"ask-sdk-model": "^1.0.0"
"ask-sdk-model": "^1.0.0",
"i18next": "^11.8.0",
"i18next-sprintf-postprocessor": "^0.2.2"
},
"devDependencies": {},
"author": "Amazon.com",
Expand Down