Web Speech APIで英語の教材を作るには?
ここではブラウザがサポートしているWeb Speech APIを使用して英語の教材を作る方法を解説します。
このAPIを使えば英語・日本語のテキスト文字を読み込んで音声出力させたり、音声認識などが可能になります。
ここではこのAPIを使用して「英語」を「日本語」に翻訳したり、「日本語」を「英語」に翻訳するといった教材を作成します。
作成手順
-
VS2019(Visual Studio 2019 or Visual Studio 2022)を起動したら新規のWebフォーム「Article015.aspx」を作成します。
-
Webフォームのヘッダータグ内「<head>...</head>」にSTYLEタグを追加して以下のようなCSSを入力します。
Article015.aspx:
<style>
input[type=text] {
width: 45rem;
max-width: 100%;
}
input[type=button]{
background-color: orange;
}
</style>
-
Webフォームのヘッダータグ内「<head>...</head>」にSCRIPTタグを追加してjQueryのライブラリーを取り込みます。
さらにSCRIPTタグを追加して、次のようなJavaScript/jQueryのコードを入力します。
Article015.aspx:
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script type="text/javascript">
// jQuery Document Ready Event...
$(function () {
var voices = [];
// getVoices function
var getVoices = function () {
if (voices.length) {
return console.log('getVoices : Already Changed');
}
voices = window.speechSynthesis.getVoices();
if (!voices.length) {
return console.log('getVoices : Cannot Get Voices');
}
};
getVoices(); // 1st call
window.speechSynthesis.onvoiceschanged = getVoices; // 2nd call,...
// getVoiceLangJP() function
var getVoiceLangJP = function () {
var jp = -1;
for (let i = 0; i < voices.length; i++) {
var voice = voices[i];
if (voice.lang == 'ja-JP') {
if (voice.name.indexOf('Google 日本語') != -1 || voice.name.indexOf('Microsoft Nanami Online') != -1) {
jp = i;
break;
}
}
}
// Not found voice lang?
if (jp == -1) {
for (let i = 0; i < voices.length; i++) {
var voice = voices[i];
if (voice.lang.indexOf('ja') != -1 && voice.lang.indexOf('JP') != -1) {
jp = i;
break;
}
}
}
return jp;
};
// getVoiceLangUS() function
var getVoiceLangUS = function () {
var us = -1;
for (let i = 0; i < voices.length; i++) {
var voice = voices[i];
if (voice.lang == 'en-US') {
if (voice.name.indexOf('Google US English') != -1 || voice.name.indexOf('Microsoft Aria Online') != -1) {
us = i;
break;
}
}
}
// Not found voice lang?
if (us == -1) {
for (let i = 0; i < voices.length; i++) {
var voice = voices[i];
if (voice.lang.indexOf('en') != -1 && voice.lang.indexOf('US') != -1) {
us = i;
break;
}
}
}
return us;
};
// --------------------------------------------- Exercise 1
// Speak Japansese
$('.speakJP').click(function () {
var jp = getVoiceLangJP();
if (jp == - 1) {
return console.log('Not found voice lang ja-JP');
}
var text = $(this).parent().parent().find('span').text();
const speechSynthesisUtterance = new SpeechSynthesisUtterance(text);
speechSynthesisUtterance.volume = 1;
speechSynthesisUtterance.rate = 1;
speechSynthesisUtterance.pitch = 1;
speechSynthesisUtterance.voice = voices[jp];
speechSynthesisUtterance.lang = speechSynthesisUtterance.voice.lang;
window.speechSynthesis.speak(speechSynthesisUtterance);
});
// Translate Japanese to English
$('.translateUS').click(function () {
var $text = $(this).parent().parent().parent().find('input[type=text]');
var hidtext = $(this).parent().parent().parent().find('input[type=hidden]').val();
$($text).val(hidtext);
});
// Speak English
$('.speakUS').click(function () {
var us = getVoiceLangUS();
if (us == - 1) {
return console.log('Not found voice lang en-US');
}
var text = $(this).parent().parent().find('input[type=text]').val();
console.log(text);
if (!text.trim()) {
return alert('Please Input Text!');
}
const speechSynthesisUtterance = new SpeechSynthesisUtterance(text);
speechSynthesisUtterance.volume = 1;
speechSynthesisUtterance.rate = 1;
speechSynthesisUtterance.pitch = 1;
speechSynthesisUtterance.voice = voices[us];
speechSynthesisUtterance.lang = speechSynthesisUtterance.voice.lang; // [en-US]
window.speechSynthesis.speak(speechSynthesisUtterance);
});
// --------------------------------------------- Exercise 2
// Speak Japanese
$('.speakJP2').click(function () {
var jp = getVoiceLangJP();
var text = $(this).parent().parent().find('input[type=text]').val();
if (!text.trim()) {
return alert('Please Input Text!');
}
const speechSynthesisUtterance = new SpeechSynthesisUtterance(text);
speechSynthesisUtterance.volume = 1;
speechSynthesisUtterance.rate = 1;
speechSynthesisUtterance.pitch = 1;
speechSynthesisUtterance.voice = voices[jp];
speechSynthesisUtterance.lang = speechSynthesisUtterance.voice.lang; // [ja-JP]
window.speechSynthesis.speak(speechSynthesisUtterance);
});
// Translate English to Japansese
$('.translateJP').click(function () {
var $text = $(this).parent().parent().parent().find('input[type=text]');
var hidtext = $(this).parent().parent().parent().find('input[type=hidden]').val();
$($text).val(hidtext);
});
// Speak English
$('.speakUS2').click(function () {
var us = getVoiceLangUS();
var text = $(this).parent().parent().find('span').text();
const speechSynthesisUtterance = new SpeechSynthesisUtterance(text);
speechSynthesisUtterance.volume = 1;
speechSynthesisUtterance.rate = 1;
speechSynthesisUtterance.pitch = 1;
speechSynthesisUtterance.voice = voices[us];
speechSynthesisUtterance.lang = speechSynthesisUtterance.voice.lang; // [en-US]
window.speechSynthesis.speak(speechSynthesisUtterance);
});
});
</script>
-
WebフォームにHTMLのTABLEタグを追加して以下のような表を配置します。
「練習1-1」から「練習1-3」までは日本語を英語に翻訳させます。「練習2-1」から「練習2-3」までは英語を日本語に翻訳させます。
Article015.aspx:
<table class="article015-table" border="1">
<caption>練習1-1: 日本語を英語に翻訳</caption>
<tr>
<th>日本語</th>
<td>
<span>私たちは地球温暖化のことを心配せずにいられない。</span>
</td>
<th>
<input class="speakJP" type="button" value="Speak" />
</th>
</tr>
<tr>
<th colspan="3">
<input class="translateUS" type="button" value="Translate to English" />
</th>
</tr>
<tr>
<th>英 語</th>
<td>
<input type="text" value="" />
<input type="hidden" value="We can't help worrying about global warming." />
</td>
<th>
<input class="speakUS" type="button" value="Speak" />
</th>
</tr>
</table>
<table class="article015-table" border="1">
<caption>練習1-2: 日本語を英語に翻訳</caption>
<tr>
<th>日本語</th>
<td>
<span>そんないい話断るなんておかしいよ。</span>
</td>
<th>
<input class="speakJP" type="button" value="Speak" />
</th>
</tr>
<tr>
<th colspan="3">
<input class="translateUS" type="button" value="Translate to English" />
</th>
</tr>
<tr>
<th>英 語</th>
<td>
<input type="text" value="" />
<input type="hidden" value="It doesn't make sense to turn down such a good offer." />
</td>
<th>
<input class="speakUS" type="button" value="Speak" />
</th>
</tr>
</table>
<table class="article015-table" border="1">
<caption>練習1-3: 日本語を英語に翻訳</caption>
<tr>
<th>日本語</th>
<td>
<span>どの候補者も私は信用しない。彼らはいつも守らない約束をする。</span>
</td>
<th>
<input class="speakJP" type="button" value="Speak" />
</th>
</tr>
<tr>
<th colspan="3">
<input class="translateUS" type="button" value="Translate to English" />
</th>
</tr>
<tr>
<th>英 語</th>
<td>
<input type="text" value="" />
<input type="hidden" value="I don't trust any candidate. To begin with, they always make promises they don't keep." />
</td>
<th>
<input class="speakUS" type="button" value="Speak" />
</th>
</tr>
</table>
<hr />
<table class="article015-table" border="1">
<caption>練習2-1: 英語を日本語に翻訳</caption>
<tr>
<th>英 語</th>
<td>
<span>Unless you make every effort, you'll be left behind.</span>
</td>
<th>
<input class="speakUS2" type="button" value="Speak" />
</th>
</tr>
<tr>
<th colspan="3">
<input class="translateJP" type="button" value="Translate to Japanese" />
</th>
</tr>
<tr>
<th>日本語</th>
<td>
<input type="text" value="" />
<input type="hidden" value="あらゆる努力をしなければ置いていかれる。" />
</td>
<th>
<input class="speakJP2" type="button" value="Speak" />
</th>
</tr>
</table>
<table class="article015-table" border="1">
<caption>練習2-2: 英語を日本語に翻訳</caption>
<tr>
<th>英 語</th>
<td>
<span>Making money is not an end in itself but just a means to an end.</span>
</td>
<th>
<input class="speakUS2" type="button" value="Speak" />
</th>
</tr>
<tr>
<th colspan="3">
<input class="translateJP" type="button" value="Translate to Japanese" />
</th>
</tr>
<tr>
<th>日本語</th>
<td>
<input type="text" value="" />
<input type="hidden" value="お金を稼ぐことはそれ自体が目的ではなく、目的のための手段に過ぎない。" />
</td>
<th>
<input class="speakJP2" type="button" value="Speak" />
</th>
</tr>
</table>
<table class="article015-table" border="1">
<caption>練習2-3: 英語を日本語に翻訳</caption>
<tr>
<th>英 語</th>
<td>
<span>As far as I'm concerned, I'm willing to take any risk.</span>
</td>
<th>
<input class="speakUS2" type="button" value="Speak" />
</th>
</tr>
<tr>
<th colspan="3">
<input class="translateJP" type="button" value="Translate to Japanese" />
</th>
</tr>
<tr>
<th>日本語</th>
<td>
<input type="text" value="" />
<input type="hidden" value="私に限って言えば、どんな危険を冒すこともいとわない。" />
</td>
<th>
<input class="speakJP2" type="button" value="Speak" />
</th>
</tr>
</table>
操作手順と解説
-
サンプルがブラウザに表示されると、JavaScriptの関数「getVoices()」が実行されます。
この関数では7行目で「voices = window.speechSynthesis.getVoices();」を実行して1行目で定義した配列変数「voices」にブラウザがサポートしている
音声情報を取得して格納します。配列変数「voices」には図1のような音声情報が格納されています。
図をクリックすると拡大表示されます。
var voices = [];
var getVoices = function () {
if (voices.length) {
return console.log('getVoices : Already Changed');
}
voices = window.speechSynthesis.getVoices();
if (!voices.length) {
return console.log('getVoices : Cannot Get Voices');
}
};
-
サンプルが表示されたら「練習1-1」からはじめてみましょう。
-
まずは日本語の文章「私たちは地球温暖化のことを心配せずにいられない。」を読んだら英語に翻訳して「英語」のテキストボックスに入力します。
[Translate to English]ボタンをクリックすると回答が表示されます。[Speak]ボタンをクリックすると音声が日本語・英語で出力されます。
日本語の[Speak]ボタンをクリックしたときはJavaScriptの1行目から14行目が実行されます。2行目で関数「getVoiceJP()」を呼び出していますが、
この関数では配列変数「voices」に格納されている音声情報を検索して日本語の音声情報が格納されている要素番号を返します。
具体的には図1の10番目「name:"Google 日本語"、lang:"ja-JP"」の要素番号を返します。
6行目ではHTMLの「INPUT(type=text)」要素から日本語のテキスト文字を取得しています。
そして7行目で「SpeechSynthesisUtterance」のインスタンスを生成します。
このインスタンスを13行目でwindow.speechSynthesisのspeak()メソッドの引数に指定すると音声が出力されます。
8行目-12行目では音声出力に関する詳細を設定しています。
英語のテキスト文字の音声を出力するときも同様の手順で行います。
英語のテキスト文字を英国の女性の音声で出力するときは配列変数「voices」から2番目の要素「name:"Google UK English Female", lang:"en-GB"」を選択します。
男性の音声で出力するときは3番目の要素「name:"Google UK English Male", lang:"en-GB"」を選択します。
$('.speakJP').click(function () {
var jp = getVoiceLangJP();
if (jp == - 1) {
return console.log('Not found voice lang ja-JP');
}
var text = $(this).parent().parent().find('span').text();
const speechSynthesisUtterance = new SpeechSynthesisUtterance(text);
speechSynthesisUtterance.volume = 1;
speechSynthesisUtterance.rate = 1;
speechSynthesisUtterance.pitch = 1;
speechSynthesisUtterance.voice = voices[jp];
speechSynthesisUtterance.lang = speechSynthesisUtterance.voice.lang;
window.speechSynthesis.speak(speechSynthesisUtterance);
});
var getVoiceLangJP = function () {
var jp = -1;
for (let i = 0; i < voices.length; i++) {
var voice = voices[i];
if (voice.lang == 'ja-JP') {
if (voice.name.indexOf('Google 日本語') != -1 || voice.name.indexOf('Microsoft Nanami Online') != -1) {
jp = i;
break;
}
}
}
if (jp == -1) {
for (let i = 0; i < voices.length; i++) {
var voice = voices[i];
if (voice.lang.indexOf('ja') != -1 && voice.lang.indexOf('JP') != -1) {
jp = i;
break;
}
}
}
return jp;
};
-
「練習1-1」から「練習1-3」が終了したら「練習2-1」に移ります。
-
「練習2-1]では英語を日本語に翻訳します。英語の文章「Unless you make every effort, you'll be left behind.」を読んだら日本語に翻訳して
「日本語」のテキストボックスに入力します。[Translate to Japanese]ボタンをクリックすると回答が表示されます。
[Speak]ボタンをクリックすると音声が日本語・英語で出力されます。