LoginSignup
1

Vue.jsと音声APIで世界のみんなにこんにちは!

Posted at

外国人観光客に話しかけられたとき・・・

2020年から3年間続いたコロナ禍もようやく落ち着き、最近は街中で外国人観光客の姿を見ることが多くなりましたね。私は話しかけやすそうな見た目なのか、よく彼らに道を聞かれます。

道案内をするときは「Where are you from?」と聞くようにしていますが、自分が前提知識を持っていない国から来た方だったとき、「Oh...」しか言うことがなく、逆に気まずい感じになってしまいます:sweat:

せめて、「こんにちは」だけでも彼らの母語で言えたら・・・!

ということで!外国人観光客に道を聞かれたとき、彼らの母語であいさつできるWebアプリを作りました:information_desk_person:

この記事でわかること

  • Vue.jsaxiosを使ったAPIの呼び出し方
  • Web Speech API(音声読み上げ)で多言語対応する方法

世界の言葉でこんにちは!

アプリはCodePenで作成したものをエクスポートし、Netlifyでデプロイしました!

See the Pen 世界の言葉でこんにちは! by 小島チヒロ (@blttsxag-the-solid) on CodePen.

APIはブラウザで音声を扱えるWeb Speech APIと、世界の挨拶を取得できるHelloSalut APIを使いました!国は入国者上位10か国から選びましたが、ベトナムとフィリピン(タガログ語)はAPIで取得できませんでした:sweat_smile:

実装

①ソースコード

HTMLコード
index.html
<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>say_hello</title>
  <meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="./style.css">

</head>
<body>
<!-- partial:index.partial.html -->
<html>

<head>
  <title>世界の言葉でこんにちは!</title>
</head>

<body>
  <h1>世界の言葉でこんにちは!</h1>
  <div id="app">
    <form>
      <select id="nation" v-model="nation" class="nation-select">
        <option value="">どこから来たの?</option>
        <option>de:ドイツ</option>
        <option>en:アメリカ</option>
        <option>es:スペイン</option>
        <option>fr:フランス</option>
        <option>hi:インド</option>
        <option>id:インドネシア</option>
        <option>ko:韓国</option>
        <option>zh:中国</option>
      </select>
    </form>
    <button id="btn" v-on:click="getSalut">あいさつ!</button>
    <p>{{ nation.split(":")[1] }}</p>
    <p id="salut">{{ salut }}</p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue2.6.10/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</body>

</html>
<!-- partial -->
  <script  src="./script.js"></script>

</body>
</html>
CSSコード
style.css
body {
  background-color: rgb(153, 242, 179);
  text-align: center;
}

h1 {
  color: black;
}

.info {
  font-weight: bold;
  color: green;
}

#comingout {
  display: none;
  color: blue;
}
JavaScriptコード
script.js
let app = new Vue({
  el: "#app",
  data: {
    nation: "",
    salut: ""
  },
  methods: {
    getSalut: async function () {
      let lang_code = this.nation.split("")[0];
      let HelloSalutUrl =
        "https://hellosalut.stefanbohacek.dev/?lang=" + lang_code;
      await axios
        // HelloSalut API呼び出し
        .get(HelloSalutUrl)
        .then(function (response) {
          return response["data"];
        })
        .then(function (jsonData) {
          this.salut = jsonData["hello"];
          document.getElementById("salut").innerHTML = `${this.salut}`;
          readAloud(decNumRefToString(this.salut), lang_code);
        });
    }
  }
});
// 音声読み上げする関数
function readAloud(salut, lang_code) {
  // ブラウザにWeb Speech API Speech Synthesis機能があるか判定
  if ("speechSynthesis" in window) {
    // 発言を設定
    const uttr = new SpeechSynthesisUtterance();
    uttr.text = salut;
    uttr.lang = lang_code;
    // 発言を再生
    window.speechSynthesis.speak(uttr);
  } else {
    alert("このブラウザは音声合成に対応していません。");
  }
}
// 数値文字参照(10進数)を文字列に変換する関数
function decNumRefToString(decNumRef) {
  return decNumRef.replace(/&#(\d+);/gi, function (match, $1, idx, all) {
    return String.fromCharCode($1);
  });
}

②解説

実装に苦労した以下2か所について解説します。
時間の都合上詳細解説は割愛しますが、コメントで質問いただければ対応します:thumbsup:

  • Vue.jsaxiosを使ったAPIの呼び出し方
  • Web Speech API(音声読み上げ)で多言語対応する方法

