Markdown Note Apri にスペルチェック機能を追加しました。(ただしα版です。)

markdown.aprifield.com

オンラインで利用可能な Markdown エディターを公開しています。 エディター部には VSCode でも利用されている Monaco Editor を採用しています。

f:id:aprifield:20200506124517p:plain

Markdown Note Apri にスペルチェック機能を追加しましたので、この件について記載します。

なお、今回の実装方法ではパフォーマンスが悪いし、機能的にもまだ改善の余地がありそうなため、 α 版機能という扱いで設定画面には (alpha) と書いています。

はじめに

ブラウザの input タグや textarea タグであれば、ブラウザ標準機能でスペルチェックできますが、Monaco Editor の入力項目は input タグや textarea タグではないため、標準機能が利用できません。 Monaco Editor にはスペルチェック機能が搭載されていないため、独自に作りこむ必要があります。

ブラウザですぐに使えるライブラリがあればよかったのですが、そのようなライブラリは見つからず、Web API を呼び出すことで対応しました。 以下の 2 つを検証し、結論としては後者を選択しました。

Azure の Bing Spell Check について

Bing Spell Check は Azure 上で公開されている Web API サービスです。 APIスペルチェック のキーワードで最初にヒットしたのがこれです。 価格 は、個人で使う分には許容範囲でしょうか。

大手のなので期待をもって使ってみましたが、チェックできるテキストの上限が厳しいという問題があり、利用を見送りました。

チェックモードには、校正(proof)とスペル(spell)があり、ドキュメントには以下のように書いてあります。

  • Proof
    • Meant to provide Office Word like spelling corrections. It can correct long queries, provide casing corrections and suppresses aggressive corrections.
    • (スペル修正のような Office Word を提供することを意味します。長いクエリを修正し、大文字と小文字を修正し、積極的な修正を抑制できます。)
  • Spell
    • Meant to provide Search engine like spelling corrections. It will correct small queries(up to length 9 tokens) without any casing changes and will be more optimized (perf and relevance) towards search like queries.
    • (スペル修正のような検索エンジンを提供することを意味します。大文字と小文字を変更せずに小さなクエリ(最大 9 トークン)を修正し、クエリのような検索に対してより最適化されます(パフォーマンスと関連性)。)

Proof であれば上限がないように見えますが、実際に試してみると正常に機能しません。 どんなテキストを指定してもチェックエラーなしの正常終了となります。 使い方が悪いのか?

Spell の場合は正常のチェックしてくれました。 ただし、ドキュメントの通り、長い文章はチェックしてくれません。

将来的に利用するかもしれないので、忘れないように axios でのサンプルコードを載せておきます。

  public async check(): Promise<void> {
    const params = new URLSearchParams();
    params.append('text', 'when+its+your+turn+turn,+john,+come+runing');
    // params.append('mode', 'proof');
    params.append('mode', 'spell');
    params.append('mkt', 'en-us');
    const result = await axios.post(
      'https://api.cognitive.microsoft.com/bing/v7.0/SpellCheck',
      params,
      {
        headers: {
          'Ocp-Apim-Subscription-Key': 'サブスクリプションキー'
        }
      }
    );
  }

textlint について

textlint は、特定のルールにしたがってテキストをチェックするツールです。 英語やそれ以外の言語のスペルチェックのほか、日本語の構文チェックしてくれて、なかなか優秀そうなツールです。

利用可能なルールがまとめられており、ざっと数えると 70 個ほどのルールがあります。

https://github.com/textlint/textlint/wiki/Collection-of-textlint-rule

この中から使えそうなものをピックアップします。 まずは、最大の目的であるスペルチェックに絞って調査しました。

スペルチェックができるルール

スペルチェックができるルールは 5 つほどあります。

textlint-rule-common-misspellings

Wiki に書いてある、よくあるスペルミスをチェックするルールです。

改行コードが CRLF だとエラーを検知してくれないときがあったり、 LF でも検知してくれないケースがありました。 例えば以下のようなパターンで検知してくれませんでした。

{CRLF}
Tihs{CRLF}
{LF}
Tihs is a pen.{LF}

ただ、オフィシャルサイトだとうまく動いているので、使い方が悪かっただけかもしれません。

上記のような問題があったのと、そもそも Wiki にまとめられる程度の単語ではチェックする単語の数が少ないのではないかと思い、利用は見送りました。

textlint-rule-spelling

https://github.com/wooorm/dictionaries に定義されている辞書ベースにチェックするルールです。

英語以外にいろんな言語のチェックをしてくれます。日本語辞書は存在しません。 英語のスペルチェックの場合は dictionary-en を利用するようです。

このルールは数値の 70 などを 0, 7 へ修正するように指示したり、よくわからない指摘をしてくれますが、 他と比べてこれがもっともよかったので、今回はこれを採用しました。

textlint-rule-en-spell

英語に特化したスペルチェックルールです。

