TOIEC頻出フレーズ＋読み上げ機能（WebSpeechAPI）による”英語学習Webアプリ”を作った

# 背景
　前回記事の[Google Apps ScriptによるクイズWebアプリを作って英語学習する](https://qiita.com/Suibari_cha/items/87503c55d984ff0f43e6)のアプリを自分で使っていて、以下の機能があると、なお良いと思った。

* **答えの読み上げ機能**（英単語の場合、iPhoneだと長タッチで辞典開いて発音記号を見れるが、その時間を短縮する）
* 問題番号を表示する機能（問題が本の何頁にあるのか、逆引きするのにひと手間かかる）
* これまでの出題回数を表示する機能（問題数が増えてくると、過去出題された累計が気になる）
* **難しい問題のチェック機能**（問題のチェックはスプレッドシートにアクセスすれば可能だが、アプリからワンクリックでチェックできるようにしたい）

　以前の記事では、目標をクイズWebアプリ作成として、比較的汎用的なアプリとしてリファクタリング・解説しました。
　

　ですが、上で述べた要求機能がどんどん出てきて、**このアプリを、より自身の英語学習目的に特化したアプリにしたい**という目標ができました。
　本記事以降、本アプリの名前を、TOEIC金フレを更に学ぶための**"英語学習Webアプリ"**と改めます。

# 目的
　以下4つの機能追加の結果報告・苦労した点の情報共有を、本記事の目的とします。
　また今後、**GASによる英語学習Webアプリをコーディングされる方のために、この資料が参考になればと思います。**

1. 答え表示時、**Web Speech APIを用いた英単語の読み上げ機能追加**する。
2. 出題時、問題番号を表示する機能追加する。
3. 出題時、これまでの出題回数を表示する機能を追加する。（[前回の記事](https://qiita.com/Suibari_cha/items/87503c55d984ff0f43e6)の通り、最大出題回数を満たした問題は出題されない）
4. 一定回数以上出題された問題の答え表示時、**「問題チェック」ボタンを表示し、押下でスプレッドシートを着色する機能追加**する。（チェックされた問題は、3の条件を満たした問題であっても出題される）

# 結論
　**できました！**
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">PC実行風景<br>想像より流暢に読み上げてくれる<br>（個人的にはiOS safariの声の方が好み） <a href="https://t.co/i4uxcpsS9H">pic.twitter.com/i4uxcpsS9H</a></p>&mdash; すいばり@&#39;18年度1戦0勝1敗 (@Suibari_cha) <a href="https://twitter.com/Suibari_cha/status/993438566326796295?ref_src=twsrc%5Etfw">2018年5月7日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 内容
　全体コードについては、GitHubを参照ください！(2020/8/15追記)
[GiHub: suibari/QuizVocabulary](https://github.com/suibari/QuizVocabulary)

　以下、目的で述べた**Web Speech API利用**および**問題チェック機能**など、各モジュールについて解説します。

## -1. Web Speech API
　**ブラウザのテキスト読み上げ機能**を使っています。
　よって、**動作可否や性能（声質）などはブラウザに依存**しますが、私の環境（"Win10 Chrome"および"iOS11.3 Safari"）では、十分流暢に発音してくれると感じました。
　たった数文でこんな手軽に喋らせられることに感動。

```javascript:ブラウザ側スクリプト
function speechTextInLang (text, lang) {
     
  var speech   = new SpeechSynthesisUtterance();
  var voices   = window.speechSynthesis.getVoices();
        
  speech.text  = text;
  speech.lang  = lang;
  // 声質の設定
  for (var i = 0; i < voices.length; i++) { 
    if (voices[i].lang == lang) speech.voice = voices[i];
  }
  
  // 喋らせる
  speechSynthesis.speak(speech);
};
```

### -1.1 声質の設定について
　声質の設定は以下部分で行っています。
　アルゴリズムについては後述します。**このアルゴリズムがミソだと思ってます**。

```javascript
var speech   = new SpeechSynthesisUtterance();     // インスタンス生成
var voices   = window.speechSynthesis.getVoices(); // ブラウザから声質配列voicesを取得

// voicesを掃引し、langプロパティが引数lang("US-en")と等しい声を選び、
// speech.voiceプロパティにセットする
for (var i = 0; i < voices.length; i++) { 
    if (voices[i].lang == lang) speech.voice = voices[i];
}
```

　声質の配列`voices`は、ブラウザ毎に約30前後実装されており、開発者が任意の声質を選択して発声させる仕組みです。
　なので、**プリセットの声質（日本語）のまま英語を喋らせてみたらひどい棒読みだった**というのは有り得ます。
　しかも、`voices`は[ブラウザごとにプロパティの名前が全く異なる](https://app.codegrid.net/entry/2016-web-speech-api-1#toc-1)ので、マルチデバイスを意識する場合、**アプリ側で、`voices`を識別をしてから発声させなければならない**といえます。
　参考文献より、以下引用します。

> 声質の種類を指定するには、uttrインスタンスの`voice`属性に`voice`オブジェクトをセットする必要があります。`voice`オブジェクトは、`speechSynthesis.getVoices()`を実行することで、利用環境で使用可能な`voice`オブジェクトを配列形式でまとめて取得することができます。
> *参考文献： [Web Speech APIの実装 いろいろな種類の声質で再生する](https://app.codegrid.net/entry/2016-web-speech-api-1#toc-1) より*

　したがって、上のコードでは`speechSynthesis.getVoices()`で得た声質配列`voices`のうち、`"US-en"`の発音に向いたvoiceを、配列をなめて選択しています。

## -2. 問題チェック機能
　「問題チェック」ボタンの押下時、スプレッドシートの該当する問題を着色します。
　コードは以下の通り。

```html:ブラウザ側スクリプト
<script>
function onHighlightBtnClick() {
  // html読み取り
  var answer  = document.getElementById("answer");
  var btn_hlt = document.getElementById("btn_hlt");
      
  google.script.run.highlightSpecificQuiz(answer.innerHTML);
  btn_hlt.innerHTML = "チェック済み";
}
</script>
```

```javascript:サーバ側スクリプト
function highlightSpecificQuiz(ans) {

  // スプレッドシート処理
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('List');
  
  // 行番号探索
  var r = searchRowInColumnByString(ans, "D");
  
  // 問題番号セルを着色
  sheet.getRange("A" + r).setBackground('#ffff00');
}
```

# 考察
* 前回の記事同様、隙間時間での英語頻出フレーズ学習という点で、高い学習効果があると思っています。**既に500フレーズ近く覚えられました。**おすすめです！
* このように、**自分の学習目的**（開発以外）**で何らかのアプリを開発する場合、そのアプリのデバッグ・検証を行っていく過程で、（いやでも）本来目的の学習が進んでいく**というメリットがあることに気づきました。
* ちなみに、肝心のTOEICは**7月下旬**受験します。6月まで金フレ、7月は公式問題集の予定です。結果はTwiiterで。
* [テキスト読み上げ関数](#-1-web-speech-api)を見ていただくと分かるように、呼び出されるたびに`SpeechSynthesisUtteranceのインスタンス`を生成してます。**本来、HTML読み込み時に一度生成するだけで良い**と思ってます。処理が遅くなるわけでもないので、ほったらかしにしてます。うまいやり方あるのかしら。

# 参考文献
1. [Web Speech Synthesis API（音声合成API）で遊んでみた](https://qiita.com/kyota/items/da530ad22733b644518a)
2. [Web Speech APIの実装 前編 Speech Synthesis API](https://app.codegrid.net/entry/2016-web-speech-api-1)