macOS でドイツ語学習環境をつくる

# 2022-11-14 追記

vscode でカバーできるようになりました。

- IM -> 拙作: [Schriftsetzer](https://github.com/sumomoneko/Schriftsetzer)を使う
- リアルタイムにドイツ語のスペルチェックをする -> [German - Code Spell Checker](https://github.com/streetsidesoftware/vscode-cspell-dict-extensions/tree/main/extensions/german)を使う
- 単語・文章を読み上げる -> [vscode-read-aloud-text](https://github.com/azu/vscode-read-aloud-text)を使う

＃ 時代は変わったなぁ...

# はじめに

何故かドイツ語が必要になりそうな気配なので、macOS上のemacsでドイツ語環境をあれこれしました。

# 出来るようになること

- ウムラウトを入力する
- リアルタイムにドイツ語のスペルチェックをする
- 単語・文章を読み上げる

## 前提環境
- macOS High Sierra 10.13.4
- brew
- brew cask 版 GNU Emacs 25.3.1

## 事前準備

- cask版emacs

  ```
  brew cask install emacs
  ```
- スペルチェッカ aspell

  ```
  brew install aspell
  ```
- emacsパッケージ
  - MELPAから flyspell-correct-popup をインストール

## ウムラウトの入力設定

ドイツ語にはäやöといった英語にはみられない文字があります。
日本語キーボードをつかってこれらを入力するにはいくつかの方法がありますが、macOS上でのemacsだと、

```
(setq default-input-method "MacOSX")
```
としてシステムのIMを使う設定になっている前提で、

```
M-x set-input-method "german-postfix"
```

が便利でした。このあたり、理解が怪しくてアレなのですが、IMEオンにするといつもの日本語入力となり、オフにすると基本的に英語で、加えて以下の入力でemacsがウムラウトに変換してくれるようになりました。結果オーライです。

| キー | 印字 |
|:-----|:-----|
| ae   | ä    |
| aee  | ae   |
| oe   | ö    |
| oee  | oe   |
| ue   | ü    |
| uee  | ue   |
| sz   | ß    |
| szz  | sz   |

※大文字も同様


バッファ毎に設定するためには、ファイルの先頭にこう書いておけば便利です。
```
-*- eval: (set-input-method "german-postfix") -*-
```
詳細は[File Variables](https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html) を参照ください。


## スペルチェックする

aspellを使います。

brewでは、追加の辞書を指定しなくても英語・ドイツ語・スペイン語・フランス語の辞書がデフォルトで入るようです。

ただ、emacs+aspellはシステムのロケールをみて言語を判断しているようで、何もしないと「日本語(ja)の辞書がないよ」エラーが発生します。
`~/.aspell.conf` に利用する辞書を書くというやり方もありますが、それだと辞書が固定されてしまい、英語・ドイツ語のチェックを併用したい場合は困ってしまいます。

なので、emacs側で指定します。
```
(setq-default ispell-dictionary "german")
```
ちなみに ubuntu16.04では [このバグ](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=839628) を踏んでしまうので、
```
ln -s /usr/lib/aspell/de_DE.multi /usr/lib/aspell/german.alias
```
しておく必要があります。


英語をデフォルト辞書として、特定のファイルだけドイツ語辞書でチェックしたい場合は、`init.el`に
```
(setq-default ispell-dictionary "english")
```
としておいて、別途ファイルのヘッダにドイツ語だと指定しておくのもよいです。先のIM指定と合わせて書くなら、こんな感じになります。(ついでにcompany-modeの補完候補表示が学習の敵なのでdisable)
```
-*- ispell-dictionary: "german"; eval: (set-input-method "german-postfix"); company-idle-delay: nil -*-
```
他、先達の皆様の情報から

- 日本語混在対策
- キャメルケース("GetNextUser"とかの記法)対応
- 修正候補をマウスでなくポップアップメニューで表示する

などなど合わせ、こんな感じで設定しました。

```
;; spell check
(setq-default ispell-program-name "aspell")
(eval-after-load "ispell"
  '(add-to-list 'ispell-skip-region-alist '("[^\000-\377]+")))
(setq-default ispell-dictionary "english")
(setq ispell-extra-args '("--sug-mode=fast" "--run-together" "--run-together-limit=5" "--run-together-min=2"))

;; flyspell-mode
(mapc
 (lambda (hook)
   (add-hook hook
             '(lambda () (flyspell-mode 1))))
 '(
   text-mode-hook
   org-mode-hook
   c++-mode-hook
   c-mode-hook
   objc-mode-hook
   emacs-lisp-mode-hook
   python-mode-hook
   rjsx-mode-hook
   haskell-mode-hook
   ))

(require 'flyspell-correct-popup)
(define-key flyspell-mode-map (kbd "C-;") 'flyspell-correct-previous-word-generic)
```


## ドイツ語を読み上げる

読み上げはmacOSに入っているコマンド say を使います。

まず、OSの
```
システム環境設定→アクセシビリティ→スピーチ→システムの声→カスタマイズ...
```
から、ドイツ語の Annaさんを追加しておきます。

そして、次の設定を追加します。


```elisp
(defun my-speech (&optional string)
  "regionあるいは現在のセンテンスをsayする"
  (interactive)
  (if (eq (process-status "speech") 'run)
      (delete-process "speech"))
  (setq string
        (cond ((stringp string) string)
              (current-prefix-arg
               (read-string "Say: "))
              ((use-region-p)
               (buffer-substring (region-beginning) (region-end)))
              (t
               (save-excursion
                 (let (s)
                   (forward-char 1)
                   (backward-sentence)
                   (setq s (point))
                   (forward-sentence)
                   (buffer-substring s (point)))))))
  (process-kill-without-query
   (start-process-shell-command "speech" nil
                                "/usr/bin/say" "-v" "Anna" (concat "\"" string "\"" ))))

(global-set-key (kbd "C-c s") 'my-speech)

```
これで、以下の操作が追加されます。

- C-c s (regionあり): regionを読み上げ
- C-c s (regionなし): 現在のセンテンスを読み上げ
- C-u C-c s: 入力文字列を読み上げ

なおこのスクリプトは、こちらのスクリプトの悪魔合体です。まさにコピペ合体なので普通はそうはせんだろうなどの指摘がありましたらこっそり教えてください...

http://emacs.rubikitch.com/google-translate/
http://d.hatena.ne.jp/setoryohei/20120331/1333203154

# おわりに

これで LCL Füllung, Seele, aufhebenや Gewalt といった厨二単語の学習もはかどりますね。