Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

textlintによる表記ゆれ撲滅

Eightでフロントエンドエンジニアをしている青山です。

今回はEightのCI環境にtextlintという文書校正ツールを導入してみたので、その経緯や実施した内容をお伝えします。 サービスを複数抱えているなど、文書管理が煩雑になってチェックが大変、といった場合に活用できるかもしれません。

textlint.github.io

ある日のフィードバック

Eight(およびSansanの各プロダクト)では、社内のslackでフィードバックを受け取るチャンネルが存在しています。活発に社内外からのフィードバックが投稿されていますが、ある日の投稿で以下のようなものがありました。

今日届いたお知らせの文言に「副業」というのが使われてました。

Eightでは「副業」使わずに「複業」とする、みたいな方針だった気がしたので、気になりました。

実際このフィードバックが上がった直後には、素早く修正リリースが行われたのですが、初期リリース時にはどうやら担当者のチェックフローが漏れてしまったようでした。

目視によるチェックは負荷が大きいですし、当然人間ならミスもします。こういった場合には、そう、自動化です。 というわけで、textlintをCIに組み込むことを思いつき、作業を進めていきました。

要件を整理

textlintによるチェックを導入したいとひとことに言っても、適用内容やその範囲など、やりたいことを整理する必要があります。 今回は以下のような内容を検討しました。

  • 表記ゆれを検知できる
  • プロダクトごとに設定、ルールをカスタマイズできる
  • Ruby on Rails,Reactのアプリケーションに適用できる

これらを実現するに当たって、textlintのカスタマイズ性やルールをまとめて公開できる機能が適していると考えました。*1

共通ルールセット作成

要件にそって、社内用のtextlintルールセットとして@eightcard/textlint-rule-preset-eightを作成し、各プロダクトに適用してきました。 まずは@eightcard/textlint-rule-preset-eightそのものについてです。

ベース定義

textlint-rule-preset-japaneseという日本語文書用のルール定義を参考に、以下のようなコードを作成しました。ほとんどtextlint-rule-preset-japaneseを流用させてもらい、後述するprhルールを追加しているというかたちです。

"use strict";
const path = require('path');
const { moduleInterop } = require("@textlint/module-interop");
const presetJapanese = moduleInterop(require("textlint-rule-preset-japanese"));

module.exports = {
  ...presetJapanese,
  rules: {
    ...presetJapanese.rules,
    "prh": moduleInterop(require("textlint-rule-prh")),
  },
  rulesConfig: {
    ...presetJapanese.rulesConfig,
    "prh": {
      "rulePaths" :[path.join(__dirname, "./prh.yml")]
    },
  },
};

表記ゆれ設定

表記ゆれについては、textlint-rule-prhを利用することで対応できます。textlint-rule-prhはprhという文書校正ツールを利用したtextlintルールで、正規表現をもちいて表記ゆれを指摘したり、fixオプションで修正してくれます。

以下のようなルールに沿って定義を作っておけば、チェックから置換処理まで任せることができます。

rules:
  - expected: 複業
    pattern: 副業
  - expected: でき$1
    pattern: /出来([^かね])/
    specs:
      - from: 出来る
        to: できる
      - from: 出来ます
        to: できます
      - from: 出来ません
        to: できません
      - from: 出来かねる
        to: 出来かねる
      - from: 出来かねます
        to: 出来かねます

特に複雑な条件になってしまう場合に有効なのがspecsフィールドです。実際に置換したい内容を記述しておくことで、実行前に正規表現が意図したものになっているかをチェックすることができます。(実際にやらかしてこれの重要性に気づかされました 😇 )

細かい設定内容はtextlint-rule-prhのドキュメントを参照ください。

設定のカスタマイズ

textlintはeslintのようにpluggableであり、ルールの適用や除外を設定でカスタマイズできます。

  "rules": {
    "@eightcard/textlint-rule-preset-eight": {
      "sentence-length": false,
      "max-ten": {
        "max": 4,
      }
    }
  },

prhの設定については、以下のようにEightの共通定義ファイルを参照して拡張するようにしました。もう少しスマートなやり方がないか悩んでおり、よりよい方法があれば指摘していただけるととても喜びます。

  version: 1
  imports:
    - path: ./node_modules/@eightcard/textlint-rule-preset-eight/prh.yml
      ignoreRules:
        # 除外したいルール
  rules:
    # 追加したいルール

Ruby on Rails, Reactへの適用

先にも述べた通り、textlintはpluggableであり、プラグインを使うとサポートするファイル形式の拡張が可能です。 今回は以下のようなプラグインを利用しました。必要なものだけを、利用するリポジトリ側で設定します。

ルールセット公開

@eightcard/textlint-rule-preset-eightの公開方法としては、GitHub Private Registry(GPR)を選択しています。

GitHubの手順に従って、公開および各リポジトリでの適用を行ないました。

リポジトリによっては、renovateやdepandabot*2を利用しているため、以下の手順にそって、それぞれの設定を修正しています。

バージョン更新

社内でのパッケージとはいえ、ルールが追加されるなど、バージョン更新が発生します。 この作業をスムーズにするために、リリースノートの作成自動化をしました。 release-drafterというGitHub Actionsを利用してリリースノートを自動的に作成し、GitHub releaseイベントをトリガーにしたGitHub Actionsを作成して、GPRへの公開と社内slackへの通知を行なっています。

f:id:mt-blue81:20220403160749p:plain
リリースノート

f:id:mt-blue81:20220403160820p:plain
slack通知

表記ゆれ設定のメンテナンス

表記ゆれの定義を作成しているころに、ちょうとデザイナーのtimesチャンネル*3で、デザイナーチームが持っている表記ゆれチェック用のスプレッドシートを、Eightチーム全体に公開したという内容を知りました。

これを使わない手はないということで、このシートをベースに prh ルールを定義していくことにしました。 以下のような方針で、デザイナーチームと協業して進めました。

  • 既存プロダクトに対して、置き換えたらどうなるかを確認
    OKが出たものだけをルールとして追加
  • 条件が複雑(タイトルには使わないなど)な場合は、ルールから除外
  • 規約など、文章の変更が難しいものは、ファイル単位で置換対象外*4

まとめ

何気ないフィードバックから始まった改善作業でしたが、以下のような収穫があったと感じています。

  • Pull Request単位で文書校正ツールでチェックされるようになった
    • 表記ゆれだけではなく、日本語として読みにくくなりそう、などにも適用されていていい感じ
  • GitHub Private Registryやリリースノート作成自動化などの知見を得た
    • 共通ルール更新から、renovate,dependabotによる検知によって各リポジトリへの適用までが自動化された
    • 今後の社内パッケージに波及させていけそう

いっぽうで、表記ゆれスプレッドシートの更新を検知する方法を確立していないため、共通ルールリポジトリのメンテナンスが定まっていないという課題が残っています。 正規表現を組み立てる作業は必要なため、社内メンテナを集いながらちょっとずつ改善していくつもりです。

*1:今後プロダクトが増えたときにもこれを活用できるだろうという狙いです

*2:npmなどのパッケージ管理システムの更新を検知して、適用するPRを作成してくれるサービス群です

*3:社内では個人が日常の作業内容や考えていることなどを投稿する分報(times)チャンネルが多くあります

*4:.textlintignoreという対象ファイルを除外する仕組みを利用しています

© Sansan, Inc.