Build

Build an IBM Watson Language Translator Chat App

Michael Carroll on Jul 29, 2019
Build an IBM Watson Language Translator Chat App

IBM Watson has a variety of innovative APIs that allow you to build cognitive capabilities into your applications, powering language, speech, vision, and insights. Essentially, it upgrades your app in order to do some pretty amazing things with your structured and unstructured data.

IBM Watson's suite of APIs plays wonderfully with PubNub Data Streams in our BLOCKS Catalog, allowing you to build cognition into your real-time data streams, processing inputs on-the-fly, and returning an output based on the work of Watson's APIs.

In this tutorial we'll build a multilingual chat app and utilize the IBM Watson Language Translator BLOCK, which translates messages in near real-time from one language to another. With 11 different languages available, you've got a powerful, robust language translator to translate messages as they're sent and received.

Our result will look something like this:

diy multilingual chat

Getting Started with IBM Watson on Bluemix

You'll first need a PubNub account if you don't have one. From there, go to your PubNub Admin Dashboard for your unique publish and subscribe keys (you'll need them in a bit!).

The next thing you'll need is a Bluemix account to take advantage of the Watson APIs.

  • Step 3: Go to the service credentials pane of the service instance you just created and make note of the username and password.

Setting up the BLOCK

Next, we'll set up PubNub BLOCKS to get your code running in the network! This is where all the near real-time translation will happen.

Create Block
  • Step 2: Create a new BLOCK.
List of Blocks
  • Step 3: Paste in the BLOCK code from the next section and update the credentials with the Watson credentials from the previous steps above.
Translation Block
  • Step 4: Start the BLOCK, and test it using the “publish message” button and payload on the left-hand side of the screen.

That's all it takes to create your serverless code running in the cloud!

Diving into the Code – the BLOCK

You'll want to grab the 40 lines of BLOCK JavaScript and save them to a file called pubnub_watson_translate.js. It's available as a Gist on GitHub for your convenience.

First up, we declare our dependencies: console (for debugging), xhr (for HTTP requests), store (for Key-Value storage), query (for query string encoding), and auth (for HTTP Basic auth support).

const console = require('console');
const xhr = require('xhr');
const query = require('codec/query_string');
const base64 = require('codec/base64');
const pubnub = require('pubnub');

Next, we create a method to handle incoming messages, declare the credentials for accessing the Language Translator API, set up the URL for talking to the remote web service, and declare the target language as “es” (Spanish).

In your application, this could be a dynamic value.

export default (request) => {
    const username = '00000000-0000-0000-0000-000000000000';
    const password = '000000000000';
    const apiUrl = 'https://gateway.watsonplatform.net/language-translation/api/v2/translate';
    const lang = 'es';

Next, we set up the Language Translator API web service query parameters and Basic authentication header. Note that we're specifying English as the source language here. In your application, this could also be a dynamic value.

    const queryParams = {
        source: "en",
        target: lang,
        text: request.message.text_en
    };
    const httpOptions = {
        headers: {
            Authorization: 'Basic ' +  base64.btoa(username + ':' + password)
        }
    };

Finally, we make a remote service request to the Language Translator API, and add a message attribute with the translated text. We return request.ok() to pass on the modified message object.

    return xhr.fetch(apiUrl + '?' + query.stringify(queryParams), httpOptions)
        .then(r => {
            request.message['text_' + lang] = r.body;
            return request.ok();
        }, e => {
            console.log(e);
        })
        .catch((e) => {
            console.error(e);
        });
};

All in all, it doesn't take a lot of code to add text translation to your application.

Diving into the Code – the User Interface

You'll want to grab these 70 lines of HTML & JavaScript and save them to a file called pubnub_watson_translate.html.

The first thing you should do after saving the code is to replace two values in the JavaScript:

  • YOUR_PUB_KEY: with the PubNub publish key mentioned above.
  • YOUR_SUB_KEY: with the PubNub subscribe key mentioned above.

If you don't, the UI will not be able to communicate with anything and probably clutter your console log with entirely too many errors.

For your convenience, this code is also available as a Gist on GitHub, and a Codepen as well.

Dependencies

First up, we have the JavaScript code & CSS dependencies of our application.

<!doctype html>
<html>
<head>
  <script src="https://cdn.pubnub.com/pubnub-3.15.1.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
  <script src="https://cdn.pubnub.com/sdk/pubnub-angular/pubnub-angular-3.2.1.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
  <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" />
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" />
</head>
<body>

For folks who have done front-end implementation with AngularJS before, these should be the usual suspects:

  • PubNub JavaScript client: to connect to our data stream integration channel.
  • AngularJS: were you expecting a niftier front-end framework? Impossible!
  • PubNub Angular JavaScript client: provides PubNub services in AngularJS quite nicely indeed.
  • Underscore.js: we could avoid using Underscore.JS, but then our code would be less awesome.

In addition, we bring in 2 CSS features:

  • Bootstrap: in this app, we use it just for vanilla UI presentation.
  • Font-Awesome: we love Font Awesome because it lets us use truetype font characters instead of image-based icons. Pretty sweet!

Overall, we were pretty pleased that we could build a nifty UI with so few dependencies. And with that… on to the UI!

The User Interface

Here's what we intend the UI to look like:

Translation UI

The UI is pretty straightforward – everything is inside a div tag that is managed by a single controller that we'll set up in the AngularJS code.

<div class="container" ng-app="PubNubAngularApp" ng-controller="MySpeechCtrl">
<pre>
NOTE: make sure to update the PubNub keys below with your keys,
and ensure that the translate BLOCK is configured properly!
</pre>
  
<h3>MyText Translation</h3>

We provide a simple text input for a message to send to the PubNub channel as well as a button to perform the publish() action.

<input ng-model="toSend" />
<input type="button" ng-click="publish()" value="Send!" />

Our UI consists of a simple list of messages. We iterate over the messages in the controller scope using a trusty ng-repeat. Each message includes the original English text (provided that's what you publish) as well as the Spanish translation.

<ul>
  <li ng-repeat="message in messages track by $index">
    en: {{message.text_en}}
    <br />
    es: {{message.text_es}}
  </li>
</ul>
</div>

And that's it – a functioning real-time UI in just a handful of code (thanks, AngularJS)!

The AngularJS Code

Now we're ready to dive into the AngularJS code. It's not a ton of JavaScript, so this should hopefully be pretty straightforward.

The first lines we encounter set up our application (with a necessary dependency on the PubNub AngularJS service) and a single controller (which we dub MySpeechCtrl). Both of these values correspond to the ng-app and ng-controller attributes from the preceding UI code.

<script>
angular.module('PubNubAngularApp', ["pubnub.angular.service"])
.controller('MySpeechCtrl', function($rootScope, $scope, Pubnub) {

Next up, we initialize a bunch of values. First is an array of message objects which starts out empty. After that, we set up the msgChannel as the channel name where we will send and receive real-time structured data messages. NOTE: make sure this matches the channel specified by your BLOCK configuration!

  $scope.messages     = [];
  $scope.msgChannel   = 'pubnub-chat';

We initialize the Pubnub object with our PubNub publish and subscribe keys mentioned above, and set a scope variable to make sure the initialization only occurs once. NOTE: this uses the v3 API syntax.

  if (!$rootScope.initialized) {
    Pubnub.init({
      publish_key: 'YOUR_PUB_KEY',
      subscribe_key: 'YOUR_SUB_KEY',
      ssl:true
    });
    $rootScope.initialized = true;
  }

The next thing we'll need is a real-time message callback called msgCallback; it takes care of all the real-time messages we need to handle from PubNub. In our case, we have only one scenario – an incoming message containing text with its translation. The push() operation should be in a $scope.$apply() call so that AngularJS gets the idea that a change came in asynchronously.

  var msgCallback = function(payload) {
    $scope.$apply(function() {
      $scope.messages.push(payload);
    });
  };

The publish() function takes the contents of the text input, publishes it as a structured data object to the PubNub channel, and resets the text box to empty.

  $scope.publish = function() {
    Pubnub.publish({
      channel: $scope.msgChannel,
      message: {text_en:$scope.toSend}
    });
    $scope.toSend = "";
  };

In the main body of the controller, we subscribe() to the message channel (using the JavaScript v3 API syntax) and bind the events to the callback function we just created.

Pubnub.subscribe({ channel: [$scope.msgChannel], message: msgCallback });

We mustn't forget close out the HTML tags accordingly.

});
</script>
</body>
</html>

Not too shabby for about seventy lines of HTML & JavaScript!

Additional Features

There are a couple other features worth mentioning in the Language Translator API.

const queryParams = {
    source: "en",
    target: lang,
    text: request.message.text_en
};

You can find detailed API documentation here.

  • accept is the MIME type of the return format you'd like to receive: text or JSON.
  • model_id is the translation model you'd like to use: if you have a specific domain you'd like to train the service, that's possible using a training API!

All in all, we found it pretty easy to get started with language translation using the API!

Beyond chat, there are a number of great fits for the Language Translator API from IBM Watson, including:

  • Content accessibility for users from different cultures
  • Near real-time augmented reality text translation
  • Integration with text-to-speech services
  • Translation to a common “hub” language

And also check out our other IBM Watson BLOCKS, and see how you can make your data streams even smarter.