ZOZOテクノロジーズ #4 Advent Calendar 2019 の10日目です。
昨日は@pakioさんの「Laravel + Vue + Vuetifyでbundle sizeの削減を試みてみる」でした。
なんと先日、英単語の日本語訳とレベルを同時に取得でき、しかもローカル環境だけで動く素晴らしいJavaScriptライブラリに出会いました! この記事ではそのライブラリを使ってChrome拡張機能を作っていきます。
作っているもの
YouTubeの英語字幕をディクテーション(リスニング)できるChrome拡張機能「YouTube-Dictation」
github.com/tippy3/youtube-dictation
わかりにくいスクショで恐縮です。
つまりは動画の下に字幕を出して、ユーザーが英単語をディクテーションするということです。
実際に作った流れ
Chrome拡張機能はJavaScript, HTML, CSSで開発します。めちゃくちゃ簡単です。
1 manifest.json
まずは各ファイルを用意し、エントリーポイントとなるmanifest.json
を作ります。
{
"name": "YouTube-Dictation",
"short_name": "YouTube-Dictation",
"description": "YouTubeで楽しく英語学習。好きな動画の英語字幕でディクテーション(リスニング)ができる拡張機能です。",
"version": "1.0",
"icons": {
"128": "images/128.png"
},
"content_scripts": [{
"matches": ["https://*.youtube.com/*", "http://*.youtube.com/*"],
"run_at": "document_end",
"css": ["css/main.css"],
"js": ["js/superagent-master/superagent.js", "js/underscore-master/underscore.js", "js/javascript-lemmatizer-master/js/lemmatizer.js", "js/kantan-ej-dictionary-master/kantan-ej-dictionary.js", "js/main.js"]
}],
"web_accessible_resources": [
"js/javascript-lemmatizer-master/dict/*.json"
],
"permissions": [
"storage"
],
"manifest_version": 2
}
概要をざっくり説明すると、
content_scriptsは表示しているWebページに挿入されるJavaScriptです。表示中のDOMを操作したり、キーイベントを追加したりすることができます。他にも拡張機能のアイコンを押されたときに実行されるpage_actionなどがあります。
今回はcontent_scriptsでYouTubeのURLにマッチしたときにCSSとJavaScriptを読み込みます。JavaScriptは依存関係のあるライブラリを順に読み込み、最後に自分の処理を書くmain.js
を読み込みます。"run_at"でWebページの読み込み後に実行するようにしています。
また、ライブラリにJSONを使うものがあったのでweb_accessible_resourcesにファイル名を書きました。これを書かないとJavaScriptからJSONにアクセスできません。また、Web Storageを使うライブラリがあったのでpermissionsに"storage"を入れました。
ここからmain.js
をガリガリ書いていきます。
2 UIを出力する
このやり方は完全に自己流なのですが、JavaScriptでDOMをappendChildしてUIを出力します。なんか頭の悪い感じもしますが動くのでヨシ!
let html = document.createElement("div");
html.id = "yt-dictation-container";
html.innerHTML = '<p id="yt-dictation-text">YouTube-Dictation</p>...長いので省略';
html.addEventListener('click', clickEvent);
document.body.appendChild(html);
3 AjaxでAPIを叩き字幕を取得する
AjaxするためのライブラリsuperagentでJSONを取得します。使い方はjQueryの.ajax()とほぼ同じです。
function requestCCList(){
SUPERAGENT
.get(YOUTUBE_CC_API)
.accept('json')
.query({
type: 'list',
v: video_id
})
.end(responseCCList);
}
function responseCCList(err, res){
// 略。下記参照
}
実際には、まずは字幕一覧を取得し、もし英語の字幕があったらその字幕を取得する、という2段構えになっています。
また、字幕を取得後、行ごとの配列を作成し、さらに単語ごとの配列を作成しています。
4 ディクテーションする英単語を選ぶ
**この章がこの記事の本編です。**はじめは英単語をランダムに出題していたのですが、ユーザーのレベルに合わせた英単語を出題したいと思いました。そこで使ったのが以下のライブラリです。
-
kantan-ej-dictionary
- 英単語の和訳とSVLレベルを取得できるとても素晴らしいライブラリ
-
javascript-lemmatizer
- 英単語の原型を取得できるとても素晴らしいライブラリ
英単語のレベル分けには、アルクによるSVL12000(重要英単語12000語を1から12までの12段階にレベルわけしたもの)を採用し、それを和訳とともに取得できるライブラリkantan-ej-dictionaryを使ってみました。
しかし、複数形や過去形だとkantan-ej-dictionaryにヒットしないため、原型でヒットしなかった場合はjavascript-lemmatizerを使って英単語を原型に戻し、再度検索しました。
なんとこれらライブラリはいずれもローカルだけで動きます。しゅばらしい。。。
あとは取得した英単語のレベルを使い、最もユーザーのレベルにあった英単語を出題します。
実際のコードが下記です。英単語ごとにループを回し、以下の処理を行います。
let candidates = []; // 問題にする単語の候補(単語のインデックス番号が入る)
let min_diff = 99;
cc.words.forEach((word,index)=>{
const original_word = removeSymbol(word);
let result = kanten_dictionary[original_word]; // まずは英単語を辞書で検索
if(!result || !result.svl_level){
// 辞書にヒットしなかった場合、英単語を原型に戻す
const lemma = LEMMATIZER.lemmas( original_word );
if(lemma.length==0){
return false; // 原型に戻せなかった場合
}else{
result = lemma[0][0];
}
result = kanten_dictionary[result]; // 原型で辞書を検索
if(!result || !result.svl_level){
return false; // 原型でも辞書にヒットしなかった場合
}
}
// 単語のレベルとユーザーのレベルの差を求める
const diff = Math.abs( result.svl_level - USER_SVL_LEVEL );
if( diff<min_diff || min_diff==99 ){
// よりレベルの近い単語が見つかった場合、その単語を候補にする
min_diff = diff;
candidates = [];
candidates.push(index);
}else if( diff==min_diff ){
// レベルの差が同じ単語が見つかった場合、その単語を候補に入れる
candidates.push(index);
}
});
あとは出題する英単語をinputタグに変え、動画の再生時間に合わせて字幕をUIに出力します。入力の判定処理やキーイベントなども書いて、、、完成!!!せず、本日を迎えてしまいました。
実は一旦ストアへリリースしたのですが、
- このテキトーなUIを改善しなきゃなぁ
- ユーザーのレベルは決め打ちじゃなくて動的に決めたいなぁ
- 英単語の和訳も表示したいなぁ
- 間違えた単語をセーブして復習できるようにしたいなぁ
などの欲が出た結果、このアドベントカレンダーに間に合わなくなりました。ごぺんなさい🐧
ここからはストアへの公開についてです。
Chromeウェブストアへの公開方法
Q: ストアへの登録は難しいですか?
A: 面倒くさいですが難しくはありません。
1 まずはデベロッパー登録($5)
無料で公開する場合もデベロッパー登録が必要です。氏名・住所・電話番号等を入力し、登録料5ドルをクレジットカードで支払います。登録料は1回払えばOKです。
2 作った拡張機能を登録
拡張機能の名前や説明、販売する国や金額を入力していきます。途中で縦横比別にサムネイル画像を登録していくのですが、これがとても面倒くさかったです。アイコンやスクリーンショット含めて5種類も画像を作らされました。
3 Googleによる審査を待つ
無料公開でもGoogleによる審査があります。私の場合は公開申請後1日以内に通ることが多かったです。
これでようやく公開!(してないけど)
記事本文はここまでです。
謝辞
共同開発者の@motonari728に深く感謝申し上げます。また一緒に食事でも行きましょう。
もっとChrome拡張機能について知りたくなったら(参考文献)
QiitaにはChrome拡張機能に関する記事がたくさんあります。はじめに読むべき2本をオススメとして紹介します。
- Chrome拡張開発 超概要(特に主な拡張の種類の章が分かりやすいです)
- Chrome 拡張機能のマニフェストファイルの書き方(他にもググるとたくさん出てきます)
次回予告
明日は@252525さんです。お楽しみに!
最後までお読みいただきありがとうございました。