Pythonを使って、BOOTHで販売している英辞郎のテキストデータをMDict化し、BOOX Leaf2 の辞書に登録してみた

去年くらいから、 E-Inkディスプレイ搭載のAndroid端末 BOOX Leaf2 で、KindleO’Reillyアプリの電子書籍を読んでいます*1
BOOX Leaf2 – SKT株式会社

普通のAndroidタブレット電子書籍を読むのに比べたら、 Leaf2 は目はあまり疲れない感じです。

また、KindleO’Reillyアプリは起動時がややもっさりしますが、一度起動してしまえばそこまでのストレスはありません。

 
ところで、O’Reillyアプリでは洋書も読んでいるものの、「この単語は何だっけ...?」となることがよくあります。

Leaf2 に辞書機能はあるものの、その中には英和辞書がありません。

辞書を追加する方法がないか調べたところ、BOOXのヘルプページに以下の記載がありました。

To add a dictionary to your device, do the following:

  1. Search and download the dictionary files on your computer.
  2. Unzip the downloaded files and put them in a folder.
  3. Sideload the folder to the "dicts" folder in the root directory of your device.
  4. In Home Screen, go to Apps and open the Dictionary app.
  5. Tap the hamburger menu in the upper right corner.
  6. Choose Preferred Dictionary Setting and tick to add the dictionary.

 
https://help.boox.com/hc/en-us/articles/10701464167316-Translation-and-Dictionary

 
また、Leaf2の辞書アプリにも、辞書を追加できそうな記載がありました。

Tips

 

  1. StarDict、MDict、Deep Blue Dictionary、Babylonおよび他の辞書ファイル形式をサポートします
  2. デフォルトの保存場所: storage/dicts/、または外部ストレージ/dicts/
  3. 各辞書ファイルは、zipやサブフォルダを含まないフォルダに保存する必要があります

 
さらにWebを調べてみると、BOOXの別の端末に英和辞書を追加している記事がありました。
boox noteのPDFリーダに英和辞書を追加する - 工学おじさんのブログ

また、同じ著者で、Pythonを使って英辞郎のテキストデータをMDict形式にして、BOOXの端末に入れている記事もありました(以降、 参考記事 と表記します)。
英辞郎をMDict形式へ変換する - 工学おじさんのブログ

「参考記事に従えば英和辞書を追加できるかも」と感じたため、次に英辞郎のデータが販売されていないか調べたところ、BOOTHにありました。
英辞郎 Ver.144.9(2024年1月10日版)のテキストデータ - EDP - BOOTH

このBOOTHサイトへは英辞郎公式サイトからもリンクがあり、公式で提供しているデータのようでした。
英辞郎(えいじろう・EIJIRO)の最新情報

 
そこで、英辞郎のテキストデータからBOOX Leaf2向けの和英辞書を作ってみたので、メモを残します。

 
目次

 

環境

  • Python 3.12.1
  • WSL2
  • 英辞郎 Ver.144.9(2024年1月10日版)のテキストデータ

 

英辞郎のテキストデータを購入

以下のBOOTHページより、テキストデータを購入しました。
英辞郎 Ver.144.9(2024年1月10日版)のテキストデータ - EDP - BOOTH

購入後、zipファイルをダウンロードしておきます。

ちなみに、BOOTHに書かれていたファイル情報は以下の通りです。

●この圧縮ファイル(EIJIRO-1449.ZIP)(サイズ=48,595,404 バイト)をダウンロードして、ZIPを用いて展開すると、以下のテキストファイルが復元されます。

 
ファイル名: EIJIRO-1449.TXT

サイズ: 152,940,714バイト

論理行数: 2,575,101

改行コード: CR+LF

日本語文字コード: Shift JIS

 

ツール類の調査

テキストファイルをMDict化するライブラリ writemdict を確認

参考記事では、以下のライブラリを使って英辞郎のテキストデータをMDict化していました。
zhansliu/writemdict: A library for writing dictionary files in the MDict (.mdx) format

 
リポジトリを見たところ

という状態でした。

 
また、参考記事では

Clone or download→Download ZIPでダウンロード&解凍をお願いします。

と書かれていました。手動で使うことを前提にしたライブラリのようです。

そのため、変換処理を自動化するPythonスクリプトwritemdict を import して使うのは厳しそうでした。

 

