DEV Community

Cover image for Alexa in Arabic
awedis
awedis

Posted on • Edited on

Alexa in Arabic

Let us make a quote generator skill

The main points that will be tackled in this article:

  • Implementing the Arabic language in Alexa
  • Localization
  • DynamoDB communication

To keep our skill simple, will create a random quote generator, when you ask Alexa for a quote it will say a random one from our list of quotes, later to make things a bit more interesting will add functionality that you can ask a quote for different modes, like for example "I want a motivation quote" or "give me a business quote" which will read the data from DynamoDB

First let's see the steps need to be done to add the Arabic language

  • Route to language settings & add a new language from the available list, choose Arabic and save it.
    The images shown below are a quick flow of how can be done using the console.
    screen 1
    screen 2
    screen 3

  • We will have 2 interaction models one for the English language & another one for the Arabic.

English Interaction Model (en-US.json)

{
  "interactionModel": {
      "languageModel": {
          "invocationName": "random quote",
          "intents": [
              {
                  "name": "AMAZON.CancelIntent",
                  "samples": []
              },
              {
                  "name": "AMAZON.HelpIntent",
                  "samples": []
              },
              {
                  "name": "AMAZON.StopIntent",
                  "samples": []
              },
              {
                  "name": "AMAZON.NavigateHomeIntent",
                  "samples": []
              },
              {
                  "name": "RandomQuoteIntent",
                  "slots": [],
                  "samples": [
                      "give me quote",
                      "I want a quote"
                  ]
              }
          ],
          "types": []
      }
  }
}
Enter fullscreen mode Exit fullscreen mode

Arabic Interaction Model (ar-SA.json)

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "قول عشوائي",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.FallbackIntent",
                    "samples": []
                },
                {
                    "name": "RandomQuoteIntent",
                    "slots": [],
                    "samples": [
                        "من فضلك أعطني قولاً",
                        "أريد قولًا"
                    ]
                }
            ],
            "types": []
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Our localization function, where all the magic happens

  • The function is executed on the request interceptor: Request interceptors are invoked immediately before the execution of the selected handler for an incoming request. You can use them to add any logic that needs to be performed for each request, irrespective of the type of request.

Let us add i18Next package which will handle our internationalization logic

"dependencies": {
    "ask-sdk-core": "^2.6.0",
    "ask-sdk-model": "^1.18.0",
    "aws-sdk": "^2.326.0",
    "i18next": "^20.3.2"
}
Enter fullscreen mode Exit fullscreen mode

Add inside the exports.handler

.addRequestInterceptors(
  LocalisationRequestInterceptor
)
Enter fullscreen mode Exit fullscreen mode

LocalisationRequestInterceptor function will check what language the user is using and it will return a list of locales for that specific language

const LocalisationRequestInterceptor = {
    process(handlerInput) {
        i18n.init({
            lng: Alexa.getLocale(handlerInput.requestEnvelope),
            resources: languageStrings
        }).then((t) => {
            handlerInput.t = (...args) => t(localizationClient(...args));
        });
    }
};
Enter fullscreen mode Exit fullscreen mode

Our localizationClient function will check the local type if its object returns its value, else if its array, it will return a random value from it, how cool is that right? 😉 now all we have to do is to use the function and add some locales to our code

