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