Voice Of The World開発記 第3回です。
前回のエントリはこちら:多言語読み上げブラウザVOTWをAndroid向けに作った話(2)
Voice Of The World Android版販売中です。
Voice Of The World(GooglePlay)
今回のお話
前回までで、JavaScriptからコンテンツを取り出し、処理しやすいようにテキストの最適化までできました。また、その前に言語を判定することで、何語で書いてあるかまでを判別することができました。
今回は、これを踏まえて、テキストリスト配列のそれぞれの項目を「文章なのか、そうでないのか」判別して、マーキングしていきたいと思います。
文章なのかい、文章じゃないのかい、どっちなんだい!?
多言語で書いてあるコンテンツを対象とする場合は、文章の書式をある程度法則でふるい分ける必要がありますが、当然言語によって法則が異なります。
VOTWの実装では、判別用のベースクラスを作って、各言語向けに具象クラスを作っています。
日本語の文字列判別クラスの実装はこんな感じです。
public boolean isSentense(String str, int lang) {
// 文字列判定offだったら、常に文字列として返す
if( mSetStringSearchMode == false ) return true;
// 全部ANK文字だったらfalse
if( isANKString(str) == true ) return false;
// 分割マーカーの文字があったら、分割してから評価する
// 縦棒、全角スペース(半角は見た目調整で使っている文章が多いので除外)
Pattern splitter = Pattern.compile("[|| ]+");
String[] strs = splitter.split(str);
for( int i=0; i<strs.length; ++i ) {
if (strs[i].matches("(?s).*[.,”、。「」?,?!\"].*")) {
return true;
}
// 格助詞の前に最低1文字ある
if (strs[i].matches("(?s).+(が|の|を|に|へ|と|から|より|で|や|ヲ).*")) {
return true;
}
// 空白以外を数えて一定文字数以上であること
if (countNoSpace(strs[i]) > 8) return true;
}
return false;
}
苦労してますね(笑) それに、我ながらちょっと正規表現が冗長です。
助詞の判定にまで手を出しているのは、(まだ)日本語版フィルタだけです。
欧米の言語の場合、単語で区切る特性があるので、そちらも注目します。英語の判別クラスはこんな感じです。
public boolean isSentense(String str, int lang) {
if( mSetStringSearchMode == false ) return true;
// スペースが3個以上あったら、文章らしいとみなす
int spcnt=0;
int s=0;
String sp=" ";
Pattern p = Pattern.compile(sp);
Matcher m = p.matcher(str);
while( m.find(s))
{
spcnt++;
s = m.end();
}
if( spcnt < 3 )
{
return false;
}
// NG文字群
if( str.matches("(?s).*[|].*"))
{
return false;
}
if( str.matches("(?s).*[,\\.\\?\\!\"].*"))
{
return true;
}
if( spcnt >= 5 )
{
return true;
}
return false;
}
他の言語でもなるべく特徴をとらえるようにしていますが、全体的な傾向としてこんな感じです。
- 日本語、中文繁体、中文簡体、韓国語
- 句読点、括弧、感嘆符やクエスチョンマーク、引用符などを積極的に評価する
- 文字数もある程度参考にするけど、優先度は低め
- 英語、ドイツ度、フランス語、スペイン語
- 空白文字を数えて単語数を数えるのを最も優先度高く評価する。特に、単語が少なすぎるかどうかを真っ先に見る。
- カンマや引用符、感嘆符、クエスチョンマークなども高めに評価する。
- スペイン語みたいに特殊な引用符を持つ場合も、積極的に文章の一部として評価する(「¿¡」等)
また、上記の判定コードでもやっているように、「|」のように、コンテンツの見た目を整えるために使うけど、文章の中には絶対に登場しない文字はNG文字として評価します。
こういうアルゴリズムを煮詰めていくのに、AIエージェントの力を借りれば各言語の特徴を正確にとらえられそうなので、今後さらに改善を加えていく予定です(AIに判定してもらうロジックではなくて、言語の特徴を研究するという意味で)
このフィルタを通過して、「文章ぽい」と判断されたテキストが、リストとして画面に表示されます。mSetStringSearchModeがfalseで渡された場合は、フィルタは素通しで、前回ご紹介した最適化処理までとなります。あまり使いませんが、設定画面で「全部読む」のモードも設定できるようになっています。
改善の効果
さて、第2回の文字列最適化と今回の文章フィルタを適用して、読み上げる文章だけをリスト化するとどのように改善していくか、実際にやってみました。
お題は第1回目の記事をQiitaサイト上に表示したものです。
まず、VOTWアプリでQiitaアプリを表示してみます。こんな感じですね。
これを(本来のVOTWアプリでは持っていない表示モードですが)第1回の処理だけ適用して、JavaScriptから抜きだした直後のものをリスト化してみました。
結果はこちら。
文字列がかなりちぎれたような状態で取得されています。括弧なども独立したテキストになっちゃってますね。これがDOMツリーの中からテキストノードを取り出したばかりの状態です。
では、第2回の最適化処理を加えてみます。
文字列がかなりまとまりました。括弧もまとめられていますね。この状態だと、まだ文章フィルタリングしていないので、取り出した全てのテキストが含まれています。
(上記の結果より取得時間がずれたせいか、「Qiitaにログインして・・・」あたりの文章が増えちゃってますが)
次に、今回の文章フィルタ処理を加えて、文章だけを抜き出してみます。
今回ご紹介した文章フィルタを適用すると、完全ではないものの、ほぼ文章だけになっています。このくらいなら、何にもしないで再生ボタンを押す気になるんじゃないでしょうか(笑)
次は読み上げ
今回までで、大分読み上げブラウザっぽくなってきました。
ですが、まだテキストを取り出しただけなので、今度はTTSにこれを流し込んでやる必要があります。
次回はTTSのお話です。
ではまた。



