0

I'm using an Azure Web App Bot (Node v 6.9.1) and when the Test in Web Chat interface is initialized, text and textLocale are null. But when the first input is received from the user, event.textLocale is assigned a default 'en' value so I'm not able to reset this just once from bot.use(receive) after doing a language detect (as it is not null at this stage).

Is there an alternative way to reset event.textLocale just once as I'm not able to propagate the locale value through any global node.js variable from bot.use(receive) to bot.use(send) event either? Please help, thanks

app.js Snippet

bot.use({
    receive: function (event, next) {       
        var text = ''; 
        async.waterfall([
            function(callback) {
                text= event.text;
                textLocale = event.textLocale;
                request.get(
                    'http://<<<hostname>>>/translateIntent?intent='+text.toString()+'&textLocale='+(textLocale).toString(), 
                    function (error, response, body) {
                        if (!error && response.statusCode == 200) {
                            var jsonStr = JSON.parse(body);
                            event.text=jsonStr.outputText;
                            // Storing the user's text locale if not already set 
                            // - does not execute as event.textLocale is set by default and global variables are not propagating from receive to send 
                            if(!event.textLocale && (jsonStr.inputLanguageContext != "(Unknown)")) {
                                event.textLocale = jsonStr.inputLanguageContext;
                                console.log("Rewriting event text locale to :" + jsonStr.inputLanguageContext); 
                            }

                            callback(null, event);
                        }
                    }
                );  
            },
            function(event, callback) {
                console.log("Rec after modification:");
                console.log("event.textLocale: " + event.textLocale);
                next();
                callback(null, 'done');
            }
        ]);
    },
    send: function (event, next) {
        // translate response using event.textLocale as languageTo  {event.text, languageFrom='en', event.textLocale}
    } 
});

translate api:

app.get("/translateIntent", function(request, response) {
    var inputText = request.param('intent');
    if(inputText) {
        var textLocale = '';    var outputText = '';
        // If locale is already set, languageDetectHelper does not need to be called  
        if(request.param('textLocale')) {
            textLocale = request.param('textLocale');
            if(textLocale == 'en') {
                // No translation required, just send back the inputText
                response.end({outputText:inputText, inputLanguageContext:languageCodeDetected}); 
            } else {
                // Call translate, with languageFrom as textLocale, and languageTo = 'en'
                translateHelper.GetTranslation(inputText, textLocale, languageTo).then(jsonStr =>  {
                    response.end(jsonStr);
                }).catch(error => { 
                    console.log("Error: " + error); 
                }) 
            }
        } else {
            // Locale not yet set, call detectLanguage 
            languageDetectHelper.GetTextLanguage(inputText).then(languageCodeDetected => {
                var languageTo = 'en';  
                if(languageCodeDetected == 'en' || languageCodeDetected == '(Unknown)') {
                    // Send back without translation 
                    response.end({outputText:inputText, inputLanguageContext:languageCodeDetected}); 
                } else {
                    // Translate to English 
                    translateHelper.GetTranslation(inputText, languageCodeDetected, languageTo).then(jsonStr =>  {
                        console.log("JSON stringify output in server: " + JSON.stringify(jsonStr));
                        response.end(jsonStr);
                    }).catch(error => { 
                        console.log("Error: " + error); 
                    }) 
                }               
            }).catch(error=> {
                console.log("Error: " + error);
            });     
        }
    } else {
        console.log("No translation required, waiting for the next event"); 
    }
});
mlnewbie
  • 35
  • 7

1 Answers1

1

In my understanding, you're trying to detect user's input language and then if it's not English, translate it to English so your backend like LUIS service can handle it, and after handling your bot will send English response by default to your user, but you want to translate the response again back to user's input language.

Since I can't use your code to build a complex demo, I'm here use azure cognitive service to create this demo:

var tokenHandler = require('./tokenHandler');
tokenHandler.init();

var TOLOCALE = 'en';
var FROMLOCALE;

