📕

Macの辞書アプリとObsidianをPythonで連携しましょう

に公開

について

Macのビルトインの辞書をずっと前から使っているのだけど、大抵オンライン辞書やGoogle翻訳などのWebサービスに頼るようになり、ほとんど利用しないことになります。先日ふと「他のアプリと連携しよう」とよく考えてみると、perplexity.aiを教わって、 PyObjC というpython frameworkを紹介してもらいました。その裏に CoreServices framewrok [1] が利用ならAPI [2] で辞書は Obsidian と連携することが判明しました。

プレリクイジット・環境構築

関するアプリやパッケージがこちらです。バージョンなら同じなわけではないですが、もし問題があったら、古くなったアプリを更新してみると、問題が消えていくかもしれません。

アプリケーション バージョン
macOS 15.5
Dictionary app 2.3.0 (294)
Obsidian 1.8.9
Templater 2.11.1
Python 3.13.3
PyObjC 11.1

辞書の設定

辞書アプリで「辞書」>「設定」と選択します。選んだ辞書は二つ:

  • スーパー大辞林(物書堂)
  • ウィズダム英和辞典(三省堂)

様々な場合に応じて辞書は日本語でも英語でもOKです。

スクリプトを作る

始めての前に、まずは TerminalWezTerm などの端末に、dependenciesの一つ PyObjCpip でインストールします:

pip3 install pyobjc-framework-CoreServices (必要なframeworkのみ)
pip3 install pyobjc (あるいは全てのframeworks)

プログラムはターミナルで実行できる必要がありますが、 argparse [3] というstandard library moduleもインポートして。上記の辞書は2冊とも解析の終わりに親項目と子項目が付けるときがありますので、 re (regular expression) [4] を利用して取り除かれます。

ファイルネーム: mac_dict_app.py

mac_dict_app.py
##!/usr/bin/env python3

from CoreServices import DCSCopyTextDefinition
import argparse
import re

def main():
    parser = argparse.ArgumentParser(description='Mac辞書アプリから単語を調べる')
    parser.add_argument('word', help='辞書で調べる単語')
    args = parser.parse_args()

    word = args.word
    definition = DCSCopyTextDefinition(None, word, (0, len(word)))

    if definition:
        # '〈子項目〉' と '〈親項目〉' を取り除く
        cleaned = re.split(r'〈子項目〉|〈親項目〉', definition)[0].strip()
        # 最初の '】' の後ろに改行を挿入する
        cleaned = re.sub(r'】', '】\n', cleaned, count=1)
        print(cleaned)
    else:
        print(f'「{word}」の定義が見つからない') # プロンプトメッセージ

if __name__ == '__main__':
    main()

ちなみに、その引数 descriptionhelp が必要なわけではないですが、ただ引数が何なのかを示す簡潔な説明のため、

cd ~/Documents
python3 mac_dict_app.py -h

ヘルプページを引き出して -h を付いて:

usage: mac_dict_app.py [-h] word

Mac辞書アプリから単語を調べる

positional arguments:
  word        辞書で調べる単語

options:
  -h, --help  show this help message and exit

プログラムがターミナルから実行できるかを確認しておいてください。

cd ~/Documents
python3 mac_dict_app.py 令和

出力された結果:

れいわ 【令和】
年号(2019年5月1日〜 )。平成の後。今上天皇の時代。

定義が見つからなかったら、このプロンプトメッセージが返されるはず。例えば:

python3 mac_dict_app.py zenn
「zenn」の定義が見つからない

プロンプトメッセージは後のObsidian templateと関係がありますので、そのままにしておいた方がいい。詳しく後述します。

オブシディアンとTemplater

もちろん Obsidian をインストールしておいて、 Plugins のページに Templater を探して。

Templaterをインストールする

オブシディアンで Templater [5] というcommunity pluginをインストールすることは必要があります。このプラグインはテンプレートが作成されるだけではなく、 システムコマンドを実行した結果を取得することができます。 [6]

さらにpython3の絶対パス (absolute path) を探しておいて。端末に:

## python3の実行ファイルのフルパスを表示する
python -c 'import sys; print(sys.executable)'

## もしpyenvを使っているなら
## pyenv which python3

## 仮想環境の場合
## env | grep -i python3

設定によってパスが異なる場合があります。

/Users/shinei/.pyenv/versions/3.13.3/bin/python3

Templateを構築する