MDictフォーマットに変換できるライブラリを調査

続いて、MDictをフォーマットに変換できるライブラリを調査してみました。

 
まずは mdict-utils です。
liuyug/mdict-utils: MDict pack/unpack/list/info tool

starがそこそこあり、「MDict pack/unpack tool」と書かれていることから期待が持てました。

ただ、実際に使ってみると

  • MDictへの変換については、writemdict.py を取り込んでいる
  • ただ、 writemdict.py は拡張されており、単に差し替えただけでは動作しなかった
  • READMEのUsageを見ると、ライブラリというよりはツールの雰囲気
    • mdict コマンドの実行例しか記載されていないため

と、使いこなすのには時間がかかりそうでした。

 
次は pyglossary を見てみます。
ilius/pyglossary: A tool for converting dictionary files aka glossaries. Mainly to help use our offline glossaries in any Open Source dictionary we like on any modern operating system / device.

ただ、READMEによると、MDictファイルへの書き込みには対応していないようでした。

 
他にはPythonでMDictを扱えるようなライブラリは見当たりませんでした。

 

MDictフォーマットについて調査

そもそもMDictフォーマットとは何か分かってなかったので調べたところ、2016年時点の記事がありました。辞書のフォーマットは非公開のようです。
EBWin EBPocket のMDict対応検討 - hishidaの開発blog

次に、MDictの公式サイトを見に行きました。
https://www.mdict.cn/wp/?page_id=5325&lang=en

ただ、アプリはあるものの、ソースコードやファイルフォーマットが公開されていませんでした。

 
これらより、MDictのライブラリを自作するのも容易ではなさそうと感じました。

 

StarDictについて調査

ところで、Leaf2 がサポートしている辞書は、前述の通り

  • StarDict
  • MDict
  • Deep Blue Dictionary
  • Babylon
  • その他

のようです。

このうち、 Deep Blue DictionaryBabylon は調べてみても出てきませんでした。

一方、 StarDict については、いくつかの記事でふれられていたので、少し調べてみることにしました。

 
まず、以下のサイトでは、英辞郎をStarDict形式に変換するスクリプトが公開されていました。
英辞郎をStarDict形式に直接変換するスクリプト「eiji2sd」 - 実録コンピュータ物語

ただ、言語が PowerShellPerl のようで、そのまま使うのは難しそうでした。

 
次に、Pythonpenelope パッケージを使うことで StarDict 形式にも対応できそうな記事がありました。
フリーの辞書をKindle用辞書に変換: ドイツ語の本読んでみ・・

そこで、 penelope を見に行くと 2018年にアーカイブされていました。
pettarin/penelope: Penelope is a multi-tool for creating, editing and converting dictionaries, especially for eReader devices

READMEにはImportant Updateが記載されており、「メンテナンスされていないこと」や「PyGlossaryが使えるかもしれないこと」が記載されていました。

そのため、 penelope を使うのは厳しそうでした。

 
最後に、StarDict形式については規格の行方が厳しそうな話もありました。
StarDict 計画 進捗(2) - hishidaの開発blog

そのため、StarDictでなんとかする方向はやめておきました。

 

英辞郎のテキストファイルをMDict化するライブラリ eiji_to_mdict.py を確認

ありがたいことに、参考記事ではMDict化するライブラリ eiji_to_mdict.pyソースコードがGistで公開されていました。
英辞郎をMDict形式に変換します。

ただ、参考記事にもあるように、 writemdictディレクトリの中に入れて使う想定で作られています。

そのため、importした時に処理が走ってしまうことから、これも別のPythonスクリプトimport して使うのは厳しそうでした。

 

実装方針:両ライブラリをfork・修正する

ここまでの調査で

  • 参考記事にある両ライブラリを import して、そのまま使うのは厳しい
  • 容易な代替案もない

ということが分かりました。

 
そのため、方針案としては

  • 参考記事にあるように、Python 3系の古いバージョンをインストールし、手動で実行
  • 両ライブラリをforkして修正を加え、ある程度自動で変換できるようにする

あたりを考えました。

ただ、辞書の更新を考えると、なるべく自動でMDict化したくなりました。

 
そこで、後者の案をもとにした

  • writemdict をforkし、pip installできるように修正
  • eiji_to_mdict をforkし、英辞郎をMDict化するコマンド eiji_to_mdict を用意

