Screen Shot 2018-07-20 at 11.53.31 AM

Translating Chat Messages in Realtime with Amazon Translate and ChatEngine

Language translation opens up many doors for where you can take your app. With advancements in cognitive technologies, it’s now possible to translate messages in realtime – a simple, yet powerful functionality that makes your app truly global. From connecting players across the globe in a multiplayer game, to inspiring connection between citizens of different countries, language translation will continue to grow in implementation and adoption.

Tutorial Overview

If you’re looking to build a chat app where users have the choice to select their language at will, then you’ve come to the right place. In this tutorial, we’ll walk you through how to build a multilingual chat app with on-the-fly language selection feature.

But building a chat app from the ground-up has its challenges. That’s why at PubNub, we developed a full-featured chat framework called ChatEngine, which leverages our network to deliver a best-in-class chat experience at scale. For developers, it offers a super friendly set of APIs and a set of packaged PubNub services that take care of both the front end and the backend development of a chat app.

It’s also extensible, meaning third-party APIs can plug into it with ease. For a multilingual chat app, we need this feature to translate between languages. We have chosen the Amazon Translate service for this purpose.

This tutorial is divided into three main portions:

  • Basic Chat Feature: Learn about essential ChatEngine features and how to build your own chat app with ChatEngine.
  • Language Translation: Insight into what makes the language translation feature work using Amazon Translate API.
  • App Deployment and Testing: Setting up the various components and deploying and testing the application.

The full GitHub repo is available here.

Basic Chat Features

With PubNub ChatEngine, we’ll relieve the headaches of building a chat app. All the primary chat features that you can imagine are available out of the box – from showing online users to group chat to message history, everything is baked into the framework.

For demo purposes, we’ll use the chat app that can be built in 4 steps (including UI), in our ChatEngine Quickstart. It’s essential that you configure ChatEngine through this quick start guide.

PubNub Functions drive the backend of the chat app. By default, a Function is created for handling all the usual chat features of the basic chat app. Yet, you will need an additional Function to handle the language translation feature. Let’s head over to the next section to learn more about that.

Language Translation

For this multilingual chat app, we have two predefined users, John and Peter. Let’s look at the enhancements that we need to achieve for multilingual capability.

First, we need a slightly modified chat UI that can provide the language selection option.

You can see that the chat UI has a drop down selection option to choose the preferred language.

Note: As the time of this writing, the Amazon Translate service can only translate text to and from English. Keeping this limitation in mind, we have considered the user John as an English speaking user and Peter as the multilingual user.

Now comes the real magic of translation. Amazon Translate provides a REST API interface to perform language translation. For enabling translation, you will need to call this API via an AWS IAM user. You also need to set up a PubNub Function that intercepts the chat message and calls the API to get the translated message. In this way, all messages sent by John are translated into the target language as selected by Peter.

Here is how the Amazon Translate API is called to translate the chat message:

//Handle chat message translation
    if (payload.translate.user){

      return vault.get('AWS_access_key').then((AWS_access_key) => {
            return vault.get('AWS_secret_key').then((AWS_secret_key) => {
                  return db.get(payload.translate.user).then((langPref)=>{

                        let translate = payload.translate;

                        //Setup translation options;
                        let opts = {
                            path: '/',
                            service: 'translate',
                            region: 'us-east-2',
                            headers: {
                                'Content-Type': 'application/x-amz-json-1.1',
                                'X-Amz-Target': 'AWSShineFrontendService_20170701.TranslateText'
                            },
                            host: 'translate.us-east-2.amazonaws.com',

                            body: JSON.stringify({
                                "Text": translate.source_text,
                                "SourceLanguageCode": "en",
                                "TargetLanguageCode": peterLang
                            })

                        }

                        //Set translation rule for messages to peter 
                        if(payload.translate.user == "peter"){
                          opts.body = JSON.stringify({
                                "Text": translate.source_text,
                                "SourceLanguageCode": "en",
                                "TargetLanguageCode": langPref
                            });
                        } 
                        //Set translation rule for messages to john
                        else if(payload.translate.user == "john"){
                          opts.body = JSON.stringify({
                                "Text": translate.source_text,
                                "SourceLanguageCode": "auto",
                                "TargetLanguageCode": langPref
                            });
                        }

                        //Call AWS Translate API
                        signAWS(opts, { accessKeyId: AWS_access_key, secretAccessKey: AWS_secret_key });
                        const http_options = { 'method': 'POST', 'body': opts.body, 'headers': opts.headers };

                        return xhr.fetch('https://' + opts.host, http_options).then((response) => {
                            const body = JSON.parse(response.body);

                            if (body.TranslatedText) {
                               request.message.data.target_text = body.TranslatedText;
                               request.message.data.source_text = translate.source_text;
                            } else {
                                translate.error = body['Message'];
                            }

                            return request.ok();
                        }).catch((error) => {
                            console.log('Translation Error:', error);
                        });
                    });
              });
        });

    }    

There is one missing piece in this entire translation feature – how will the PubNub function know about the current language selection of Peter? The trick lies on sending custom message to the Function to indicate the change of language by the user.

//Send 'userPref' signal to indicate language preference
function setLanguagePref(userPref){

      homeChat.emit('userPref',{
        text:userPref,
        message:{
          userName:"peter",
          userLang: userPref
        }

      })

   };

The emit( ) function, part of the ChatEngine API, sends custom messages to PubNub Function. This function is wrapped in setLanguagePref( ) which gets invoked every time Peter selects a new language from the drop-down.

At the PubNub Function’s end, this is handled by storing the language preference in a KV store.

//Handle change in language preference 
    if (payload.message) {
        let userPref = payload.message;
        if(userPref.userName == "peter"){
             peterLang = userPref.userLang;
             db.set(peter,peterLang);
                console.log("New language preference for peter is ",peterLang);
            }

        //Language for John always set to English
        //Note: THIS IS AN AWS Translate LIMITATION
        db.set(john,johnLang);
    }

This way your PubNub Function can handle change in language selection.

App Deployment and Testing

So you have the three components that make up the multilingual chat app:

You’re all set now.

Follow the deployment and testing steps in the README and launch the user John and user Peter in two separate browser windows. However, make sure to first set up the PubNub keys as per the app you created under your PubNub account.

Initially, Peter’s app is configured in English. So all chat messages are received as is without translation. But once Peter selects a different language, all chat messages are translated.

Perfect! Now the language selection and translation is working well together.

Further Development

If you are contemplating building a similar app for your needs, then you have a few options to extend this demo app.

Check out the entire ChatEngine documentation to know more about the features and options. Most importantly, you would want to have user join the chat dynamically. You can also enable instant chat notifications such as typing indicators and read receipts, all thanks to the real-time capabilities of the PubNub Data Stream Network.

Try PubNub Today