textlint-rule-spelling と同様で dictionary-en の辞書を利用するようです。 英語限定でスペルチェックするのであれば、textlint-rule-spelling よりこちらのほうがシンプルでよさそうですが、 Tihs is a pen. っていう単純なスペルミスを検知してくれなかったので textlint-rule-spelling のほうを採用しました。

textlint-rule-ginger

外部サービスである Ginger Proofreading を利用したルールです。

おそらく、スペルチェックとしては優秀でしょうが、このサービスは無料プラン以外に有料プランもあり、将来的な不安があるので未検証です。

textlint-rule-spellchecker

ネイティブ(OS?)の機能を利用してスペルチェックするルールです。

これが使えるならこれが一番いいような印象ですが、Python がないと使えないので未検証です。

スペルチェック以外に使えそうなルール

スペルチェックは textlint-rule-spelling を採用しましたが、textlint にはスペルチェック以外に多くのルールがあります。

検証結果をまとめました。

textlint-rule-prh

表記ゆれをチェックするルールです。

Microsoft Word であれば、1 ファイル内に「フォルダ」と「フォルダー」が混在するような場合に警告を出しますが、 こちらは決まったルールでしかチェックしてくれないようです。

例えば、「プライマリー」という単語は必ず「プライマリ」へ修正するよう指摘します。

少し期待外れだったことと、以下のような指摘もあり、利用を見送りました。

-表記ゆれの修正に特化したルール
+表記ゆれの修まさに特化したルール
-正解ということもない
+正解というこ伴

このルールは定義を指定する必要があり、今回の検証で利用した定義ファイルは https://github.com/prh/rules/blob/master/media/WEB%2BDB_PRESS.yml です。 定義を自分好みに作成すれば利用はありかもしれません。

textlint-rule-web-plus-db

textlint-rule-prh と同じ表記ゆれをチェックするルールです。

textlint-rule-prh とは異なりルールで利用するファイルが固定化されており、こちらは非推奨となっています。 textlint-rule-prh と同様、利用は見送りました。

textlint-rule-ng-word

NG ワードを定義するしてチェックするルールです。

利用したかったのですが、チェックで指摘する行と実際の行が違うので、利用を見送りました。

本ブログを書いている時点で、オフィシャルサイトには以下のように textlint-rule- が 2 回書かれていて、なんとも微妙な状況です。

npm install textlint-rule-textlint-rule-ng-word

textlint-rule-preset-jtf-style

「JTF 日本語標準スタイルガイド」をもとにチェックするルールです。

オフィシャルサイトの通りだと以下をチェックすると思われます。

  1. 本文を、敬体(ですます調)あるいは常体(である調)のどちらかに統一する。
  2. 句読点は「、」と「。」を使う。
  3. 常用漢字表にある漢字を主に使用する。
  4. 動詞の送りがなは本則に従う。
  5. カタカナ語の語尾の長音は省略しない。
  6. 長いカタカナ複合語は中黒または半角スペースで区切る。
  7. 漢字、ひらがな、カタカナは全角で表記する。
  8. 数字とアルファベットは半角で表記する。
  9. 原則として記号類は全角で表記する。
  10. 半角文字と全角文字の間に半角スペースを入れない。
  11. ピリオド(.)、カンマ(,)、スペースは半角で表記する。
  12. 単位の表記を統一する。

この中で個人的に一番使いたいルールは カタカナ語の語尾の長音は省略しない。 ですが、 実際に利用してみるとチェックしてくれなかったので利用を見送りました。

textlint-filter-rule-allowlist

チェックでヒットした単語をエラー扱いにしないためのフィルターです。

スペルチェック機能ではよくある機能だと思いますので利用することにしました。

textlint-filter-rule-comments

特定の範囲の文章のチェックしないようにするためのフィルターです。

<!-- textlint-disable --><!-- textlint-enable --> のコメントを入れることになりますが、 この HTML コメントを無視して描画するか、そのまま描画するかはマークダウンの描画処理次第なので利用を見送りました。

textlint についてのまとめ

textlint には多くのルールがありますが、最終的に利用すると決めたのは以下の 2 つだけです。 ずいぶん時間をかけて調べましたが、こうなってくると、textlint にこだわらないほうが良かったかもしれません。

  1. textlint-rule-spelling
  2. textlint-filter-rule-allowlist

最後に textlint をブラウザから呼び出す必要がありますが、textlint はブラウザで動作させることを想定していないみたいです。 ブラウザで動作させている記事も見つけましたが、今回は Firebase の Cloud Functions にデプロイし API として公開する対応をしました。 それなりに重い処理をしているのでパフォーマンスが悪いです。 性能を上げるにはお金がかかるので、パフォーマンスを上げる予定はありません。

で、出来上がった API は以下になります。 認証しないと使えない内部的な API ですので詳細(パラメータやレスポンス)については割愛します。

https://asia-northeast1-markdown-60249.cloudfunctions.net/lint

まとめ

Markdown Note Apri のスペルチェック機能について記載しました。 ブラウザ上で動作する JavaScript ライブラリって意外にないんですね。