Templates という名前のフォルダーを作成して、後は裏に mac_dict_app という名前の新規ノート(.mdファイル)を作成する。

.
└── Templates
    └── mac_dict_app.md

オブシディアンでの設定

  1. 「設定」>「コミュニティプラグイン」に「制限モード」を 無効化 して、「インストールされたプラグイン」に Templater をつける
  2. 「設定」>「コミュニティプラグイン Templater」>「User system command functions」: Enable user system command functions をつける
  3. ページを下ろして、 User function n<sup>。</sup>1 に、このファンクションを追加する:
    • mac_dict_app (左)
    • zsh -c '/Users/shinei/.pyenv/versions/3.13.3/bin/python3 /Users/shinei/Documents/mac_dict_app.py "$word"' (※右)
  4. トップに Template folder location 設定する: Templates

作成したTemplateの内容

内容はこちらです。辞書の結果がcallout [7] に包まれるため、出力の最初のラインの行頭 >[!dict] と、次第のラインの行頭にも > を挿入することは必要があります。

全てのスクリプトは:

mac_dict_app.md
<%*
const selection = tp.file.selection();

if (!selection) {
    const notice = new tp.obsidian.Notice("単語を選んでください");
    setTimeout(() => notice.hide(), 3000);
} else {
    const word = selection;
    const definition = await tp.user.mac_dict_app({ word });
    // 次の文字列定数がmac_dict_app.pyのプロンプトメッセージと
    // 全く同じでなければいけません
    const notFoundMsg = `${word}」の定義が見つからない`;

    if (definition === notFoundMsg) {
        tR += word;
        const notice = new tp.obsidian.Notice(notFoundMsg);
        setTimeout(() => notice.hide(), 3000);
    } else {
        // 定義を行に分割する
        const lines = definition.split("\n");
        // Callout 構文を追加する
        let callout = `>[!dict]+ ${lines[0]}\n`;
        for (let i = 1; i < lines.length; i++) {
            callout += `> ${lines[i]}\n`;
        }
        tR += callout.trim() + "\n";
    }
}
-%>

Template hotkeysの設定

「設定」>「コミュニティプラグイン Templater」>「Template hotkeys」:

Add new hotkey for template

「ホットキー」に Templater: Insert Templates/mac_dict_app.md を定義する。例えば C-dC-f 。システムのショートカットと衝突しないように、なるべく コマンドキー を使用しないで。

CSS Snippetsの設定

ここまでは充分ですけど、デフォストたらcalloutのアイコンはなんてったって見舞わないと思いますので、ついCSS Snippets [8] を自定義してみます。公式ヘルプページに CSS snippets: Adding a snippet をご覧ください。

他のアイコンが Lucide に自ら選ばれまして、私にとってあの book-a はオリジナルと比べて一番似ているのと思いますね。アイコンの色は firebrick あるいは indianred がそっくり似ています。

ディレクトリの例示:

.
└── .obsidian
    └── snippets
        └── snippet_callout.css

CSSファイルの例示:

snippet_callout.css
.callout[data-callout="custom-question-type"] {
  --callout-color: 0, 0, 0;
  --callout-icon: lucide-alert-circle;
}

.callout[data-callout="dict"] {
  --callout-color: 205, 92, 92; /* IndianRed */
  --callout-icon: book-a;
}

出力の実演

オブシディアンに単語を選んでホットキーを押して、結果は:

単語:令和

>[!dict]+ れいわ 【令和】
> 年号(2019年5月1日〜 )。平成の後。今上天皇の時代。

単語:中途半端

>[!dict]+ ちゅうとはんぱ 4【中途半端】
> (名•形動)文ナリ 物事が完成していないこと。また,徹底していないで,どっちつかずなさま。「―な態度」

まとめ

日本語を勉強しているわたしにとって、言語を学びたちにとってこの関数が役に立つと思います。 最後まで読んでいただきありがとうございました。不備がありましたら、ご指導いただけますと嬉しいです。

脚注
  1. PyObjC - the Python to Objective-C bridge, API Notes: CoreServices framework ↩︎

  2. Apple Developer Documentation, Core Services/Dictionary Services ↩︎

  3. Argparse チュートリアル ↩︎

  4. re — 正規表現操作 ↩︎

  5. Templater Obsidian Plugin ↩︎

  6. Templater - System Command User Functions ↩︎

  7. Obsidian Help - Callouts ↩︎

  8. Obsidian Help - CSS snippets ↩︎

GitHubで編集を提案

Discussion