②-1:Vue.jsaxiosを使ったAPIの呼び出し方

「あいさつ!」ボタンを押したらHelloSalut APIが呼ばれ、取得した結果を画面に表示するというわりとありきたりな機能なのですが、すぐに使えるコードがなかなか見つからず:sweat: 試行錯誤した結果、以下のようなコードだとうまく動きました。ぜひお使いください!

ボタンを押したら画面表示(HTML)
<button id="btn" v-on:click="getSalut">あいさつ!</button>
<p>{{ nation.split(":")[1] }}</p>
<p id="salut">{{ salut }}</p>
axiosでAPI呼び出し
methods: {
    getSalut: async function () {
      let lang_code = this.nation.split("")[0];
      let HelloSalutUrl =
        "https://hellosalut.stefanbohacek.dev/?lang=" + lang_code;
      await axios
        // HelloSalut API呼び出し
        .get(HelloSalutUrl)
        .then(function (response) {
          return response["data"];
        })
        .then(function (jsonData) {
          console.log(jsonData);
          this.salut = jsonData["hello"];
          document.getElementById("salut").innerHTML = `${this.salut}`;
          readAloud(decNumRefToString(this.salut), lang_code);
        });
    }
  }

axiosで「なんか取得できない!?」となったら、とりあえず呼び出し箇所にawaitが付いているかを確認しましょう:point_up:

②-2:Web Speech API(音声読み上げ)で多言語対応する方法

つまづきポイント①:APIで取得できるあいさつが謎の数列

読み上げ機能の多言語対応は本当に大変でした・・・
中国語や韓国語など一部の言語について、APIで取得できるあいさつが謎の数列だったのです:scream:

こんなやつ
"&#20320;&#22909;"

調べてみると文字参照というものらしいです。ブラウザ表示は自動で文字に変換して表示してくれたのですが、Web Speech APIは数字をそのまま読み上げてしまいました・・・

解決策としては、ありがたいことに文字参照を文字列に変換するコードをGitHubにあげてくださっている方がいたので、使わせていただきました:pray:

数値文字参照(10進数)を文字列に変換する関数
function decNumRefToString(decNumRef) {
  return decNumRef.replace(/&#(\d+);/gi, function (match, $1, idx, all) {
    return String.fromCharCode($1);
  });
}
つまづきポイント②:読み上げがカタコト

Web Speech APIは、デフォルトでAPIを叩く環境(私の場合日本)に依存した設定になるようです。なので、何もせずにHelloを読み上げさせると「はろー」とめちゃくちゃカタコトになってしまいました・・・
ちょうどAPI呼び出し用に取得していた言語コードを、Web Speech APIの言語設定にも使えたので流用しました!

音声読み上げ
// ブラウザから「言語コード:国名」の形で取得しているため「:」前後で分割
let lang_code = this.nation.split("")[0];
// 省略
readAloud(decNumRefToString(this.salut), lang_code);
// 省略
// 音声読み上げする関数
function readAloud(salut, lang_code) {
  // ブラウザにWeb Speech API Speech Synthesis機能があるか判定
  if ("speechSynthesis" in window) {
    // 発言を設定
    const uttr = new SpeechSynthesisUtterance();
    uttr.text = salut;
    uttr.lang = lang_code;
    // 発言を再生
    window.speechSynthesis.speak(uttr);
  } else {
    alert("このブラウザは音声合成に対応していません。");
  }
}

今後改良できる点

①スマホで使えるようにする

今回外国人観光客に話しかけられたときに使うことを目的にしていたのですが、肝心のスマホでは何故か音声が流れず・・・iPhoneだからか?と思いましたがそういうわけでもなさそう。
どなたか原因がわかる方がいらっしゃればぜひコメントで教えてください:bow:

②おしゃれなデザインにする

今回は機能面を充実させることに注力したため、アプリのUIはイマイチになってしまいました:sweat_smile: JavaScriptにはUIに特化したライブラリがたくさんあるため、今後は積極的に使っていこうと思います!

  • jsDelivr:様々なJSライブラリへのオンラインアクセスを提供してくれるサービス

③対応している国・地域・言語を増やす

同じ国・地域で異なる言語を使っていたり、逆に同じ言語が複数の国で使われていたりするので、すべてを網羅するのは難しいですが、できる限り増やしていきたいです!

参考にしたサイト

この記事が参考になった!
という方はぜひ、いいね・ストック・コメントや、あなたのQiita記事にリンクをつけてください:wink: 励みになります!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1