const localizationClient = function () {
    const args = arguments;
    const value = i18n.t(args[0], {
        returnObjects: true
    });
    if (Array.isArray(value)) {
        return value[Math.floor(Math.random() * value.length)];
    } else {
        return value;
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally this way we can use our helper function "t" 😀

const speakOutput = handlerInput.t('WELCOME_MSG');
Enter fullscreen mode Exit fullscreen mode

Now our locales.js file which holds all our speeches for different languages

module.exports = {
    en: {
        translation: {
            WELCOME_MSG: `Welcome to random quote, say I want a quote`,
        }
    },
    ar: {
        translation: {
            WELCOME_MSG: `مرحبًا بك في قول عشوائي ، قل أريد قولً`,
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Outputs:
output-1-en
output-1-ar

Let's make our skill more interesting using DynamoDB 😎

What's DynamoDB? Amazon DynamoDB is a fully managed proprietary NoSQL database service that supports key–value and document
data structures

First, add the right permissions so that our lambda function can access DynamoDB, below image shows the policy that can be attached to the role
iam
Great now let's create our table, with the data in it. Will name the table randomQuote, and let's give a partition-key "languageId" which will hold our language type. This way it will become simple to make queries to it, and for the modes let's have two "motivation" & "business" types, below images show both the English and Arabic languages that are created.
dynamo-en
dynamo-ar
Let's check our updated interaction models, for the customQuote slot we will use AMAZON.SearchQuery to keep things simple, but you can use custom slot types too where you need to define a list of synonyms.

{
  "name": "CustomQuoteIntent",
  "slots": [
    {
      "name": "customQuote",
      "type": "AMAZON.SearchQuery"
    }
  ],
  "samples": [
    "give me a {customQuote} quote",
    "I want a {customQuote} quote"
  ]
}
Enter fullscreen mode Exit fullscreen mode
{
  "name": "CustomQuoteIntent",
  "slots": [
    {
      "name": "customQuote",
      "type": "AMAZON.SearchQuery"
    }
  ],
  "samples":
    "أعطني مقولة {customQuote}",
    "أريد مقولة {customQuote}"
  ]
}
Enter fullscreen mode Exit fullscreen mode

In order to make our queries will have two helper functions, one that creates the connection with the database, and the other one that does the query

  • dbHelper.js
const AWS = require("aws-sdk");
const CONFIG = require("../config/aws");

module.exports.dynamoDBHelper = async function dynamoDBHelper() {
    AWS.config.update({region: CONFIG.REGION});
    const dynamoDB = new AWS.DynamoDB.DocumentClient();
    return dynamoDB;
}
Enter fullscreen mode Exit fullscreen mode
  • queryHelper.js
const CONFIG = require("../config/aws");
const tableName = CONFIG.TABLE_NAME;
const dbHelper = require("./dbHelper");

var queries = function () {};

queries.prototype.getQuotes = async (languageID) => {
    const params = {
        TableName: tableName,
        KeyConditionExpression: "#languageID = :language_id",
        ExpressionAttributeNames: {
            "#languageID": "languageId"
        },
        ExpressionAttributeValues: {
            ":language_id": languageID
        }
    }
    const dynamoDB = await dbHelper.dynamoDBHelper();
    const response = await dynamoDB.query(params).promise();
    return response;
}

module.exports = new queries();
Enter fullscreen mode Exit fullscreen mode

let's have a quick look to our query response through Amazon CloudWatch.
Amazon CloudWatch is a monitoring and management service that provides data and actionable insights for AWS, hybrid, and on-premises applications and infrastructure resources. With CloudWatch, you can collect and access all your performance and operational data in form of logs and metrics from a single platform.
cloudwatch
Nice, now let us check the Intent Handler function in index.js

const CustomQuoteIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'CustomQuoteIntent';
    },
    async handle(handlerInput) {
        const slotValue = handlerInput.requestEnvelope.request.intent.slots.customQuote.value;
        const languageID = Alexa.getLocale(handlerInput.requestEnvelope);
        let speakOutput;

        try {
            let response = await queries.getQuotes(languageID);
            let quoteArray = response.Items[0][slotValue];
            speakOutput = quoteArray[Math.floor(Math.random() * quoteArray.length)];
        } catch (error) {
            console.log('-- ERROR --', error);
            speakOutput = handlerInput.t('ERROR');
        }

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};
Enter fullscreen mode Exit fullscreen mode

The function will make a query using our queryHelper functions, in these two lines we take the list of quotes (by its mode) and then a random quote from it

let quoteArray = response.Items[0][slotValue];
speakOutput = quoteArray[Math.floor(Math.random() * quoteArray.length)];
Enter fullscreen mode Exit fullscreen mode

Our output
output-2-en
output-2-ar

That's it 😊 This was a bit long article, hope it was really helpful and clear, of course, some stuff can be improved and enhanced but for the sake of keeping stuff simple, I didn't want to go too deep.

Moreover implementing the Arabic language in Alexa will open doors for many skills that can be done and published in new regions with Arabic language demand, again hope it was straightforward and easy to grab with me. I wish you all a fun and engaging skill development journey.

The source code
https://github.com/awedis/alexa-random-quote

Top comments (2)

Collapse
 
hasan_alhamadi_eec8c5d6b6 profile image
Hasan AlHamadi

Dear Awedis

Thank you for your post it is very interesting, my only problem is the first step to change the language using the app to Arabic, it works (I mean I can find Arabic) in some devices and some I do not. I have tried to update the app and asked Alexa to update firmware and so on, however without any luck. Any idea how to add arabic language to the list so I can select it ?

Many Thanks

Collapse
 
awedis profile image
awedis

Hello Hasan,
Yes you can change the language from the app, but please can you make sure that the arabic language is enabled on the skill first. After that it should be available on the app.