Build An Audio Translator With Vue.js, Yandex Translate API and Responsive Voice
Tags: vue, yandex, responsivevoicejs, api
Published: 2018-03-11 18:21:00

---
title: Build An Audio Translator With Vue.js, Yandex Translate API and Responsive Voice
published: true
description: Audio Translator With Vue, Yandex Translate API and ResponsiveVoiceJS
tags: Vue,Yandex,ResponsiveVoiceJS,API
---

## Introduction

A few weeks ago, I decided to put together a list of cool projects to do in Vue.js and for this reason, I started digging around the internet in search of valuable resources. At the end of the day, I had gathered significant chunks of knowledge on some useful APIs. Among the others, I found the [Yandex Translate API](https://translate.yandex.com/developers) really fascinating; it's able to take almost any text and return its equivalent in over 90 languages.

After playing around with the API ( using [Postman](https://www.getpostman.com/) ) for a bit, I decided to take my Vue.js knowledge and wrap it within the awesomeness of this API. The result? I hacked together a Vue.js translator web application.

During the project, I integrated audio functionally by referencing the [ResponsiveVoiceJS](https://responsivevoice.org/) [CDN](https://code.responsivevoice.org/responsivevoice.js) and firing a simple function.

Here's a visual display of the completed project :

![](https://cdn.scotch.io/40410/vYzxg2WJTQyeqLFzViph_reek.gif)

Let's see how I built it.

## Requirements

Since we are going to be building this application with the Progressive Javascript Framework [Vue.js](https://vuejs.org/), we need to be sure to have a few things installed before proceeding:

* [Node.js ](https://nodejs.org/)
* Node Package Manager ( [npm](https://www.npmjs.com/) )
* [Vue-CLI](https://github.com/vuejs/vue-cli) tool ( npm install -g vue-cli )

We also need:
* An API key to use the [Yandex Translate API](https://translate.yandex.com/developers/keys)

To create an account with Yandex, go [here](https://passport.yandex.com/registration?origin=translate&retpath=https%3A%2F%2Ftranslate.yandex.com%2Fdevelopers):

![](https://cdn.scotch.io/40410/pxLlRBnwQhe0MjirfPMb_screenshot-passport.yandex.com-2018-01-07-09-26-19.png)

To create a free API key, go [here](https://translate.yandex.com/developers/keys):

![](https://cdn.scotch.io/40410/zI9XUlrTBSDu28pfDj08_screenshot-translate.yandex.com-2018-01-06-22-55-24.png)


Once the dependencies are installed and you have a key, we are set to begin!

## Creating The Project

We'll be creating this project with the Vue-CLI tool, an alternative would be to reference Vue.js from a [CDN](https://unpkg.com/vue@2.5.13/dist/vue.js) and you can do that if you choose to.

We'll be using the `webpack-simple` template since this will not be overly complex to develop.

> You might need to change the ownership for the project directory and run some of these commands as root ( using `sudo` )

```BASH
vue init webpack-simple translator
```

With this command, you'll need to respond to a few lines of command prompt query:

![](https://cdn.scotch.io/40410/1Sj8i9KOQ5ua8FkWnuua_snapshot1.png)

Let's navigate to the project directory and install dependencies :

```BASH
cd translator
npm install
```

### Creating The Translate Component

We will create exactly one component for this project since we don't want to clutter up the development environment with unnecessary components. This component will contain two `textareas` positioned side by side; one would hold the text to be translated while the other displays the translated text, both sides will also have an `audio` functionality.

Let's create a `components` directory and a `translate` file in the `src` directory of our freshly installed project.

```BASH
mkdir -p src/components
cd src/components
touch translate.vue
```

This file will contain all the code for the `translate` component.

## Writing The Code For The Translate Component

Before we write any code at all, let's think of the relationship between the root component ( App.vue ) and the child component ( translate.vue ). Let's assume that the heavy lifting would be done by the root component, so the child component would receive props, register a few variables and `emit` events up to the root component on user interaction. 

Now that we understand how this is going to work, let's write some code in the `script` tag to receive props and initialize variables.

```JS

export default {

  name: 'translate',
 
  props: {

   AvailableLanguages : {
     type : Object
   },

   ResponseText : {
     type : String
   },

   CurrentLanguage : {
     type : String
   },

   TranslateLanguage : {
     type : String
   }
 },

  data () {
    return {
      Text: '',
      FromLanguage : "English",
      ToLanguage : "French"
    }
  }
}
```

Let's analyze the above code from top to bottom so we understand what is going on:

1.  First, we are exporting default, which is what would allow us to import this component into the root component.
2.  We initialize the `name` variable then receive and validate some props, apparently, these props would be coming from the root component. Here's what they are and what they do:

>* `AvailableLanguages` would hold an object of the available languages for translation returned by the Yandex API.
*  `ResponseText` would hold the translated text for a specified language returned by the Yandex API.
*  `CurrentLanguage` would be the code for the current language we are typing in.
*  `TranslateLanguage` would be the code for the language that the translation would come in.

3. Lastly, we initialize a `Text` ( this is for the text being typed ) variable to an empty string. We then initialize two other variables,  `FromLanguage` and  `ToLanguage`, to "English" and "French" respectively. These are the variables that would hold the full name ( not code ) of the selected language.

Awesome.

### Writing The MarkUp

Having received the `props` and initialized some variables in the code above, we should write the `HTML` now. We will mostly be binding the `textareas` to their matching data variables and triggering events upon user interaction. The `HTML`  will be written within the `template` tags so that Vue.js knows that this is `MarkUp` and renders it at runtime.

```HTML
<div id="parent_translate">

<div id="input">

<div id="imageholder">

<select v-model="FromLanguage" @change="changeFromLanguage">

<option v-for="(lang, code) in AvailableLanguages" :value="code"> {{ lang }} </option>
  
</select>

<img @click="translateSpeak" src="../assets/speaker.png">

</div>

<textarea placeholder="Type in something here" v-model="Text"></textarea>

<button @click="submit" id="submit">SUBMIT</button>
</div>

<div id="output">


<div id="imageholder">

<select v-model="ToLanguage" @change="changeToLanguage">

  <option v-for="(lang, code) in AvailableLanguages" :value="code"> {{ lang }} </option>

</select>

<img @click="responseSpeak" src="../assets/speaker.png">

</div>

<textarea placeholder="Your output goes here" disabled v-model="ResponseText"> </textarea>

</div>

</div>
```

Let's take a close look at the functionality bound to this MarkUp:

The first `select` tag is bound to the `FromLanguage` data variable using the `v-model` directive and it triggers an event handler "changeFromLanguage", whenever its value is changed.

The `option` tag nested within the first `select` tag accepts the object of available languages ( which will come as a property from the root component ) and loops through it using the `v-for` directive. It's `:value` directive is bound to the `code` variable so that each option tag created during the loop has it's own unique `code`.

Let's see some more of the MarkUp :

```HTML
<select v-model="FromLanguage" @change="changeFromLanguage">

<option v-for="(lang, code) in AvailableLanguages" :value="code"> {{ lang }} </option>
  
</select>
```

The `img` tag just below the previously discussed `select` tag holds an image representation for audio functionality and directs click events to a "translateSpeak" event handler.

```HTML
<img @click="translateSpeak" src="../assets/speaker.png">
```

The first `textarea` is quite straightforward with what it does, it binds the `Text` variable with itself so that it displays a placeholder only when there is no text written in it.


```HTML
<textarea placeholder="Type in something here" v-model="Text"></textarea>

<button @click="submit" id="submit">SUBMIT</button>
```

The `button` tag performs a single function; it triggers a `submit` method whenever it is clicked.

After this point in the `MarkUp`, the remaining code is just a repetition of what has already been displayed, no new logic or functionality.

Let's write the event handlers within the `methods` object.

### Writing The Event Handlers

Let's begin this section by writing the handlers for the select boxes, they are triggered whenever the selected option is `changed`.

```JS

  changeFromLanguage:function( ev ) {
    let LanguageCode = ev.target.value;
    this.$emit('updateFromLanguage', LanguageCode)
  },

  changeToLanguage:function( ev ) {
    let LanguageCode = ev.target.value;
    this.$emit('updateToLanguage', LanguageCode)
  }
  
```

The function of these two handlers is quite obvious. First, a new variable holds the language `code` for the selected option and then this update is `emitted` to the root component.  The handlers on the root component to handle these emitted events are also specified.

```Javascript
  submit:function( ev ){
    let Word = this.Text
    if ( Word ) {
    this.$emit('translate', Word)
    }
  }
```

The submit method above is called whenever the `Submit` button is clicked and its simple job is to  `emit` an event while passing the typed text to the parent component.  

Before we look into the event handlers for the audio functions, we have to pull in the `ResponsiveVoiceJS` library because we depend on it for the audio functions, we can do this by plugging in a `script` tag just before the closing `</body>` tag of the `index.html` file.


```HTML
<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://fonts.googleapis.com/css?family=Indie+Flower" rel="stylesheet">
    <title>A simple translator application using the Yandex API</title>
    <style>

    body{
      overflow-x: hidden;
      overflow-y: hidden; 
      background: linear-gradient( to right, DodgerBlue, lightblue);
    }

    </style>
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/build.js"></script>
    <script src='https://code.responsivevoice.org/responsivevoice.js'></script>
  </body>
</html>

```

To make the application speak, we require a single function from the `ResponsiveVoiceJS` library:

```JS
responsiveVoice.speak()
```

We can embed this function within our audio handlers and have something like this: 

```Javascript
  translateSpeak:function(){
    responsiveVoice.speak(this.Text);
  },

  responseSpeak:function(){
    responsiveVoice.speak(this.ResponseText);
  }
```

The `speak()` function takes in the text in either one of the two data variables and plays it.

### Inlcuding Styles

Lastly, let's include some scoped `Styles` to help things stay pretty:

```CSS
<style scoped>
#parent_translate {
  width: 100%;
  height: 100%;
  display: flex;
  flex-wrap: nowrap;
  flex-direction: row;
}
select {
  padding: 0.2em 0em;
  margin: 0;
  font-family: 'Indie Flower', cursive;
}
#submit {
background: #4ed34c;
padding: 0.7em;
border: none;
border-radius: 1px;
cursor: pointer;
color: black;
}
#input, #output {
  background-color: #f1f1f1;
  width: inherit;
  height: inherit;
  text-align: center;
  font-size: 30px;
  border-radius: 60px;
}
#input {
  margin: 0px 2px 0px 0px;
}
#output {
    margin: 0px 0px 0px 2px;  
}
textarea {
  width: inherit;
  height: 60%;
  margin: 0px;
  padding: 45px 20px;
  box-sizing: border-box;
  outline: none;
  border: none;
  text-align: center;
  font-size: 25px;
  font-family: 'Indie Flower', cursive;
  font-weight: bold;
}
#input > div, #output > div {
  width: inherit;
  height: 20%;
  margin: 0px;
  padding: 0px;
}
#input > div > img, #output > div > img {
 padding: 10px 0px 0px 0px;
 width: 12%;
 height: 60%;
 cursor: pointer;
}
</style>
```

That's really everything for the `translate` component, things are about to get functional and way more interesting.

## Building The Root Component

Before we start writing code for this component, I want us to quickly explore the [Yandex Translate API](https://translate.yandex.com/developers) and retrieve a [free key](https://translate.yandex.com/developers/keys) if you haven't already done that because it's essential to the further parts of this project.

After [creating an account](https://passport.yandex.com/registration?origin=translate&retpath=https%3A%2F%2Ftranslate.yandex.com%2Fdevelopers) with Yandex :

![](https://cdn.scotch.io/40410/pxLlRBnwQhe0MjirfPMb_screenshot-passport.yandex.com-2018-01-07-09-26-19.png)

You will be able to create a [free key](https://translate.yandex.com/developers/keys) :

![](https://cdn.scotch.io/40410/zI9XUlrTBSDu28pfDj08_screenshot-translate.yandex.com-2018-01-06-22-55-24.png)


### Writing The MarkUp

The root component doesn't need so much `MarkUp` so we will only write a few lines in which we would plug in the translate component as a custom tag :

```HTML
<div id="app">

<h2>VUE AUDIO TRANSLATOR </h2>

<div id="translate">

<translate v-bind:AvailableLanguages="AvailableLanguages" v-bind:TranslateText="TranslateText" v-bind:ResponseText="ResponseText"  @updateFromLanguage="updateFromLanguage($event)" @updateToLanguage="updateToLanguage($event)" @translate="handleTranslation($event)">
</translate>

</div>

</div>
```

There are quite two noteworthy activities going on on the `<translate>` custom tag, let's look at them:

1.  `AvailableLanguages`, `TranslateText` and `ResponseText` are passed down as props.
2.  `@updateFromLanguage`, `@updateToLanguage` and `@translate` are received as emitted events by the root component from the translate component.

### Including Styles

Let's include some styles to help the UI look good:

```CSS
<style scoped>
#translate {
  height: 45vh;
  max-width: 90%;
  margin: 2.5vh auto;
}
h2 {
  text-align: center;
  font-size: 1.6rem;
  color: black;
  font-weight: bold;
  font-family: 'Indie Flower', cursive;
}
</style>
```

At this point, we can run the application with this command:

```BASH
npm run dev
```

![](https://cdn.scotch.io/40410/GrrD2zoRjey9qMhW8iJg_Peek%202018-01-10%2015-18.gif)

The output shows that the UI of both components have been done but when the dropdown is clicked, it is not populated with languages. This is because we have not written the scripts to drive this functionality.

Let's do that now.

### Writing The Javascript

Let's do three things in this component: 

1. Import the translate component.
2. Register the component
3. Initialize variables

```JS 
import translate from './components/translate.vue'

export default {
  name: 'app',

  components: {
    translate : translate
  },

  data () {
    return {
      TranslateText : '',
      ResponseText : '',
      CurrentLanguage : 'en',
      TranslateLanguage : 'fr',
      AvailableLanguages : {}
    }
  }
  
  }
```

### Hooking Into The Created() Lifecycle Method

Vue.js allows us to hook into different [component lifecycle methods](https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks) and do different things to enhance the flexibility of the application. For this application, we want to send a `GET` request to the Yandex Translate API when the application is created.  Since Yandex Translate API allows us to make an `HTTP request` for a JSON payload of the supported languages alongside their codes, we will send a request whenever the application is created.

We need an `HTTP client` to send this request so let's get one, while there are a number of competent alternatives ( vue-resource, Fetch API etc ), we’ll be using the Promise based `HTTP client` [axios](https://www.npmjs.com/package/axios).

Let's pull in `axios` and a small wrapper for integrating `axios` to `Vue.js` :

```BASH
npm install --save axios vue-axios
```

To actually use `axios` and `vue-axios`, we need to import and use them into the `src/main.js` file : 

```JS
import Vue from 'vue'
import App from './App.vue'

import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)

new Vue({
  el: '#app',
  render: h => h(App)
})
```

Now that we have set up `axios`, we can request for the object of supported languages and their codes on every creation of the application:

```Javascript
 created(){

  var self = this;

 this.axios.get('https://translate.yandex.net/api/v1.5/tr.json/getLangs?ui=en&key=YOUR_KEY_GOES_HERE')
  .then(function (response) {
    self.AvailableLanguages = response.data.langs;
  })
  .catch(function (error) {
    console.log(error);
  })

  }
```

After querying the  Yandex Translate API, the `response` is asynchronously passed back as a Promise, we dig a bit into the `response` and update the value of the `AvailableLanguages` object.

### Writing The Translate And ValidateResponse Methods

This is the central theme of this entire article because this is where the actual translation of texts and validation of requests happen. We would be doing three simple things here:

1. Making a request to the Yandex Translate API with our unique key and the text to be translated.
2. Checking the response to confirm whether the text was translated properly or if an error was returned.
3. Updating the value of the `ResponseText` data variable if the response code is `200` or logging an error string to the console when it's not.

```JS
translate:function(){

  var self = this;

 this.axios.get('https://translate.yandex.net/api/v1.5/tr.json/translate?key=YOUR_KEY_GOES_HERE&text='+self.TranslateText+'&lang='+self.CurrentLanguage+'-'+self.TranslateLanguage+'&format=plain')
  .then(function (response) {

  self.validateResponse(response);
  })
  .catch(function (error) {
    console.log(error);
  })

  },

  validateResponse:function(response){

        if (response.status === 422) {

            console.log('Could Not Translate Text Please Try Again Later');

        } else if (response.status === 413) {

            console.log('Please Enter A Shorter Text to translate');

        } else if (response.status === 200) { 

           this.ResponseText = response.data.text[0]

        } else {
          
          console.log('Cannot translate at this moment please try again later');
        }
    }
```

That is pretty much the entire function of these methods, let's see how the root component handles the emitted events from the `translate` component.

### Writing The Event Handlers

From the earlier section above, we know that the root component has to handle three events emitted by the translate component:

>  * `@updateFromLanguage`, `@updateToLanguage` and `@translate` 

Let's write the methods to handle these respectively:

```JS
  updateFromLanguage:function( value ){
    this.CurrentLanguage = value;
    this.translate();
```

This handler receives a value from the translate component and updates the value of the `CurrentLanguage` variable with it. It then calls the `translate()` method to complete its job.

This is the method that is triggered on the root component whenever a new language ( the language to be translated from ) is selected. 

The reason it calls on the translate method is so that a user can get instant updates on translations as they toggle between languages, without having to hit the submit button every time.

```JS
  updateToLanguage:function( value ){
    this.TranslateLanguage = value;
    this.translate();
  }
```

This method does nearly the same thing as the one above, the only difference is that it works on the selection of the language being translated to, rather than the language being translated from.

Lastly, behold the method for handling click events to the submit button of the translate component:

```JS
  handleTranslation:function( value ){
    this.TranslateText = value;
    this.translate();
  }
  ```
  
  This handler simply updates the `TranslateText` variable with the value of the parameter received from the translate component, then it translates the text by calling the `translate()` method.
  
### Running The Application
  
  We can run the application with this command :
  
  ```BASH
  npm run dev
  ```
  
  This should open a new window in the browser at the development address `localhost:8080` :
  
  ![](https://cdn.scotch.io/40410/vYzxg2WJTQyeqLFzViph_reek.gif)
  
  That is all! We have just built a functional audio translator application and it's ready for testing.
  
### Build The Application
  
  We can optionally build for production with this command:
  
  ```BASH
  npm run build
  ```
  
## Conclusion

In this tutorial, we have seen how to build a simple audio translation application in Vue.js. We also used a powerful HTTP client, `axios` to query the Yandex Translate API for data.

We can conclude that Vue.js is a very exciting Progressive Framework to build lots of awesome projects. 

The code for this project is available on [Github](https://github.com/Jordanirabor/Audio-Translator).