Posted at

翻訳こんにゃくなんて1時間で作れる話


はじめに

先日LTをする機会がありまして、この記事の題通り、翻訳こんにゃくを1時間で作れる話をしてきました。

主題としては、「1時間で作れること」がメインなのですが、技術的な部分をもう少し聞きたいという声がありましたので、ここにざっくりとまとめます。詳しくはリポジトリを眺めてください。

作ったものはこちら


コードに関してはこちらを→Githubリポジトリ


翻訳こんにゃくの仕様

翻訳こんにゃくといえば、ドラ○もんのひ○つ道具として有名です。それを食べることで、あらゆる言語を理解することができます。自分の知らない言語を話すことができ、聞き取って理解することができるという代物です。

ここでは、翻訳こんにゃくをWebアプリケーションとして実装します。

フローはこんな感じ。


環境

クライアントサイドは、JQueryやReactは特に使わず、生のJSで記述します。

サーバサイドは、Node.js(v8.9.4)で記述します。

音声認識・音声合成にはWebSpeechAPIに含まれる、SpeechRecognitionとSpeechSynthesisを使います。

翻訳には、Microsoftが提供するTranslatorTextAPIを使います。

また、クライアントとサーバ間をWebSocketで通信します。

以降で、WebSpeechAPI、TranslatorTextAPI、WebSocket通信について、翻訳こんにゃくを1時間で作るのに十分な内容だけ説明します。


Web Speech API

Web Speech APIは、音声認識のSpeech Recognitionインタフェースと、音声合成のSpeech Synthesisインタフェースから構成されるAPIです。インターネット接続せずにローカルな環境で動作し、簡単な記述で音声認識と音声合成を試すことができます。1時間で翻訳こんにゃくを作る上では、最適です。


Speech Recognition

const recognition = new webkitSpeechRecognition;

recognition.lang='en-US';
recognition.start();

recognition.onresult = function(e){
console.log(e.results[0][0].transcript);
}

SpeechRecognitionを試すだけなら、これだけで動きます。

インスタンスを生成して、認識する言語を設定し、start()を実行するだけです。

onresultイベントで認識結果を取得できます。

SpeechRecognitionでは、認識の途中結果や、連続で認識し続けるなども可能です。

const recognition = new webkitSpeechRecognition;

recognition.continuous = true; //連続認識
recognition.interimResults = true; //途中結果を吐き出す

詳細はこちらをどうぞ


Speech Synthesis

const synth = window.SpeechSynthesis;

const utterance = new SpeechSynthesisUtterance("Hello world");
synth.speak(utterance);

音声合成をするには、発話情報(SpeechSynthesisUtterance)をSpeechSynthesisspeak()に渡してやるだけでOKです。これだけで、ブラウザが"Hello world"と喋ってくれます。

SpeechSynthesisUtteranceをいじることで、声色や声の高さ、速さなどを調整できます。

const synth = window.SpeechSynthesis;

const voices = synth.getVoices();

let utterance = new SpeechSynthesisUtterance();
utterance.lang = 'ja-JP';
utterance.text = 'こんにちは';
utterance.pitch = 1.5; //高さ 0.0~2.0 default:1
utterance.rate = 1.0; //速さ 0.1~10.0 default:1
utterance.voice = voices[0]; //声色

声色はたくさんあります。2019/06/19時点で66種類。


Translator Text API

MicrosoftのAPIを選択した理由は特になく。Web Speech APIのように、インターネット接続せずにローカルで動き、簡単な記述で済むものがあればよかったのですが、特に見つからない(というより、探してない)ので、使った次第です。

このAPIはPOSTしたテキストの言語を自動で推定して、指定した言語に翻訳してくれます。

POSTで送るものは、翻訳先の言語と翻訳したい元のテキストです。

基本、こちらの公式ドキュメントをなぞるだけで、十分です。


Node.js

const request = require('request');

const uuidv4 = require('uuid/v4');
function translate(_text){
return new Promise((resolve, reject)=>{
const subscriptionKey = 'xxxxxxxxxxxxxxxxxxxxxxxx';
let options = {
method: 'POST',
baseUrl: 'https://api.cognitive.microsofttranslator.com/',
url: 'translate',
qs: {
'api-version': '3.0',
'to': 'ja' // ここで翻訳先の言語を指定
},
headers: {
'Ocp-Apim-Subscription-Key': subscriptionKey,
'Content-type': 'application/json',
'X-ClientTraceId': uuidv4().toString()
},
body: [{
'text': _text // 翻訳したいテキスト。今回で言えば、音声認識したテキスト。
}],
json: true,
};

request(options, function(err, res, body){
if(err) {
console.log(err);
return
}
let json = JSON.stringify(body, null, 4);
let data = JSON.parse(json);
resolve(data[0]["translations"][0]["text"]);

});
})
}



WebSocket

今回作ったシステムでは、クライアントとサーバ間をWebSocketで通信しているので、その話もついでにしましょう。

WebSocketはウェブアプリケーションにおいて、クライアントとサーバの低コストに双方向通信を簡単に実現するための通信規格です。チャットでクライアントとサーバの間を何度も通信するときに使ったりします。今回は、音声認識するたびにサーバに送って、翻訳したテキストをクライアントに返すという通信をしたいので、WebSocketを使おうということです。

Node.jsではsocket.ioというライブラリを使うことで、Webアプリケーションに簡単にWebSocketを組み込むことができます。npmやyarnを使ってsocket.ioをインストールしましょう。


socket.io

socket.ioはイベントベースでWebSocket通信を可能にするライブラリです。クライアントとNode.jsサーバ用の機能を含んでいます。

<!-- クライアント側 -->

<script>
//これだけでサーバにWebSocketで接続できます。
const socket = io();

// 'from-client'というイベント名で'Hello'というメッセージを送ります。
socket.emit('from-client', 'Hello');

// 'from-server'というイベント名でメッセージが来たら、それをconsole出力します。
socket.on('from-server', msg=>{console.log(msg)});
</script>


Node.js

//サーバ構築にはexpressを使います。

const app = require('express')();
const http = require('http').Server(app);
const server = http.listen(3000, ()=>{});

const io = require('socket.io')(http);

// 接続時の処理
io.on('connection', socket => {
// 'from-client'というイベント名で、clientからメッセージを受信
socket.on('from-client', msg => {
// 'from-server'というイベント名で、clientにメッセージを送信
io.emit('server:message', msg);
});
});



1時間で作る話

この辺の基本的な技術を組み合わせてしまえば、翻訳こんにゃく(みたいなもの)を作れてしまいます。

何も難しいものは使っていません。APIとか組み合わせれば、作れるものは無限大だぜ

という話をLTでしました。新しい技術はどんどん触って、技術の引き出しを増やしていきたいですね。