久々に4連休となったので、たまには頻度高めにブログを書いてみることにします。

今回は、前回SKKにしたということを書きましたが、それの辞書サーバーについて書いてみようと思います。

SKKと辞書サーバー

SKKを使ったことがない方は、辞書サーバーといってもなんじゃそりゃ?となるでしょう。辞書サーバーとはそのままの意味で、SKKの辞書を提供するためのサーバープログラムを指します。

SKKの実装では、大抵はSKK辞書をインプットメソッド内にメモリとして展開し、それを利用しています。当然ながら、この形式では複数のインプットメソッドがあったら、各々でメモリを消費してしまいますし、管理が煩雑になりがちです。

そのため、SKKのほとんどの実装では、SKKプロトコルというプロトコルにもとづいたサーバーも辞書の一つとして利用できるようになっています。サーバーとして一つにまとめることで、複数のインプットメソッドから同時に利用することもできる、ということです。

ddskkでの辞書サーバーの設定

辞書サーバー、使う?

さて、辞書サーバーを使うといいことあるじゃん・・・と思いたいところですが、実際は中々そうはいきません。なぜかというと、使ってみたり運用してみた限りでは、以下のような問題があります。

一個ずつ見ていきます。

SKKプロトコルの問題

SKKが開発されたのは1987年、佐藤雅彦氏によって開発されたのが初版とされています。 参考

当然ですが、1987年当時にはUnicode consortiumすらありません。(Wikipedia)なので、日本語を扱える文字コードは、事実上 EUC-JPとShift-JISの2強 でした。Linuxにおいては、EUC-JPがデファクトスタンダードの地位を確立していた(らしい)ので、日本語のかな漢字変換プログラムであるSKKがEUC-JPを前提としていても、何の不思議もありません。

さて、しかし時は経ち、現代ではUTF-8がデファクトスタンダードとなりました。そうなると困るのは、EUC-JPでしか扱えない、というプロトコルの問題です。これはSKKプロトコルの定義を修正しない限りはなんともならないです。事実、macOSで一番使われていると思われるAquaSKKでも、serverとのやりとりはEUC-JPに固定されています(辞書はUTF-8も扱えます)。

なので、辞書がUTF-8だったり、エンコーディングがUTF-8だったりすると、上手く変換できなかったりと、問題が発生しがちです。

複数のインプットメソッドからだとめんどくさい問題

プロトコルの問題でも上げましたが、辞書と辞書サーバーの文字コード、インプットメソッド内での扱いなどが異なる場合がある、などの事情があります。なので、以外とサーバーをそのまま利用できるケース、というのは少なかったりします。

仕事用のmacOSで、AquaSKKとEmacsで共通の辞書サーバーを使おう、と思ったりしましたが、このへんが上手くいかずに挫折したりしています。

そもそもメモリが多くなった

1990年くらいのPCは、メモリがMB単位とかあるとそれだけですげー、ってなった時代でした。今は2桁GBがあたりまえです(個人の感想です)。個人所有しているPCも、32GBとか積んでいます。

片や、SKKの辞書は、他のIMEと比較してもかなり小さいほうだと思います。実際に計測してみたら、SKKが配布している辞書を全部統合した辞書(コンパイル後)で、22MBしかありませんでした。

32 * 1024MB のメモリ空間があるところに、高々数十MBの辞書をメモリに展開したところで、ほとんど影響がないのは明白でしょう・・・。

それでも辞書サーバーを使ってみる

色々問題があるにはある辞書サーバーですが、それでも使ったことがないから使ってみたい、というのは人の性でしょう。ですので使ってみます。

さて、SKKには歴史があるので、当然ながら辞書サーバーも色々な実装があります。ここで挙げるのは蛇足なので、 Wikiへのリンクを貼っておきますので、気になる方はこちらから。

Google IME≒mozcを利用して、候補を取得したりする・・・という変わり種もあったりしますが、そこまで変わり種を使いたいわけでもないので、シンプルな辞書サーバーを選択してみます。

実際、速度の面などを考えると、C/C++で作られたサーバーがいいかな・・・と最初は考えました。 yaskkservは、かなりこなれた実装でもあり、かつGentooでも使えるものです。

が、これもまた歴史のあるツールなので、色々内部構造的な問題がある、ということで、作者の方がRustでリライトした yaskkserv2というのを開発されています。

ちょうどFirefoxとかをビルドしている関係上、Rustがマシンに入っているということもあり、これを使うことにしました。(見切り発車すぎる)

yaskkserv2のビルドとか

これらは、Emacsの起動時に、対象のプログラムが存在していなければビルドしてインストールするようにしました。ただ、ビルド環境が無い場合は、バイナリを落として展開するようにしています。

