We're hiring
Products

Channels

Beams

Chatkit

DocsTutorialsSupportCareersPusher Blog
Sign InSign Up
Products

Channels

Build scalable, realtime features into your apps

Features Pricing

Beams

Send push notifications programmatically at scale

Pricing

Chatkit

Build chat into your app in hours, not days

Pricing
Developers

Docs

Read the docs to learn how to use our products

Channels Beams Chatkit

Tutorials

Explore our tutorials to build apps with Pusher products

Support

Reach out to our support team for help and advice

Status

Check on the status of any of our products

Products

Channels

Build scalable, realtime features into your apps

Features Pricing

Beams

Send push notifications programmatically at scale

Pricing

Chatkit

Build chat into your app in hours, not days

Pricing
Developers

Docs

Read the docs to learn how to use our products

Channels Beams Chatkit

Tutorials

Explore our tutorials to build apps with Pusher products

Support

Reach out to our support team for help and advice

Status

Check on the status of any of our products

Sign InSign Up

Build a multilingual chat app with React and Amazon Translate

  • Ayooluwa Isaiah
April 8th, 2019
You will need Node 8+ installed on your machine.

Building language translation features in your app provides several opportunities for expansion to different regions of the world and there are several valid reasons why you might want to translate chat messages in realtime between users who don’t speak the same language.

An example is if a customer reaches out to you by chat in French, but you don’t speak the language. It is possible to translate the message in realtime to your preferred language so that you can understand the query and provide a prompt answer.

At the end of this tutorial, you’ll have a chat application where users can select their preferred language, and have messages sent to them translated to that language in realtime.

This tutorial builds on the How to send direct messages with Chatkit one. If you’re new to Chatkit, you should take the time to complete it before you move on to this one. Otherwise, you can just clone this GitHub repository and follow the instructions in the README.md file to get set up.

Prerequisites

  • Node.js (version 8 or later) and npm. Installation instructions can be found here.
  • Previous experience with building React and Node.js application.

Update the application styles

