Creando un traductor de un idioma (Español) a otro idioma (Ingles).
Traductor tiempo real usando VUE
En esta entrada he creado un pequeño traductor en tiempo real usando node.js para el servidor, vue.js en la parte front y algo de Javascript para convertir la voz a texto (speech to text) y el texto a voz (text to speech). El código es simple pero los resultados son bastante curiosos.
index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
textarea {
resize: none;
}
</style>
</head>
<body>
<main v-cloak>
<select id="voiceSelect"></select><br>
<select id="lO">
<option v-for="(i, t, v) in langOrigen" v-bind="t">{{t}}</option>
</select>
<button v-on:click="listenOrigen()">Listen</button><br>
<textarea cols="30" rows="10" v-model="idiomaOrigen" v-on:keyup="translate()" resize="none"></textarea><br>
<select id="lD">
<option v-for="(i, t, v) in langDestino" v-bind="t">{{t}}</option>
</select>
<button v-on:click="listenDestino()">Listen</button><br>
<textarea cols="30" rows="10" v-text="idiomaDestino" resize="none" disabled></textarea><br>
<span v-text="info"></span><br>
<button id="start-record-btn" title="Start Recording" v-show="mostrar" v-on:click="startSpeech()">Iniciar reconocimiento voz a texto</button>
<button id="pause-record-btn" title="Pause Recording" v-show="!mostrar" v-on:click="stopSpeech()">Parar Iniciar reconocimiento voz a texto</button>
</main>
<script src="./vue.js"></script>
<script src="./httpVueLoader.js"></script>
<script src="./axios.min.js"></script>
<script src="./languages.js"></script>
<script src="./main.js"></script>
</body>
</html>
main.js
new Vue({
el: "main",
data: {
idiomaOrigen: null,
idiomaDestino: null,
info: "Reconocimiento de voz parada",
mostrar: true,
noteContent: "",
recognition: null,
lang: null,
langOrigen: null,
langDestino: null
},
created() {},
mounted() {
this.rellenaLang();
this.speechToText();
},
methods: {
rellenaLang: function() {
debugger;
this.langOrigen = langs;
this.langDestino = langs;
},
populateVoiceList: function() {
if (typeof speechSynthesis === 'undefined') {
return;
}
voices = speechSynthesis.getVoices();
for (i = 0; i < voices.length; i++) {
var option = document.createElement('option');
option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
if (voices[i].default) {
option.textContent += ' -- DEFAULT';
}
option.setAttribute('data-lang', voices[i].lang);
option.setAttribute('data-name', voices[i].name);
document.getElementById("voiceSelect").appendChild(option);
}
},
listenDestino: function() {
this.readOutLoud(this.idiomaDestino);
},
listenOrigen: function() {
this.readOutLoud(this.idiomaOrigen);
},
translate: function() {
debugger;
var _this = this;
if (_this.idiomaOrigen.length > 0) {
var lO = document.getElementById("lO")[document.getElementById("voiceSelect").selectedIndex].getAttribute("data-lang");
var lD = document.getElementById("lD")[document.getElementById("voiceSelect").selectedIndex].getAttribute("data-lang");
axios.get("http://localhost:3002/translate/" + _this.idiomaOrigen)
.then(function(respuesta) {
_this.idiomaDestino = respuesta.data;
})
.catch(function(error) {
console.log(error);
});
} else {
_this.idiomaDestino = null;
}
},
readOutLoud: function(message) {
this.lang = document.getElementById("voiceSelect")[document.getElementById("voiceSelect").selectedIndex].getAttribute("data-lang");
var speech = new SpeechSynthesisUtterance();
speech.text = message;
speech.volume = 1;
speech.rate = 1;
speech.pitch = 1;
speech.lang = this.lang;
window.speechSynthesis.speak(speech);
},
startSpeech: function() {
if (this.noteContent.length) {
this.noteContent += ' ';
}
this.recognition.start();
this.info = 'Reconocimiento de voz inciada';
this.mostrar = false;
},
stopSpeech: function() {
this.recognition.stop();
this.info = 'Reconocimiento de voz parada';
this.mostrar = true;
},
speechToText: function() {
var _this = this;
try {
var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
_this.recognition = new SpeechRecognition();
_this.populateVoiceList();
if (typeof speechSynthesis !== 'undefined' && speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = _this.populateVoiceList;
}
} catch (e) {
_this.info = "Error: " + e;
}
_this.recognition.continuous = true;
_this.recognition.onresult = function(event) {
var current = event.resultIndex;
var transcript = event.results[current][0].transcript;
var mobileRepeatBug = (current == 1 && transcript == event.results[0][0].transcript);
if (!mobileRepeatBug) {
_this.noteContent += transcript;
_this.idiomaOrigen = _this.noteContent;
_this.translate();
_this.readOutLoud(_this.noteContent);
}
};
_this.recognition.onstart = function() {
_this.info = 'Reconocimiento de voz activado. Prueba hablar al microfono.';
}
_this.recognition.onspeechend = function() {
_this.info = 'Debes estar en silencio para que el reconocimiento de voz se pare.';
}
_this.recognition.onerror = function(event) {
if (event.error == 'no-speech') {
_this.info = 'No se detecto a nadie hablando. Prueba de nuevo.';
};
}
}
}
});
server.js (nodeJS)
var express = require('express');
var app = express();
var translate = require('google-translate-api');
var port = 3002;
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/translate/:frase?', function(req, res) {
var _res = res;
translate(req.params.frase, { to: 'en' }).then(function(res) {
console.log(res.text);
_res.send(res.text);
}).catch(function(err) {
console.error(err);
});
});
app.get("/test/", function(req, res) {
res.send("Test Node.JS");
});
app.listen(port);
console.log('Listening on port ' + port);
Para poder realizar la magia que se crea en nodeJS he necesitado las siguientes dependencias:
- express: npm i express –save
- google-translate-api: npm i google-translate-api –save
