deepl コマンド
DeepL という精度の高い翻訳サービスがあります。ウェブに翻訳したい文章を入れると、翻訳結果を表示してくれます。Mac にアプリをインストールしていると、Command-C を2回叩くとカットバッファの内容を翻訳してくれて便利です。
とは言っても、いちいち画面を切り替えるのがちょっと面倒だと思っていたら、最近 API を通してコマンドラインから利用する方法が公開されていることに気がつきました。
このツールをインストールすると deepl
というコマンドが使えるようになって、コマンドラインからテキストを翻訳することが可能になります。
deepl text --to JA 'even monkeys fall from trees'
さるもきからおちる
document
というサブコマンドを使うと、ファイルの内容を翻訳することもできます。何も考えずにドキュメントを含むプログラムを対象にすると、こんな結果になりました。
奇妙な顔文字や不必要な部分を翻訳してしまっているのはともかくして、日本語がなんだか変です。これは、元の文章の途中に改行が入っているためです。ウェブやアプリで使う際にも、改行を取り除かないとうまく翻訳できません。テキストファイルの内容を翻訳しようとすると、どうも一手間二手間かかるのがストレスです。
greple -Mxlate::deepl
そこで greple コマンドを使って、ファイルの中の指定した部分を deepl
コマンドで翻訳した結果に置き換えるモジュールを作りました。
次の画像はこのモジュールを実装しているファイルの中身です。POD という形式でドキュメントが記述されています。この内容を翻訳することを考えると、プログラム部分は当然として、POD のキーワードなども外して、必要な部分だけを対象にする必要があります。
POD ドキュメントは先頭が =
の行から始まり =cut
で終わるので ^=(?s:.*?)(^=cut|\z)
というパターンで表せます。ドキュメント内の地の文は行の先頭からはじまるので、^(\w.*\n)+
というパターンで文章部分を選ぶことができます。文字から始まる行の連続という意味です。greple の --inside
オプションで範囲を指定して検索すると次のようになります。
greple --all --ci=A --inside '^=(?s:.*?)(^=cut|\z)' '^(\w.*\n)+'
うまく対象部分が選べているようです。この例では、=encoding
より前の部分は検索対象に入っていないので選ばれません。また、=
で始まる POD コマンドやインデントしている部分はマッチしません。
--all
はファイル全体を表示。--ci=A
はマッチした部分毎に異なる色を割り当てます。そうしないと、連続しているテキストが1行ずつマッチしているのか、全体でマッチしたのか区別できません。
greple -Mxlate::deepl
このファイルを greple の xlate::deepl
モジュールで翻訳してみます。このモジュールには POD のドキュメント部分を選ぶための --match-podtext
というオプションが予め定義されています。
greple -Mxlate::deepl --match-podtext
のように実行すると、上と同じ結果が出るので、どこが処理対象になるかを事前に確認することができます。
--xlate
範囲に問題がなければ --xlate
オプションをつけて翻訳します。
greple -Mxlate::deepl --match-podtext --xlate --xlate-fold --xlate-format=none
--xlate-fold
は行を折り返すためのオプションで、なくても構いません。--xlate-format=none
は出力形式に none
を指定し、対象部分を翻訳結果で置き換えます。
--xlate-format=conflict
--xlate-format
を指定しないとこうなります。
デフォルトの出力形式は conflict
で、対象となる部分の原文と翻訳結果の両方を git の conflict marker の形式で出力します。DeepL の翻訳はかなり正確ではあるものの、すべて正しいとは限らないので原文を見ながらでないとやはり不安です。
<<<<<<< ORIGINAL
App::Greple::xlate - translation support module for greple
=======
App::Greple::xlate - greple 用の翻訳サポートモジュール
>>>>>>> JA
--xlate-format=ifdef
ifdef
形式で出力することもできます。
#ifdef ORIGINAL
App::Greple::xlate - translation support module for greple
#endif
#ifdef JA
App::Greple::xlate - greple 用の翻訳サポートモジュール
#endif
こうすると、unifdef
コマンドで簡単に必要な部分を取り出すことができます。
sdif -V
conflict marker 形式のファイルを見やすく表示したくなったので、diff 出力を並べて表示する sdif
コマンドを対応させました。実は cdif
は以前から対応していたのですが、sdif
の必要はあまり感じられなくて未対応でした。
sdif は、diff 形式ではないデータはそのまま表示します。これでも悪くはないのですが、diff 以外のテキストは共通部分として扱うための -V
オプションを追加しました。
greple -Mxlate::deepl --match-podtext --xlate --xlate-fold | sdif --no-cdif -V | less
こちらの方が、かなり読みやすく感じられます。--no-cdif
は、単語単位の差分を表示するための cdif
を使わないためです。校正ならともかく、翻訳文章だとうるさいだけで役に立ちません。
次の画像は -Mxlate::deepl
モジュールを使って韓国語と中国語に変換した2つのファイル比較して sdif
で表示したものです。ほとんど同じように見えます。
ちなみに、これらのファイルを再び DeepL で変換すると、ちゃんと意味の通じる日本語になって出てくるのですごいです。
Emacs から使う
Emacs から使うのも簡単です。コマンド一発で変換できるようにしたので shell-command-on-region
を直接使ってもなんとかできますが、オプションを覚えるのとか面倒なので xlate.el
というマクロを作りました。これを読み込んで、選択した領域に対して xlate-region
マクロを実行すると、次のような結果になります。
下手な英語でドキュメントを書いていると、ちゃんと意味が伝わるか不安になることがあります。そんな時にちょっと確認するのに便利です。安心したら undo
すれば元に戻ります。
キャッシュ
DeepL を API で利用するためにはアカウント登録が必要です。フリーアカウントでも認証キーが発行できるので、それを使って deepl
コマンドを利用することができます。ただし、月間50万文字が上限です。コマンドのテストということもあって、何度も同じ文章を変換する必要があり、だんだんもったいなくなってきたのでキャッシュの仕組みを実装しました。
xlate.pm
というファイルなら xlate.pm.xlate-deepl-JA.json
というファイルに、JSON 形式で原文と翻訳結果を格納するようになっています。
--xlate-cache
オプションでキャッシュ方針を指定できます。デフォルトは auto
で、キャッシュファイルがすでに存在すれば、それを読み込んで更新します。なければ何もしません。最初に --xlate-cache=create
で空のキャッシュファイルを作っておくと、後は自動的に管理されます。
実装してみると、開発用途ではなくても有用なことがわかってきました。同じファイルを異なる形式で表示したいと思うことは結構あって、キャッシュのおかげで、一度変換しておけば次回からは瞬時に処理が終わります。
他の形式のファイルを処理するためには
この記事では POD 形式の文書ファイルの必要な部分だけを翻訳する方法について紹介しました。
異なる形式のデータに適用するためには、そのための準備をする必要があります。greple の --inside
, --outside
, --include
, --exclude
オプションを使うと、対象となる領域をパターンで絞り込むことができます。また、これらのオプションや検索パターンには関数を指定することもできるので、パターンマッチでは表現できないような複雑な書式にも対応することが可能です。
greple は、テキストファイルだけではなく Word や PDF のファイルも対象にできるので、結果を見るだけならある程度使い道はあると思います。使い方について不明な点などあれば、お尋ねいただければ可能な範囲で対応します。もし開発作業が必要ならご相談にのります :-)。
Greple に関しては、Hugo と docsy テーマを使って、こんなページを作ってみました。まだ作りかけですが、マニュアルや Qiita のアドベントカレンダーに書いた記事などを置いてあります。
今後の計画
実は、このモジュールは翻訳のためではなく、DeepL が最近始めた校正サービスに使えないかと思って作り始めたものです。現在は無料ウェブサービスでしか使えないようなので、API から利用できるようになったら、このモジュールをベースに対応しようと考えています。
今は、Python 実装の deepl コマンドを使っていますが、直越 API を叩くことも可能です。異常ケースを考えなければそんなに難しくはなさそうですが、サーバとのトランザクションに比べると効果は限定的なので、当面は様子見でしょうか。
2023-02-03 に更新した Version 0.04 では、deepl
コマンドを一度だけ実行して、すべての翻訳を一回ですませるようにしました。これによって、初回の実行速度が大幅に改善しました。また、キャッシュを使わなくても、実用的な時間で結果を得ることができます。このために、greple
に --postgrep
というオプションを追加してあります。
この記事を書いてから、-Mxlate
モジュールをより簡単に使うための xlate
というシェルスクリプトとポータブルな Docker 環境を作りました。これについては以下の記事に書いてあります。
関連記事