To account for the features we’ll be building in this tutorial, I’ve made a few changes to the application CSS. Copy the styles below and paste them into client/src/App.css.

    // client/src/App.css

    html {
      box-sizing: border-box;
    }

    *, *::before, *::after {
      box-sizing: inherit;
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;
    }

    h4 {
      padding-left: 20px;
      margin-bottom: 10px;
    }

    .App {
      width: 100vw;
      height: 100vh;
      display: grid;
      grid-template-columns: 1fr 4fr 1fr;
    }

    .right-sidebar {
      border-left: 1px solid #ccc;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }

    .left-sidebar {
      border-right: 1px solid #ccc;
    }

    .user-profile {
      height: 70px;
      display: flex;
      align-items: flex-start;
      padding-right: 20px;
      padding-left: 20px;
      justify-content: center;
      flex-direction: column;
      border-bottom: 1px solid #ccc;
    }

    .user-profile span {
      display: block;
    }

    .user-profile .username {
      font-size: 20px;
      font-weight: 700;
    }

    .chat-rooms li, .room-member {
      display: flex;
      align-items: center;
      padding: 15px 20px;
      font-size: 18px;
      color: #181919;
      cursor: pointer;
      border-bottom: 1px solid #eee;
      margin-bottom: 0;
    }

    .room-member {
      justify-content: space-between;
      padding: 0 20px;
      height: 60px;
    }

    .send-dm {
      opacity: 0;
      pointer-events: none;
      font-size: 20px;
      border: 1px solid #eee;
      border-radius: 5px;
      margin-bottom: 0;
      padding: 0 10px;
      line-height: 1.4;
      height: auto;
    }

    .room-member:hover .send-dm {
      opacity: 1;
      pointer-events: all;
    }

    .presence {
      display: inline-block;
      width: 10px;
      height: 10px;
      background-color: #ccc;
      margin-right: 10px;
      border-radius: 50%;
    }

    .presence.online {
      background-color: green;
    }

    .chat-rooms .active {
      background-color: #eee;
      color: #181919;
    }

    .chat-rooms li:hover {
      background-color: #D8D1D1;
    }

    .room-icon {
      display: inline-block;
      margin-right: 10px;
    }

    .chat-screen {
      display: flex;
      flex-direction: column;
      height: 100vh;
    }

    .chat-header {
      height: 70px;
      flex-shrink: 0;
      border-bottom: 1px solid #ccc;
      padding-left: 10px;
      padding-right: 20px;
      display: flex;
      flex-direction: column;
      justify-content: center;
    }

    .chat-header h3 {
      margin-bottom: 0;
      text-align: center;
    }

    .chat-messages {
      flex-grow: 1;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      margin-bottom: 0;
      min-height: min-content;
    }

    .message {
      padding-left: 20px;
      padding-right: 20px;
      margin-bottom: 10px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .message span {
      display: block;
      text-align: left;
    }

    .message .user-id {
      font-weight: bold;
    }

    .message-form {
      border-top: 1px solid #ccc;
    }

    .message-form, .message-input {
      width: 100%;
      margin-bottom: 0;
    }

    input[type="text"].message-input {
      height: 50px;
      border: none;
      padding-left: 20px;
    }

    .dialog-container {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.8);
      display: flex;
      justify-content:center;
      align-items: center;
    }

    .dialog {
      width: 500px;
      background-color: white;
      display: flex;
      align-items:  center;
    }

    .dialog-form {
      width: 100%;
      margin-bottom: 0;
      padding: 20px;
    }

    .dialog-form > * {
      display: block;
    }

    .username-label {
      text-align: left;
      font-size: 16px;
    }

    .username-input {
      width: 100%;
    }

    input[type="text"]:focus {
      border-color: #5C8436;
    }

    .submit-btn {
      color: #5C8436;
      background-color: #181919;
      width: 100%;
    }

    .submit-btn:hover {
      color: #5C8436;
      background-color: #222;
    }

    .language {
      margin-bottom: 0;
      height: 50px;
      border-radius: 0;
      border: none;
      border-top: 1px solid #ccc;
    }

Sign up for Amazon AWS

If you don’t already have an account, you can register for a free AWS account here, otherwise, you can log in to your existing account. Once you’ve completed the sign up process, you have to wait for your account to be activated. This can take up to 24 hours, but in my case it only took about an hour. You will receive an email confirming the activation of your account.

Log into your AWS account, click your username at the top right corner, then click My Security Credentials. Next, click Users on the sidebar. You will be redirected to a page where you can view, add or delete users on your account. Click the Add user button, assign a user name to the user, and tick Programmatic access under Access type.

Next, under Set permissions, click Attach existing policies directly and then select TranslateFullAccess under Filter policies.

You can skip the optional Tags step, and go ahead to create the user on step four. Once the user is created, you will be provided with the Access key ID and Secret access key. Copy the values and add them to your .env file as follows:

    // .env

    // [..]
    AWS_ACCESS_KEY_ID=<your access key id>
    AWS_SECRET_ACCESS_KEY=<your secret access key>

Update the server

We’ll create a new /translate endpoint on the server where messages will be sent to to get translated from a source language to a target language. Go ahead and install the aws-sdk by running the command below from the root of your project directory:

    npm install aws-sdk --save

Next, import the SDK and initialize the translate service as follows:

    // server.js

    // [..]
    const Chatkit = require('@pusher/chatkit-server');
    const AWS = require('aws-sdk');

    const app = express();

    const translate = new AWS.Translate({
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      region: 'us-east-2',
    });

    // [..]

Following that, create the /translate endpoint just before /users in server.js:

    // server.js

    app.post('/translate', (req, res) => {
      const { text, lang } = req.body;
      const params = {
        SourceLanguageCode: 'auto',
        TargetLanguageCode: lang,
        Text: text,
      };

      translate.translateText(params, (err, data) => {
        if (err) {
          return res.send(err);
        };

        res.json(data);
      });
    });

This endpoint expects the text to translate and the language that the text is to be translated to in the request body. As you can see, we’ve specified the value of SourceLanguageCode to be auto. This prompts the translate service to determine the source language of the text. You can also specify the language code for the language of the text. Keep in mind that the language must be a language supported by Amazon Translate.

Add a language picker on the frontend

Let’s make it possible for the user to select their preferred language in the user interface. We won’t be saving the language preference of the user in this tutorial, but don’t forget that you need to do so in your production application.

Update your client/src/App.js to look like this:

    // client/src/App.js

    import React, { Component } from 'react';
    import {
      // [..]
      updateLanguage,
    } from './methods';

    // [..]

    class App extends Component {
      constructor() {
        super();
        this.state = {
          // [..]
          language: 'en',
        };

        // [..]
        this.updateLanguage = updateLanguage.bind(this);
      }

      render() {
        const {
          // [..]
          language,
        } = this.state;

        return (
          <div className="App">
            <aside className="sidebar left-sidebar">
              // [..]
            </aside>
            <section className="chat-screen">
              // [..]
            </section>
            <aside className="sidebar right-sidebar">
              // [..]
              {currentRoom ? (
                <select
                  id="language"
                  className="language"
                  name="language"
                  value={language}
                  onChange={this.updateLanguage}
                >
                  <option value="en">English</option>
                  <option value="fr">French</option>
                  <option value="es">Spanish</option>
                  <option value="de">German</option>
                </select>
              ) : null}
            </aside>
            // [..]
          </div>
        );
      }
    }

    export default App;

Then add the updateLanguage function and export it from methods.js:

    // client/src/methods.js

    // [..]

    function updateLanguage(event) {
      const { value } = event.target;
      const { messages } = this.state;
      this.setState({
        language: value,
      });
    }

    export {
      // [..]
      updateLanguage,
    };

Language translation

Once a message is sent to the room, we need to translate it to the target language of the current user. To do this, we’ll add a new translate function in methods.js and use it as follows:

    // client/src/methods.js

    // [..]

    function connectToRoom(id = 19980438) {
      const { currentUser } = this.state;

      this.setState({
        messages: [],
      });

      return currentUser
        .subscribeToRoom({
          roomId: `${id}`,
          messageLimit: 100,
          hooks: {
            onMessage: message => {
              translateText.call(this, message);
            },
           // [..]
          },
        })
        .then(currentRoom => {
          // [..]
        })
        .catch(console.error);
    }


    function updateLanguage(event) {
      const { value } = event.target;
      const { messages } = this.state;
      this.setState(
        {
          language: value,
        },
        () => {
          messages.forEach(message => {
            translateText.call(this, message);
          });
        }
      );
    }

    function translateText(message) {
      const { language, messages } = this.state;
      const { text, id } = message;
      axios
        .post('http://localhost:5200/translate', {
          text,
          lang: language,
        })
        .then(response => {
          const index = messages.findIndex(item => item.id === id);
          const msg = {
            ...message,
            text: response.data.TranslatedText,
          };

          if (index !== -1) {
            messages.splice(index, 1, msg);
          } else {
            messages.push(msg);
          }

          this.setState({
            messages: messages.sort((a, b) => {
              return new Date(a.createdAt) - new Date(b.createdAt);
            }),
          });
        })
        .catch(console.error)
    }

    // [..]

Once a message is sent to the room, it is passed to the translate() method and sent to the server to be translated to the user’s preferred language and then then displayed on the page. Also, when a user changes their preferred language, all existing messages are translated accordingly.

Wrap up

In this tutorial, you’ve learnt how to translate messages in a chatroom in realtime using Chatkit and Amazon translate. You can extend this demo in many different ways depending on what your product does. Check out the full Chatkit documentation to know more about all the features and options available to you.

The source code used for this demo can be found in this GitHub repository.

Clone the project repository
  • Amazon Web Services
  • Chat
  • chatroom
  • JavaScript
  • Node.js
  • React
  • Social
  • Social Interactions
  • Chatkit

Products

  • Channels
  • Beams
  • Chatkit

© 2019 Pusher Ltd. All rights reserved.

Pusher Limited is a company registered in England and Wales (No. 07489873) whose registered office is at 160 Old Street, London, EC1V 9BW.