📑

Yomitan:ポップアップ辞典の強化

2024/02/14に公開

この記事でYomitanというポップアップ辞典アドオンの改善を目指すべく、Issue投稿から会話の結果までの経緯を伝えします。

Yomitanとは

まず、日本の方ならご存知ないと思いますが、YomitanとはChromeとFirefoxで使えるポップアップ辞典のアドオンです。具体的に、shiftを押しながらマウスで単語をホバーすると、次のポップアップが表示される:

Yomitanポップアップ

ご覧いただけたように、単語の定義が表示されます。ここで国語辞典を使用しておりますが、Yomitanのユーザの多くは日本語の学習者であり、たいてい日英辞典を使用されるわけだ。

辞典の取得

よく使われる辞典の一覧はこちらにあります。面白いことに、日・モ辞典(つまり、モンゴル)も乗っています。

また、国語辞典もたくさん乗っており、例えば漢検の受験者の方々に漢検漢字辞典 第二版お勧めできます。

あとはアドオンの設定から時点をインポートしましょう。

Anki

私の提案を説明する前に、Ankiというソフトウェアの説明をする必要があります。

Ankiとは言語・医学学習によく使われる間隔反復ソフトウェアです。学習は主にフラッシュカードをもとに行われます。

よくある用例は、まず「scalding」のような英単語が表示され、ユーザは訳語を思い出そうと「火傷」が思い浮かぶとします。カードの裏を表示するとこちらも「火傷」が乗っていますのでユーザ「正解」を入力します。こうすると、明日までこのカードを復習する必要はありません。そして、明日も正解を思い出せれば、今度は4日後に復習するわけです。

詳しくは次の記事をご参考ください。

Yomitan・Anki連携

YomitanにはAnkiとの連携を成し遂げるための機能がいくつか備えています;

  1. ポップアップからAnkiに新しいノート(表と裏のカードのセット)の追加ができる
    ノート追加ボタン
  2. ホバーしている単語にあたるカードはAnkiにすでにあれば、➕アイコンが本に変わる
    ノート追加ボタンが本アイコンになっている

Anki連携のセットアップについてはYomitan: Anki Integrationページをご参考ください。

重複ノート

Yomitanではデフォルトで「重複ノート」の作成は機能しません。そして、設定を変えて可能にしたとしても、今度はAnkiブラウザーでの表示が不可能になります。

ですが、同じ言葉でも、文脈に応じて複数の意味合いを保つことは多いのではないでしょうか。つまり、重複カードは単語の全ての意味の学習を徹底するための最高にツールだと考えます。実際、自分のAnkiデッキに重複ノートを数多く収めています。そして、重複ノートが二つ以上あった場合、本のアイコンにプラスが付き、右クリックすると重複のIDがそれぞれ表示されます:

「一括」における本のアイコンの変化
本のアイコンを右クリックした結果

このメニューを使い、Ankiブラウザーで表示されるノートを容易に選択できますが、単一のノートしか表示できません。また、本のアイコンが右クリックできることを示唆しているUI要素や説明文はどこにもありません。

私の提案

右クリックメニューよりも、本のアイコンをクリックすればデフォルトで全重複ノートを表示します。

予備提案:デフォルトでダメならば、右クリックメニューに「全てのノートを表示」という項目を追加します。クリックすると全重複ノートが表示されます。

開発

AnkiConnect API

ノートIDでAnkiブラウザーの表示を要求するにはAnkiConnect APIのguiBrowseアクションを送信する必要があります:

{
    "action": "guiBrowse",
    "version": 6,
    "params": {
        "query": "deck:current"
    }
}

queryにAnkiブラウザーの検索バーの入力を直接書くことができます。複数のノートを表示するには、Object IDsを用い、クエリをnid:<id1>,<id2>にします。これで<id1><id2>などにあたるノートが表示できます。

コードの変換

今まで一つのノートしか表示しなかったコードを複数のノートに対応させます。その一例として、表示制限をノート一枚にしている元の関数と新関数はこちら:

ext/js/display/display-anki.js
/**
 * @param {HTMLElement} node
 */
async _viewNote(node) {
    const noteIds = this._getNodeNoteIds(node);
    if (noteIds.length === 0) { return; }
    try {
        await this._display.application.api.noteView(
            noteIds[0],
            this._noteGuiMode,
            false
        );
    } catch (e) {
       // エラー処理
    }
}
ext/js/display/display-anki.js
/**
 * @param {HTMLElement} node
 */
async _viewNotes(node) {
    const noteIds = this._getNodeNoteIds(node);
    if (noteIds.length === 0) { return; }
    try {
        await this._display.application.api.viewNotes(
            noteIds,
            this._noteGuiMode,
            false
        );
    } catch (e) {
        // エラー処理
    }
}

変化はこれぐらい:

-   await this._display.application.api.noteView(
-       noteIds[0],
        this._noteGuiMode,
        false
    );

+   await this._display.application.api.viewNotes(
+       noteIds,
        this._noteGuiMode,
        false
    );

ホットキーも

選択された単語のノートを表示するためのホットキーもあります(デフォルトはAlt+V、MacならOpt+V)。単語を選択(つまりクリック)すると左側に青矢印が現れます:

クリックした単語の左に現れた青矢印

この機能も本来単一ノートしか表示しませんので、上記の変更との一貫性を保つために少しだけ変化を加えました。

まずはホットキーの仕組みから:ホットキーを押すと、Yomitanは特定のイベント(action)を発生します:

{
    action: 'viewNote',
    // ホットキー:V + alt
    key: 'KeyV',
    modifiers: ['alt'],
    // ポップアップか検索バーが開いている場合のみ発生する
    scopes: ['popup', 'search'],
    enabled: true
}

上記のviewNoteviewNotesに変えましょう。
次に、元viewNoteアクションのリスナー関数もviewNotesに繋げましょう:

js
- ['viewNote', this._viewNoteForSelectedEntry.bind(this)]
+ ['viewNotes', this._viewNotesForSelectedEntry.bind(this)]

あら?何も起こりません。

実は、ホットキーとアクション名を繋げるのはホットキー設定画面における<option>要素の値のようです:

html
- <option value="viewNote">View note</option>
+ <option value="viewNotes">View notes</option>

これでやっと動きます。

Discussion