という方針で進めることにしました。

 
ちなみに、英辞郎のデータ仕様は公開されているため、多少修正を加えても仕様を満たしていれば問題なく動作しそうです。
英辞郎の紹介:データ仕様

 
以降では、修正作業を行っていきます。

 

writemdict をforkして修正

writemdict に対しては

  • 自分のGithubアカウントへforkする
  • ブランチ feature/python_package にて、必要な修正を実施する

という流れで作業します。

なお、fork元がアクティブでないことから、自分のリポジトリでは、ブランチ feature/python_packagemaster へマージせずに置いておきます。

 

廃止される cgi.escape を html.escape へ差し替える

cgi.escape の代わりになるモジュールを探したところ、PythonWikiに以下の記載がありました。

The cgi module that comes with Python has an escape() function:

However, it doesn't escape characters beyond &, <, and >. If it is used as cgi.escape(string_to_escape, quote=True), it also escapes ".

Recent Python 3.2 have html module with html.escape() and html.unescape() functions. html.escape() differs from cgi.escape() by its defaults to quote=True:

 

https://wiki.python.org/moin/EscapingHtml

 
両者ではエスケープするときの挙動がほんの少しだけ異なるようです。

ただ、PyCQA/banditのプルリクでもそのまま置き換えている感じだったので、今回の用途でもそのまま置き換えてしまっても良さそうでした。
Use html.escape() instead of cgi.escape() by ericwb · Pull Request #339 · PyCQA/bandit

 

pip installできるよう、writemdictディレクトリの作成とpyproject.tomlを用意

pip installを可能にするため、最低限の定義を pyproject.toml に記載します。

また、Pythonのパッケージとして扱えるよう

とします。

 
ただ、 writemdictリポジトリでは、ルートディレクトリに example_output というディレクトリがありました。

このままだと

  • writemdict
  • example_output

の2つのディレクトリがルートディレクトリに並んでしまい、pip installする時に失敗してしまいます。

そこで、

you can explicitly list the packages in modules:

[tool.setuptools]
packages = ["my_package"]

 

Configuring setuptools using pyproject.toml files - setuptools 69.0.3.post20240124 documentation

とあるように、 [tool.setuptools] テーブルの packages を利用して、 writemdict がパッケージ向けであることを明示化します。

 
上記を踏まえた pyproject.toml は以下となりました。

[project]
name = "writemdict"
version = "0.1"
requires-python = ">=3.12"
license = {file = "LICENSE"}

[tool.setuptools]
packages = ["writemdict"]

 

writemdict.pyでのimportを相対importにする

writemdict.py の冒頭で ripemd128pureSalsa20

from ripemd128 import ripemd128
from pureSalsa20 import Salsa20

としてimportされているため、パッケージ化したときにこのままではうまく動きませんでした。

 
そこで、 . をつけて相対importできるようにしました。

from .ripemd128 import ripemd128
from .pureSalsa20 import Salsa20

 

eiji_to_mdict をforkして修正

eiji_to_mdict.py はGistにあるので、fork後、Githubリポジトリを作成しました。

今回の修正方針は以下としました。

 
以降では、やったことを簡単なメモに残しておきます。

実装の詳細は、リポジトリにあるソースコードを参照してください。
https://github.com/thinkAmi/eiji_to_mdict

 

pyproject.tomlの作成

ここでも pyproject.toml は必要最低限の定義とします。

 

依存ライブラリを dependencies に指定する

今回作成する eiji_to_mdict を実行する場合、forkした writemdict が必要になります。

そこで、 pyproject.toml の dependencies に、forkした writemdict をパッケージ化したブランチを指定しておきます。
Install dependencies from GitHub with pyproject.toml or requirements.txt — Chris Holdgraf

これにより pip install eiji_to_mdict した際に、合わせて writemdict もインストールされるようになります。

 

eiji_to_mdict コマンドを使えるよう、project.scripts テーブルを定義する

前回の記事に書いた通り、 [project.scripts] テーブルを用意することで、コマンドを使えるようにします。
Pythonで、実行時のmオプションやpyproject.toml の project.scripts の指定による、実行可能なライブラリを作ってみた - メモ的な思考的な

 

pyproject.toml の全体像

