はじめに
mdBookとはRust製のドキュメントビルダーで、Markdownのドキュメントをいい感じのHTMLなどにレンダリングできます。
Rustの公式ドキュメント・非公式ドキュメントで広く使われているのですが、現状日本語での検索ができません。
なぜ日本語で検索できないかを少し調べてみたところ、各コンポーネントは微妙に日本語対応しているものの、
全体として整合性が取れる状態にはなっていない、という感じでした。
今回、いろいろなものにパッチを当てることで、「日本語検索できるように見える」状態まで持ってくることができました。
また、完全な日本語検索に向けての課題も見えてきたので共有しておきます。
mdBookの検索
まず検索周りで使われているコンポーネントは以下のようになっています。
- mdBook: mdBook本体
- elasticlunr-rs: mdBookから呼んでいるelasticlunr.js用のインデックス生成クレート
- elasticlunr.js: JS製の全文検索エンジン
さらに日本語検索にかかわるコンポーネントが以下です。
- lunr-languages: elasticlunr.jsの多言語対応モジュール
検索の流れは以下の通り。
-
mdbook build
にてmdBookがelasticlunr.js互換のインデックスファイル(searchindex.js)を生成する - ブラウザで読んだときにsearcher.jsが上で生成したsearchindex.jsをロードして、elasticlunr.jsで検索する
パッチを当てて日本語検索させる
mdBook
まずmdBook本体からです。
$ git clone https://github.com/rust-lang/mdBook
として2つのファイルにパッチを当てます。
1つ目は以下の通りCargo.tomlにてelasticlunr-rsにja
フィーチャを追加します。
diff --git a/Cargo.toml b/Cargo.toml
index e0a2b90..2f28447 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,7 +46,7 @@ staticfile = { version = "0.5", optional = true }
ws = { version = "0.9", optional = true}
# Search feature
-elasticlunr-rs = { version = "2.3", optional = true, default-features = false }
+elasticlunr-rs = { version = "2.3", optional = true, default-features = false, features = ["ja"] }
ammonia = { version = "3", optional = true }
[dev-dependencies]
2つ目はelasticlunr::Index
を生成しているところで、Language::Japanese
を指定します。
diff --git a/src/renderer/html_handlebars/search.rs b/src/renderer/html_handlebars/search.rs
index f184a59..9cc7519 100644
--- a/src/renderer/html_handlebars/search.rs
+++ b/src/renderer/html_handlebars/search.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::Path;
-use elasticlunr::Index;
+use elasticlunr::{Index, Language};
use pulldown_cmark::*;
use crate::book::{Book, BookItem};
@@ -13,7 +13,7 @@ use crate::utils;
/// Creates all files required for search.
pub fn create_files(search_config: &Search, destination: &Path, book: &Book) -> Result<()> {
- let mut index = Index::new(&["title", "body", "breadcrumbs"]);
+ let mut index = Index::with_language(Language::Japanese, &["title", "body", "breadcrumbs"]);
let mut doc_urls = Vec::with_capacity(book.sections.len());
for item in book.iter() {
この状態でビルドしてmdbook
コマンドをインストールします。
(ローカルでmdbook
を使っている方は上書きしてしまうのでご注意ください)
$ cargo install --path .
ビルド
先ほど改造したmdbook
を使って普通にビルドします。
$ cd XXX
$ mdbook build
lunr-languagesの導入と改造
lunr-languagesから以下のファイルを落としてきて、mdbook
が生成したdocs
直下に置きます。
さらにlunr.jp.jsに以下のようにパッチを当てます。
99c99
< lunr.Pipeline.registerFunction(lunr.jp.stemmer, 'stemmer-jp');
---
> lunr.Pipeline.registerFunction(lunr.jp.stemmer, 'stemmer-ja');
109a110,113
>
> lunr.jp['wordCharacters'] = '一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9';
> lunr.jp.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.jp.wordCharacters);
> lunr.Pipeline.registerFunction(lunr.jp.trimmer, 'trimmer-ja');
さらにindex.htmlの最後の方のelasticlunr.min.jsを読んでいる部分に以下のように追記します。
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="lunr.stemmer.support.js" type="text/javascript" charset="utf-8"></script>
<script src="tinyseg.js" type="text/javascript" charset="utf-8"></script>
<script src="lunr.jp.js" type="text/javascript" charset="utf-8"></script>
これでとりあえずindex.htmlから日本語検索できるようになりました。
検索結果の例
例えば手元にある"Rust by Example 日本語版"だと以下のようになります。
いろいろな不具合
とりあえずこの状態で日本語検索できるように見えますが、いろいろと不具合があります。
- 検索結果のハイライトがおかしい。おそらく英語前提で半角スペース区切りを元にハイライトを決めているので。
- 3文字以上の漢字が検索できない。例えば「構造」はOKだが「構造体」はNG、といった感じ。詳しい条件は不明。
- 英語が検索できない。
特に3つ目はプログラミング系のドキュメントの検索としては割と致命的ですが、直すのは結構大変そうです。
完全な日本語検索に向けて
完全な日本語検索に向けて、以下のようなことをやればいいのではないかというリストを挙げておきます。
lunr-languagesのPRをマージ
lunr.jp.jsで当てたパッチは1年ほど前にPRが出ているものの放置されているものです。
すでにelasticlunr.js/lunr-languagesの作者の方はあまりアクティブではないようで、
elasticlunr.jsをOrganization管理にしたいというIssueが立っています。
正規の手順を踏むなら、できたOrgにlunr-languagesも移動、当該PRをマージしてもらう、という感じがいいでしょうか。
elasticlunr-rs と lunr-languagesの日本語名をそろえる
現状elasticlunr-rsで日本語はja
、lunr-languagesではjp
となっています。
lunr.jp.jsで当てたパッチの一部はこの不整合を解消するためでした。
分かれている経緯が不明なので、どちらに寄せるべきかはよく分かりません。
(追記:2020/6/23)
某所でご指摘いただいたのですが、言語コードとしてはja
が適切なのではないか?とのことでした。
https://ja.wikipedia.org/wiki/ISO_639-1%E3%82%B3%E3%83%BC%E3%83%89%E4%B8%80%E8%A6%A7
そのあと気づいたのですが、ja
に変更するのが正しいですね。
ここで指しているlunr-languagesはelasticlunr.js用にフォークしたものなのですが、
フォーク元の本家lunr-languages(こちらはlunr.js用)ではja
とjp
両方あって、
もともとjp
だったのを言語コード的に正しいja
に変更した、となっています。(jp
は互換性のために残してある)
なのでelasticlunr.js用lunr-languagesも本家に追従することでこの問題を解決するのが正しそうです。
elasticlunr-rsに多言語対応を入れる
elasticlunr.jsとしては多言語(つまり英語+日本語)対応しているようですが、
elasticlunr-rs側が(Issueは立っているのですが)対応していません。
基本的には多言語時のelasticlunr.jsのインデックスの構造を調べて、そのように出力すればいいはずですが…。
mdBookに検索言語選択機能を入れる
おそらくbook.tomlの設定にいれるのがいいでしょう。
ただし日本語検索のために結構依存クレートが増えるので、フィーチャは切った方がいいかもしれません。
変更箇所は、パッチを当てたIndex
の部分と、HTML/JSの生成部分です。
まとめ
というわけでmdBookで日本語検索するにはいろいろとハードルがあることが分かりました。