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

#はじめに
先日LTをする機会がありまして、この記事の題通り、翻訳こんにゃくを1時間で作れる話をしてきました。
主題としては、「1時間で作れること」がメインなのですが、技術的な部分をもう少し聞きたいという声がありましたので、ここにざっくりとまとめます。詳しくはリポジトリを眺めてください。

作ったものはこちら 
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">翻訳こんにゃくなんて1時間くらいで作れるもんなんやで。<a href="https://twitter.com/hashtag/%E3%83%89%E3%83%A9%E3%81%88%E3%82%82%E3%82%93?src=hash&amp;ref_src=twsrc%5Etfw">#ドラえもん</a> <a href="https://twitter.com/hashtag/%E7%BF%BB%E8%A8%B3%E3%81%93%E3%82%93%E3%81%AB%E3%82%83%E3%81%8F?src=hash&amp;ref_src=twsrc%5Etfw">#翻訳こんにゃく</a> <a href="https://twitter.com/hashtag/javascript?src=hash&amp;ref_src=twsrc%5Etfw">#javascript</a> <a href="https://t.co/mNPSsxIpAC">pic.twitter.com/mNPSsxIpAC</a></p>&mdash; たいら (@fmstiara) <a href="https://twitter.com/fmstiara/status/1141284860197138432?ref_src=twsrc%5Etfw">2019年6月19日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

コードに関してはこちらを→[Githubリポジトリ](https://github.com/fmstiara/HonyakuKonnyaku)

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

ここでは、翻訳こんにゃくをWebアプリケーションとして実装します。
フローはこんな感じ。
![名称未設定.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131386/69a4b533-4e80-2d0e-03d6-8fcacc098037.jpeg)

#環境
クライアントサイドは、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
```JavaScript
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では、認識の途中結果や、連続で認識し続けるなども可能です。

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

詳細は[こちら](https://developer.mozilla.org/ja/docs/Web/API/SpeechRecognition)をどうぞ

##Speech Synthesis
```JavaScript
const synth = window.SpeechSynthesis;
const utterance = new SpeechSynthesisUtterance("Hello world");
synth.speak(utterance);
```
音声合成をするには、発話情報(`SpeechSynthesisUtterance`)を`SpeechSynthesis`の`speak()`に渡してやるだけでOKです。これだけで、ブラウザが"Hello world"と喋ってくれます。

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

```JavaScript
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種類。
<img width="1625" alt="スクリーンショット 2019-06-19 17.57.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131386/687db047-620c-6cb1-42b4-a2ac6f1414a3.png">


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

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

基本、[こちらの公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/cognitive-services/translator/quickstart-nodejs-translate)をなぞるだけで、十分です。

```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サーバ用の機能を含んでいます。

```HTML
<!-- クライアント側 -->
<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でしました。新しい技術はどんどん触って、技術の引き出しを増やしていきたいですね。