[project]
name = "eiji_to_mdict"
version = "0.1"
requires-python = ">=3.12"
dependencies = [
  "writemdict@git+https://github.com/thinkAmi/writemdict@feature/python_package",
]


[project.scripts]
eiji_to_mdict = "eiji_to_mdict:main"

 

元々のソースコードの修正

作業としては

  • 変換ロジックまわりは eiji_to_mdict.py ファイルに移動する
  • その他の処理は main.py ファイルに書く

となります。

 

変換ロジックまわり

元々のソースコードでは、ファイルを1行ずつ読み込み変換していました。

そこで、関数 update_dicts を用意して、その部分を移植しました。

なお、副作用には目をつぶり、引数の dict を更新するようにしています。

 

その他の処理の実装

コマンドの引数としてzipファイル名を受け取る方法について

元々は sys.argv を使っていましたが、今回は argparser を使いました。

 
最近だと click が便利なようですが、今回は標準ライブラリだけにしたかったので、使いませんでした。
Welcome to Click — Click Documentation (8.2.x)

 

ファイルの存在チェックについて

いちおうバリデーションしておこうということで、 pathlib を使ってファイルの存在チェックを行いました。
https://docs.python.org/ja/3/library/pathlib.html#pathlib.Path.exists

 

zipファイルの扱いについて

標準ライブラリの zipfile ではzipアーカイブをいい感じに処理できます。
https://docs.python.org/ja/3/library/zipfile.html

今回はここらへんを使いました。

 
また、 ZipFile.open() のコンテキストマネージャで取得したファイルライクオブジェクトは、 io.TextIOWrapper でいい感じに扱えます。

そのため、zipファイルまわりはこんな感じになります。

with zipfile.ZipFile(path) as zf:
    originalFileName = zf.namelist()[0]
    
    with zf.open(originalFileName) as tf:
        for line in io.TextIOWrapper(tf, encoding='Shift-JIS', errors='ignore'):
            update_dicts(line, d, idiom)

 

動作確認

ここまでで実装が終わったので、WSL2上で動作確認します。

 

英辞郎のテキストデータをMDict形式に変換するところまで

eiji_to_mdict コマンドにてMDictファイルが作成されているかを確認します。

$ python --version
Python 3.12.1

$ python -m venv env
$ source env/bin/activate

(env) $ python -m pip install -e .

# Windowsのダウンロードディレクトリにあったので、コピーする
(env) $ cp /mnt/c/Users/<UserName>/Downloads/EIJIRO-1449.zip .


# 実行
## デバッグ用のコードが入っているので、それも出力される
$ eiji_to_mdict EIJIRO-1449.zip
<副> 内部で、内側は
...
処理を終了します


# mdxファイル化できたことを確認
$ ls
EIJIRO-1449.zip  eijidiom.mdx  eijiro.mdx ...

 

BOOX Leaf2 で辞書を設定して確認

今回作成した単語辞書(eijiro.mdx)と熟語辞書(eijidiom.mdx)を、Leaf2の辞書に設定し、O’Reillyアプリで使ってみます。

 
まずは、Windows側に両ファイルをコピーします。

$ cp eijiro.mdx /mnt/c/Users/<UserName>/Downloads/
$ cp eijidiom.mdx /mnt/c/Users/<UserName>/Downloads/

 
続いて、Leaf2に辞書ファイルを転送するため、Leaf2の BooxDrop アプリを使います。
Transfer with Your Computer – BOOX Help Center

転送先として Internal Storage > dicts を開きます。

次に

の2つのディレクトリを作成し、その中に各ファイルを転送します。

 
転送が終わったら辞書アプリを起動し、優先する辞書を設定します。
Translation and Dictionary – BOOX Help Center

  1. 右上のハンバーガーメニューをタップ
  2. 優先辞書設定をタップ
  3. EijiroEijideom チェックを入れる
  4. 辞書アプリを閉じる

 
最後に、O’Reillyアプリを起動して動作確認します。

単語を選択、 辞書 をタップすると翻訳が表示されました。

やや表示が崩れてそうな気もしますが、実用上は問題なさそうです。

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi/eiji_to_mdict

今回のプルリクはこちら。
https://github.com/thinkAmi/eiji_to_mdict/pull/1

*1:現在では後継のBOOX Pageが販売中のようです: https://sktgroup.co.jp/boox-page/