翻訳版 README がズレていく問題
多言語の README を持っているプロジェクトは、だいたい同じ流れで壊れていきます。
- 誰かが英語で
README.mdを書く。 - コントリビューターが
README.zh.mdの PR を出す。ありがたい。 - 3 か月後、英語版には新しいセクションが 6 つ追加されている。中国語版は最初のまま。
- 別の翻訳者が
README.es.mdを作ろうとする。元になるのはどっち?最新のREADME.md?それとも構造的にきれいに見えるREADME.zh.md? - 9 か月後、3 つの README がそれぞれ違うことを言っている状態になる。
パッと見てどのファイルが古いのか分からないし、レビュアーは 3 ファイル全部読まないし、翻訳が腐っていくのを止める仕組みがどこにもない。
これに耐えられなくなって、小さな Java ツール NRG を作りました。まだ方向転換できる規模なので、率直なフィードバックがほしいです。
アイデア: 1 つのソースから N 個の出力
README.src.md を 1 つ書くと、README.md、README.zh.md、README.ja.md など、宣言した言語ぶんのファイルが生成されます。
テンプレートの行は 3 つに分かれます。
全言語で共有される行(バッジ、コードブロック、ファイルパスなど、言語に依存しないもの)。マークアップは不要で、すべての出力にそのまま含まれます。

言語タグ付きの行 — その言語の出力にだけ現れます。
This library is small but hard to use.<!--en-->
このライブラリは小さいですが、使うのが難しいです。<!--ja-->
本库虽小,但难以使用。<!--zh-->
インラインの言語別フレーズ — アンカー名やボタンラベルなど、行ごと分けるほどでもない短い文字列に使います。
## ${en:'Table of contents', ja:'目次', zh:'目录'}
nrg -f README.src.md を実行すると、README.md、README.ja.md、README.zh.md が生成されます。各出力には「自動生成です」というヘッダコメントが付くので、読み手にもわかります。
キラー機能: CI による drift-check
ここが一番作りたかった部分です。「定期的に再生成すれば良い」では足りなかった理由でもあります。
NRG には GitHub Action (nanolaba/nrg-action@v1) が同梱されていて、check モードがあります。PR のたびに README を一時ディレクトリに再生成し、コミット済みのファイルと diff を取ります。一致しなければ、unified diff 付きでビルドが落ちます。
--- README.md (on disk)
+++ README.md (generated)
@@ line 27 @@
-## Quick start
+## Getting started
つまり、コントリビューターが README.zh.md をテンプレートをスキップして直接編集することは「できなくなる」。README.src.md を編集して再生成するか、CI が PR を弾くかの 2 択です。サイレントなズレが起きようがなくなります。
CLI にも同じフラグがあります: nrg -f README.src.md --check。pre-commit フックで便利です。
組み込みウィジェット
テンプレート構文で直接表現できないものはウィジェットになっています。同梱されているもの:
-
${widget:tableOfContents(ordered='true')}— 見出しから自動で目次を生成。 -
${widget:import(path='docs/intro.src.md')}— テンプレートの分割と合成。1 つの 800 行 README を回避できます。 -
${widget:exec(cmd='git rev-parse --short HEAD')}— シェルコマンドの出力を埋め込み。「最終ビルドコミット: X」のような表示に便利。 -
${widget:fileTree(path='src/main/java', depth='2')}— ディレクトリツリーを自動生成。 -
${widget:math(expr='\\pi r^2')}— LaTeX をレンダリング。GitHub の MathJax がうまく動かないケース向けに SVG フォールバック付き。 - ほかに
alert,badge,if/endIf,date,todoがあります。
カスタムウィジェットは NRGWidget インターフェイスを実装した 1 クラスでいけます。プロジェクト固有の繰り返しパターン(たとえば「対応ランタイムを行ごとに表示する feature matrix ウィジェット」など)を定義したいときに便利です。
3 つの統合方法
CLI — nrg -f README.src.md。テンプレート内に <!--@nrg.languages=en,ja,zh--> と書く以外の設定は不要です。
Maven プラグイン — Java プロジェクト用。compile フェーズに紐付け、ビルドのたびに再生成します。
<plugin>
<groupId>com.nanolaba</groupId>
<artifactId>nrg-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<file><item>README.src.md</item></file>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals><goal>create-files</goal></goals>
</execution>
</executions>
</plugin>
GitHub Action — Python、JS、Rust など Java ではないプロジェクト向け。Action 自身が Java をプロビジョニングするので、リポジトリ側に Java ツールチェインは不要です。
- uses: nanolaba/nrg-action@v1
with:
file: README.src.md
mode: check
これでセットアップは完了です。ほかの生成パイプラインに組み込みたい場合は Java ライブラリとしても使えます。
正直な制限事項
- Java 8 以上が必要。バイナリ自体は移植性が高いですが、JDK のインストールが嫌なら GitHub Action 経由が唯一のゼロタッチ選択肢です。
- 翻訳ツールではありません。NRG が同期させるのは構造です。本文の翻訳は人間(または好みの LLM)の仕事のままです。
-
Markdown AST は持っていません。置換とウィジェットは生のテキストに対して動きます。99% のケースでは問題になりませんが、巧妙な書き手が NRG にキャッチされない壊れた Markdown を出力することは可能です。そのため
validateモードが別にあります。 - まだ初期段階。現在 v1.2、いくつかの OSS リポジトリで使われている程度です。ウィジェット API はまだ変わる可能性があります。
フィードバックをいただきたい点
- drift-check ワークフロー — 安全網として有用?それとも翻訳者がタイポを直したいだけのときに摩擦になりますか?翻訳ドキュメントを大規模にメンテしている人の感覚を知りたいです。
-
ウィジェット構文 —
${widget:tableOfContents(title='...', ordered='true')}は読みやすいでしょうか?それとも Mustache の劣化版を再発明していますか? - どうなったら実際に採用しますか? 現在の運用(手動同期、自作スクリプト、何もしない)と比較して。「これは絶対に使わない、なぜなら…」という回答が一番ありがたいです。
リポジトリ、フルドキュメント、デモ GIF: github.com/nanolaba/readme-generator
読んでいただきありがとうございます。コメントでの質問もお気軽に。