(leaf *skk-server
  :after f
  :if my:use-skkserver
  :init
  (let ((server-program (expand-file-name "yaskkserv2"  my:user-local-exec-path))
        (dictionary-program (expand-file-name "yaskkserv2_make_dictionary" my:user-local-exec-path)))
    (cond ((and my:build-skkserver
                (executable-find "cargo")
                (not (executable-find server-program))
                (not (executable-find dictionary-program)))
           (let ((base-path "/tmp/yaskkserv2"))
             (unless (f-exists? base-path)
               (call-process "git" nil nil t  "clone" "https://github.com/wachikun/yaskkserv2" "/tmp/yaskkserv2"))
             (call-process "cargo" nil nil t "build" "--release" "--manifest-path" (expand-file-name "Cargo.toml" base-path))
             (unless (f-exists? server-program)
               (f-copy (expand-file-name "target/release/yaskkserv2" base-path) server-program))
             (unless (f-exists? dictionary-program)
               (f-copy (expand-file-name "target/release/yaskkserv2_make_dictionary" base-path) dictionary-program))
             ))
          (t
           (let* ((target (cond ((eq window-system 'ns) "apple-darwin")
                                (t "uknown-linux-gnu")))
                  (path (format "https://github.com/wachikun/yaskkserv2/releases/download/%s/yaskkserv2-%s-x86_64-%s.tar.gz" my:yaskkserv2-version my:yaskkserv2-version target)))
             (call-process "curl" nil nil t "-L" path "-o" "/tmp/yaskkserv2.tar.gz")
             (call-process "tar" nil nil t "-zxvf" "/tmp/yaskkserv2.tar.gz" "-C" my:user-local-exec-path "--strip-components" "1"))))))

辞書のコンパイル時の注意

yaskkserv2は、独自の辞書形式を利用しているため、SKKの辞書はそのまま利用せず、 yaskkserv2_make_dictionary というツールから変換する必要があります。

このとき、引数に渡す辞書の順で、変換候補の順序が概ね決まるような実装になっているため、下手に人名辞書とかを SKK-JISYO.L より前にもってきたりすると、変換候補の選択でムキーってなります(した)ので、SKK-JISYO.Lだけは明示的に先頭に指定するのをお勧めします。

$ yaskkserv2_make_dictionary --utf8 --dictionary-filename <辞書の位置> SKK-JISYO.L <それ以外>

Emacs側での設定の必要性

https://github.com/wachikun/yaskkserv2#utf-8-dictionary

yaskkserv2のReadmeでも言及されていますが、UTF-8の辞書を利用する場合には、ddskk側の振舞いをハイジャックして、EUC変換を行わないようにしておく必要があります。これをやらないとそもそも変換できねーです。

わたしのddskkの初期化では、以下のようにしています。ほとんど上記のリンクで言及されている方法と一緒です。これが、他のインプットメソッドと辞書サーバーを共有できなかった理由でもあります。

(cond (my:use-skkserver
       (setq skk-server-host "localhost"
             skk-server-portnum "1178"
             skk-large-jisyo nil)
       (defun skk-open-server-decoding-utf-8 ()
         "辞書サーバと接続する。サーバープロセスを返す。 decoding coding-system が euc ではなく utf8 となる。"
         (unless (skk-server-live-p)
           (setq skkserv-process (skk-open-server-1))
           (when (skk-server-live-p)
             (let ((code (cdr (assoc "euc" skk-coding-system-alist))))
               (set-process-coding-system skkserv-process 'utf-8 code))))
         skkserv-process)
       (setq skk-mode-hook
             '(lambda()
                (advice-add 'skk-open-server :override 'skk-open-server-decoding-utf-8))))
      (t
       (setq skk-get-jisyo-directory (expand-file-name "skk-jisyo" user-emacs-directory)
             skk-large-jisyo (expand-file-name "SKK-JISYO.L" skk-get-jisyo-directory))))

SKKたのしいです

辞書サーバーを使えたり使えなかったり、という問題はありますが、個人のWorkstationでは問題なくddskkを利用できるようになりました。SandSをがっつり利用するようにしているので、小指とかの負荷はまだそれほどでもないですね。

AZIKにも大分慣れてきて、徐々に拡張を利用できたりするようになってきました。しゃ、ちょ、などの拗音が3キーではなく、2キーで入力できるようになるのはやはり大きいですね。あと、ひらがな入力がダイレクトにできるというのは楽で、いちいちEnterを押さなくていい、というのは、これはこれで親指とかに対して優しいですね。

ライブ変換とかで変換しなくていいところで変換されて、それを再学習させるのにイラッと来たことがある方(自分)とか、一度試してみてはいかがでしょうか?毎回区切りを自分で指定するのは面倒でもありますが、自分で書いている、という手書き気分が味わえるので、作文とかには意外と向いていると思います。

今回は書いていない&試していないですが、Google IMEから候補を取得するようにしたりすると、連文節変換のような勢いで変換することもできる、という話です。(yaskkserv2はデフォルトでこの機能が有効になっています)

それではよい日本語入力ライフを。