RedPenとPandocでドキュメント作りの手間を省こう

# はじめに

この記事は [NSSOL Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nssol) 18日目の記事です。

皆さんドキュメントを作ることはありますか。
仕事をしていると、形式はどうあれ、何らかの形で文章を残すことがあるでしょう。
個人用のメモなら多少雑でも自分が困らなければどうということはありませんが、他人向けだとそうもいきません。

自分が文章の読み手であるケースのときによく思うのですが、私自身は **表記の揺れ** や **typo** が非常に気になるタイプです。
微妙な表記の揺れでニュアンスを表現したりするケースがある以上、同意であれば統一してほしいと思いますし、そうした不注意を気にしない精度で作られた文章だと思うと、全体の信頼性を疑ってしまうことがあります。

とはいえ、「致命的な間違いでないからそこまで言うことではない」「実用上問題ない」ということもありますし、自分がレビューを受ける立場だったとして、延々と表記の揺れやtypoを指摘されていたくもありません。
というわけで、そういうものは機械に任せようということで、文書検査ツールを使ってみます。

# 利用ツール

今回利用するツールは主に以下の3つです。

-  **[RedPen](http://redpen.cc)**
  - 文書検査ツール
  - 定義したルールに従って文書をテスト、違反箇所を教えてくれる
-  **[Pandoc](https://pandoc.org)**
  - 文書変換ツール
  - 様々な形式の文書を相互変換できる
- **[GitLab](https://gitlab.com)**
  - Gitホスティングサービス
  - Gitリポジトリだけでなく、コンテナレジストリやCI機能も統合されている

RedPenで文書検査を行いますが、やむを得ずWord文書を入力にするケースを考えて、セットでPandocも試してみました。

記事中で順次利用しますが、最終的な資材は以下にまとまっています。適宜ご利用ください。
[t_nakayama0714/document_validation](https://gitlab.com/t_nakayama0714/document_validation)

(なぜGitHubでないかといえば、なんとなくGitLab派であることと、GitLabCIを使いたかったからです)

# RedPenの使い方

では早速RedPenを使ってみます。

1. **RedPenのインストール**
2. **RedPenの基本的な使い方**
3. **RedPenのルール作成**

## RedPenのインストール

[公式ドキュメント](http://redpen.cc/docs/latest/index_ja.html) にある通りですが、 [リリースページ](https://github.com/redpen-cc/redpen/releases/) から実行ファイルを入手するだけです。
RedPenはJava製なので、Javaが動かせる環境ならどこでも大丈夫です。
現時点での最新バージョンが [1.10.3](https://github.com/redpen-cc/redpen/releases/tag/redpen-1.10.3) でしたので今回はこれを使います。

Ubuntu 18でのインストール例ですが、基本的にはバイナリの展開のみなので特段苦労することもないと思います。

```bash:tool/redpen_setup.sh
#!/bin/bash

# install java
apt update
apt install -y openjdk-11-jre

# install redpen
wget https://github.com/redpen-cc/redpen/releases/download/redpen-1.10.3/redpen-1.10.3.tar.gz
tar xvf redpen-1.10.3.tar.gz
mkdir -p /usr/local/redpen
mv redpen-distribution-1.10.3 /usr/local/redpen

export PATH="/usr/local/redpen/bin:${PATH}"

# clean
rm redpen-1.10.3.tar.gz
```

## RedPenの基本的な使い方

このようなファイルがあるとします。

```:sample/demo1.txt
本日はは晴天なり。
```

たまにありますよね、こういうこと。
ではこいつをRedPenにかけてみましょう。

```
$ redpen -L ja -c redpen-conf.xml ../sample/demo1.txt 2>/dev/null
demo1.txt:1: ValidationError[SuccessiveWord], 単語 "は" は連続して使用されています。 at line: 本日はは晴天なり。
```

期待通り、「は」の連続を検出してくれました。
`-L` は出力言語のオプション、 `-c` は設定ファイルのオプションですね。
また、標準エラー出力でINFOの内容が多く出てくるので /dev/null に捨ててしまっています。

ここで、コマンドの戻り値を確認してみると

```
$ echo $?
0
```

ということで、エラーが出ているにも関わらず正常終了したことになっています。
文章がある程度の規模になってくると、完全にエラーを消えたかどうかも悩みどころになってきますので、このしきい値も可変になっており、デフォルトでは3が指定されています。
というわけで `-l` オプションで厳しくしてみましょう。

```
$ redpen -L ja -l 0 -c redpen-conf.xml ../sample/demo1.txt 2>/dev/null
demo1.txt:1: ValidationError[SuccessiveWord], 単語 "は" は連続して使用されています。 at line: 本日はは晴天なり。

$ echo $?
1
```

続いて、 `demo2.txt` もチェックしてみましょう。

```:sample/demo2.txt
サーバ構築では、まずOSをサーバーにインストールします。
```

さて、これはどうなるでしょうか。

```
$ redpen -L ja -l 0 -c redpen-conf.xml ../sample/demo2.txt 2>/dev/null
(略)
demo2.txt:1: ValidationError[KatakanaEndHyphen], カタカナ単語 "サーバー" に不正なハイフンが見つかりました。 at line: サーバ構築では、まずOSをサーバーにインストールします。
demo2.txt:1: ValidationError[SpaceBetweenAlphabeticalWord], アルファベット単語の前にスペースが存在しません。 at line: サーバ構築では、まずOSをサーバーにインストールします。
demo2.txt:1: ValidationError[SpaceBetweenAlphabeticalWord], アルファベット単語の後にスペースが存在しません。 at line: サーバ構築では、まずOSをサーバーにインストールします。

[2019-12-18 00:01:49.624][ERROR] cc.redpen.Main - The number of errors "3" is larger than specified (limit is "0").
```

合計3か所の指摘がありました。
現時点ではほぼデフォルトのルールを適用していますが、

- **KatakanaEndHyphen**
  - カタカナ語がハイフンで終わっていること
  - ex) サーバー
- **SpaceBetweenAlphabeticalWord**
  - アルファベットの前後にスペースが入っていないこと

の違反がありました。

このようにして、RedPenは文章の誤りを指摘してくれます。

## RedPenのルール作成

では、デフォルトのルールに加えて、自分なりのルールも追加してみましょう。

[redpen-conf.xml](https://github.com/redpen-cc/redpen/blob/master/redpen-cli/sample/conf/redpen-conf-ja.xml)

こちらが公式のサンプル設定で、既に色々入っています。
見てわかるように `<validators>` 以下に、個々のvalidatorを追加していくことで、ルールの追加ができます。
個々のルール解説は [公式ドキュメント](http://redpen.cc/docs/latest/index_ja.html#validator) に譲りますので気になる方は確認してみてください。

今回はその中でも、特に **表記ゆれ** に対応するルールを追加してみましょう。
以下のような文章があったとします。

```:sample/demo3.txt
日鉄ソリューションズの英略称は NSOL （エヌソル）です。
```

まぁ、弊社的にはよくあるtypoです。
ルールファイルとしては、TSVで `[誤った表現] [正しい表現]` を記載します。

```:tool/dict/suggest-expressions.tsv
# bad     good
NSOL      NSSOL
エヌソル   エヌエスソル
```

そして、設定ファイルに

```:redpen-conf.xml(抜粋)
<redpen-conf lang="ja">
    <validators>
        ...
        <validator name="SuggestExpression">
            <property name="dict" value="dict/suggest-expressions.tsv" />
        </validator>
        ...
    </validators>
</redpen-conf>
```

を追加してみてください。
ある程度推測がつくかと思いますが、ユーザ定義の辞書に基づき、表現のサジェストを行う機能です。
では実行してみましょう。

```
$ redpen -L ja -l 0 -c redpen-conf.xml ../sample/demo3.txt 2>/dev/null
demo3.txt:1: ValidationError[SuggestExpression], 不正な単語 "エヌソル" がみつかりました．かわりに "エヌエスソル" を利用してください。 at line: 日鉄ソリューションズの英略称は NSOL （エヌソル）です。
demo3.txt:1: ValidationError[SuggestExpression], 不正な単語 "NSOL" がみつかりました．かわりに "NSSOL" を利用してくだ さい。 at line: 日鉄ソリューションズの英略称は NSOL （エヌソル）です。
```

このように、つい素通りしてしまいそうな不適切表現に対しても、ルール通りに検出できることが検査ツールの強みです。

# Pandocの使い方

続いてPandocを使っていきます。
RedPenは多様な言語に対応する反面、検査できるのは以下形式のファイルだけです。

|形式|拡張子|
|:--:|:--:|
|plain|.txt|
|wiki|.wiki|
|markdown|.md, .markdown|
|asciidoc|.adoc, .asciidoc|
|review|.re, .review|
|latex|.tex, .latex|
|properties|.properties|
|rest|.rest, .rst|

ですので、Wordなどは上記いずれかに変換してからでないと検査させることができません。
そこで登場するのがPandocというわけです。

1. **Pandocのインストール**
2. **Pandocの基本的な使い方**

## Pandocのインストール

公式の [Install](https://pandoc.org/installing.html) ページを見るとわかるように、非常に多くの環境をサポートしています。
Javaで動くRedPenと同様に、動作環境でそうそう困ることはないでしょう。

こちらも引き続きUbuntu環境でセットアップします。

```bash:tool/pandoc_setup.sh
#!/bin/bash

# install pandoc
apt install pandoc
```

こちらは公式にインストーラがあるのでこれだけで完了です。

## Pandocの基本的な使い方

Pandoc自体にはいろいろな機能がありますが、今回はWord→txtの変換のみですので、以下のコマンドだけで大丈夫です。

```
$ pandoc -t plain -o ../sample/demo4.txt ../sample/demo4.docx

$ cat ../sample/demo4.txt
RedPenとPandocで快適文書生活。
```

`-t` オプションで出力形式を指定し、 `-o` オプションで出力先を指定しています。

こうすると、そのままではRedPenで検査できなかったWordファイルも

```
$ redpen -L ja -c redpen-conf.xml ../sample/demo4.txt 2>/dev/null
demo4.txt:1: ValidationError[LongKanjiChain], 長い熟語 "快適文書生活" (6) が使われています。 at line: RedPenとPandoc で快適文書生活。
demo4.txt:1: ValidationError[SpaceBetweenAlphabeticalWord], アルファベット単語の後にスペースが存在しません。 at line: RedPenとPandocで快適文書生活。
demo4.txt:1: ValidationError[SpaceBetweenAlphabeticalWord], アルファベット単語の前にスペースが存在しません。 at line: RedPenとPandocで快適文書生活。
demo4.txt:1: ValidationError[SpaceBetweenAlphabeticalWord], アルファベット単語の後にスペースが存在しません。 at line: RedPenとPandocで快適文書生活。
```

というように検査できるようになります。

# CIへの組み込み

では最後に、この仕組みをCIに組み込んでみましょう。
CI環境はコンテナ内で行われるので、まずRedPenとPandocの使えるコンテナを用意するところからはじめます。

```:dockerfile
FROM ubuntu:bionic

### General settings
# install tool
RUN apt-get update
RUN apt-get install -y wget

### For redpen
# install java
RUN apt-get install -y openjdk-8-jre

# setup redpen
RUN wget https://github.com/redpen-cc/redpen/releases/download/redpen-1.10.3/redpen-1.10.3.tar.gz
RUN tar xvf redpen-1.10.3.tar.gz
RUN mkdir -p /user/local/redpen
RUN mv redpen-distribution-1.10.3 /usr/local/redpen
ENV PATH="/usr/local/redpen/bin:${PATH}"

### For pandoc
# install pandoc
RUN apt-get install -y pandoc

### Env settings
# change workdir
WORKDIR /usr/local/documents

# set locale
ENV LANG ja_JP.UTF-8

# copy redpen configuration file to workdir
COPY redpen-conf.xml .
COPY dict/ ./dict/
```

こちらもUbuntuコンテナをベースとしているので、それぞれのセットアップ手順を書き連ねた程度です。
あとはこのコンテナイメージを使うようなCI設定を書いてやればよいので、

```:.gitlab-ci.yml
stages:
  - validation

validation:
  stage: validation
  image: registry.gitlab.com/t_nakayama0714/document_validation
  before_script:
    - find document -not -name "*.txt" -type f | xargs -I FILE pandoc -t plain -o FILE.txt FILE 2> pandoc.log
    - ls -l document/
  script:
    - find document -name "*.txt" -type f | xargs -I FILE redpen -L ja -c tool/redpen-conf.xml FILE 2> redpen.log
```

というふうに書いておくと、 `document` 配下に置かれたファイルを自動的に検査してくれるようになります。
実用上は、特定ディレクトリ配下のファイルを全チェックし続ける必要はないと思うので、命名規則などでfindコマンドで適当にフィルタするか、xargsなども使わず特定ファイルのみチェックさせるようにしてもいいでしょう。

[Job](https://gitlab.com/t_nakayama0714/document_validation/-/jobs/386875436) からは、 `document` に置かれた `demo5.docx` がCIによって検査される様子が確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45253/0b9c7fa4-3396-1984-80c5-ef819bd45ae9.png)

これも例によって `-l` オプションで誤りを厳しく検出していないのでCIが成功したことになっていますが、このあたりもルール作りでもう少し考えるべきところがありそうです。

# おわりに

今回は使う第一歩に焦点を当て、実用的なルールについてはほとんど触れていません。
が、typoしやすい言葉や記号の使い方などは、自分のチームごとに特有なケースが多いと思いますので、徐々に設定ファイルを **育てて** いきながら、文書作成とうまく付き合っていけるようになれれば幸いです。

ちなみに、ここまで書いてきた文章を Markdown テキストとして RedPen にかけたところ、 **無事105個の問題が検出されました** 。ある意味、とても満足です。