bot.use({
    receive: function (event, next) {
        var token = tokenHandler.token();

        //text ayalytics 
        if(event.text != ""){
            var options = {
                method: 'POST',
                url: 'https://westcentralus.api.cognitive.microsoft.com/text/analytics/v2.0/languages',
                body: { documents: [{ id: 'message', text: event.text }]},
                json: true,
                headers: {
                    'Ocp-Apim-Subscription-Key': 'YOUR-KEY'
                }
            };
            request(options, function (error, response, body) {
                if (!error && body) {
                    if (body.documents && body.documents.length > 0) {
                        var languages = body.documents[0].detectedLanguages;
                        if (languages && languages.length > 0) {
                            event.textLocale = languages[0].iso6391Name;
                            FROMLOCALE = event.textLocale;
                        }
                    }
                }
            //text translations
            if (token && token !== ""){ //not null or empty string
                var urlencodedtext = urlencode(event.text); // convert foreign characters to utf8
                var options = {
                    method: 'GET',
                    url: 'http://api.microsofttranslator.com/v2/Http.svc/Translate'+'?text=' + urlencodedtext + '&from=' + FROMLOCALE +'&to=' + TOLOCALE,
                    headers: {
                        'Authorization': 'Bearer ' + token
                    }
                };
                request(options, function (error, response, body){
                    //Check for error
                    if(error){
                        return console.log('Error:', error);
                    } 
                    else if(response.statusCode !== 200){
                        return console.log('Invalid Status Code Returned:', response.statusCode);
                    } 
                    else {
                        parseString(body, function (err, result) {
                            console.log(result.string._);
                            event.text = result.string._;
                            next();
                        });

                    }
                });
            } 
            else{
                    console.log("No token");
                    next();
                }
            });
        }
        else{
            next();
        }
    },
    send: function (event, next) {
        // translate response using event.textLocale as languageTo  {event.text, languageFrom='en', event.textLocale}
        var token = tokenHandler.token();
        if (token && token !== ""){
            var options = {
                method: 'GET',
                url: 'http://api.microsofttranslator.com/v2/Http.svc/Translate'+'?text=' + event.text + '&from=' + TOLOCALE +'&to=' + FROMLOCALE,
                headers: {
                    'Authorization': 'Bearer ' + token
                }
            };
            request(options, function (error, response, body){
                //Check for error
                if(error){
                    return console.log('Error:', error);
                } 
                else if(response.statusCode !== 200){
                    return console.log('Invalid Status Code Returned:', response.statusCode);
                } 
                else {
                    parseString(body, function (err, result) {
                        console.log(result.string._);
                        event.text = result.string._;
                        next();
                    });

                }
            });
        }
    }
});

//=========================================================
// Bots Dialogs
//=========================================================
var luisAppUrl = 'YOUR-LUIS-ENDPOINT';
var recognizer = new builder.LuisRecognizer(luisAppUrl);
var intents = new builder.IntentDialog({ recognizers: [recognizer] });
bot.dialog('/', intents);

//Route the luis intents to the various dialogs
intents.matches('greeting', (session)=>{
    session.send('You reached Greeting intent, you said \'%s\'.', session.message.text);
}).onDefault((session)=>{
    session.send('Sorry, I did not understand \'%s\'.', session.message.text);
});

The code for tokenhandler is like this:

var request = require('request');

var token = "";
var tokeninterval;
var TRANSLATIONKEY = 'YOUR-KEY';

function getToken() {

    var options = {
        method: 'POST',
        url: 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken?Subscription-Key=' + TRANSLATIONKEY
    };

    request(options, function (error, response, body){
        //Check for error
        if(error){
            return console.log('Error:', error);
        } else if(response.statusCode !== 200){
            return console.log('Invalid Status Code Returned:', response.statusCode);
        } else {
            //Token gets returned as string in the body
            token = body;
        }
    });

    interval = setTimeout(getToken, 540000); // runs once every 9 minutes, token lasts for 10
}

// Stop the token generation
function stopInterval() {
    clearTimeout(tokeninterval);
}

module.exports = {
  init: function() {
      getToken();
  }, 
  stop: function() {
      stopInterval();
  },
  token: function () {
      return token;
  }
};

This demo works by my side, but for your demo, if you want to use the locale that is set in receive, you can directly define a variable like the FROMLOCALE in my code, set the value to it in receive and use this variable in send.

If I've misunderstand your requirement, please feel free to let me know.

Grace Feng
  • 16,564
  • 2
  • 22
  • 45
  • @grace-feng-msft Would it be possible to extend the token handler (with for example a getLocale and a setLocale) to propagate the locale through it from Receive to Send please? We tried and it did not work. The issue with the above code is that since a language detect is being done once for every input, it switches from detecting Malay first (which should be the persisting locale after the first detect) to detecting Indonesian in the next intent etc since we're equating the FROMLOCALE to whatever the languageDetector is finding instead of equating it to some global static variable. – mlnewbie Feb 13 '18 at 13:13
  • @mlnewbie, if you only want to detect locale for the first time, you can add a 'if/ else' condition before language detection to see if `FROMLOCALE` is an empty value, no matter we wrap this detection function to a module or not, still we need to call it for the first time before translation, so we can try to add a condition to achieve this requirement here. And if you wan to wrap it to a module and make the `FROMLOCALE` to a global variable, it's good. – Grace Feng Feb 14 '18 at 05:45
  • @mlnewbie, You can refer to [node.js global variables](https://stackoverflow.com/questions/5447771/node-js-global-variables) to see how to make the `FROMLOCALE` here for global using. And I only provide a demo here to show the process of handling such scenario, not a completed project, so if you have other requirements, you may open a new thread in SO for further discussion, sorry I can't email you. Finally if you find this answer helpful, could you please mark this answer? Thank you. – Grace Feng Feb 14 '18 at 05:48