プログラミング
ポリグロット(多言語対応)ドキュメントサイトの無法地帯
The Wild West of polyglot docs sites (technicalwriting.dev)
要約
複数のプログラミング言語でライブラリを提供するプロジェクトでは、各言語固有のAPIリファレンスジェネレーターに対応するため、ドキュメントサイトの構築が複雑化します。この記事では、この「ポリグロットドキュメントサイト」構築における「変換」と「タードゥッケン(サブサイト統合)」という2つの主要な戦略と、それぞれの課題について解説しています。特に、後者の戦略では、共通ヘッダーやPagefindを活用した統合検索により、異なる生成元からのドキュメントをシームレスに提供するアプローチが紹介されています。
全文翻訳
ポリグロット(多言語対応)ドキュメントサイトの無法地帯
2026年10月08日
N個の異なるプログラミング言語でライブラリを提供するプロジェクトがある場合、そのプロジェクトのドキュメントサイトは、しばしばN個またはN+1個の異なるドキュメントジェネレーターと連携する必要があります。これは、各プログラミング言語に独自のAPIリファレンスジェネレーターが存在するためです。ライブラリ自体は互いに疎結合または非結合であるかもしれませんが、ドキュメントサイトは多くの場合、様々な方法でより密接な結合を必要とします。例えば、すべてのページで同じフォントと色を使用し、サイト内検索のUXを一貫性があり包括的なものにする必要があります。このような、異なるドキュメントジェネレーターからの出力を一つのまとまった全体にまとめようとする種類のドキュメントサイトには、確立された用語が存在しないようです。現時点では、これを「ポリグロットドキュメントサイト」と呼ぶことにしましょう。将来的には、誰かがより良い名前を思いつくかもしれません。
長話になりますが、ポリグロットドキュメントサイトは、技術ライティングにおける、ガラガラヘビやタンブルウィード(転がる草)に満ちた、ほとんど探求されていないフロンティアのように感じられます。そして、おそらく少しの金もあるかもしれません。
戦略
トップダウンの戦略という点では、ポリグロットドキュメントサイトを構造化する方法は2つしか思いつきません。
変換
最初の戦略は、各APIリファレンスジェネレーターの出力を解析し、メインのドキュメントジェネレーターと連携しやすいマークアップに変換することです。例えば、私の最初の仕事では、DoxygenのHTMLを入力として取り込み、XSLT(!!)を使用してよりシンプルなHTMLフラグメントに変換し、その後、rawディレクティブを使用してそれらのHTMLフラグメントをSphinxサイトに引き込みました。
変換アプローチの主な欠点は、APIリファレンスジェネレーターの専門知識を失うことです。Doxygen、rustdoc、javadocなどのツールは、それぞれの言語の詳細を私よりもはるかに良く理解しています。出力を「単純化」するカスタム変換を行う場合、ユーザーが実際に必要とする情報を削除してしまうリスクがあります。つまり、チェスタートンのフェンス(理由のわからない古い規則をむやみに撤廃すべきではないという考え方)です。これらのツールは、APIリファレンスのUXに多くの考慮を払っています。例えば、構造化された検索クエリ `vec -> usize` の場合、rustdocの検索エンジンは、`vec` を引数に取り `usize` を返す関数のみを返します。
変換のもう一つの欠点は、エコシステムの流れに逆らうことです。RustプログラマーはrustdocのUIに慣れています。理論的にはあらゆる点で優れたAPIリファレンスを作成できたとしても、ユーザーには、他では見かけない新しく異なるUIを理解するように求めていることになります。
変換アプローチの別の例はBreatheです。まずDoxygenのXMLビルドを実行し、それをSphinxビルドへの入力として利用可能にします。再構造化テキスト(reStructuredText)では、`.. doxygenclass:: pw::Foo` のようなディレクティブを挿入して、`pw::Foo` のAPIリファレンスを配置すべき場所を示します。BreatheはDoxygen XMLから情報を解析し、Sphinxが理解できるAPIリファレンスコンテンツに変換します。これは、2022年から2024年までpigweed.devのC/C++ APIリファレンスコンテンツの基盤でした。私たちはいくつかの理由で次のセクションで説明するアプローチに移行しました。
一つの問題は遅さでした。正確な数字は覚えていませんが、Breatheはドキュメントビルドにおいてかなりのボトルネックでした。Breatheを使用していた頃の約90秒から、それを使用しなくなってから60秒に短縮されました。
もう一つの問題は、過剰なグルーコード(つなぎ合わせるためのコード)によるサイレントフェイル(静かな失敗)でした。Doxygenコメントでヘッダーをマークアップするのに加えて、`.. doxygenclass:: pw::Foo` のようなディレクティブを通じてSphinxにコンテンツをプルすることを忘れないようにする必要があります。かなりの数の機会に、SWE(ソフトウェアエンジニア)がドキュメントを記述しようと誠実な努力をしましたが、`doxygenclass` のステップを忘れていたために、ドキュメントが実際には公開されませんでした。
最後の問題は、柔軟性が高すぎたことでした。一部のドキュメントコントリビューターは、単一のページで `doxygenclass` ディレクティブをアルファベット順に並べました。他の人はテーマ別のアプローチを取りました。例えば、`foo the bar` の方法に関するガイドの途中に、`pw::Foo` のAPIリファレンスを挿入しました。
タードゥッケン
第二の戦略は、APIリファレンスジェネレーターの専門知識を尊重し、その出力をそのまま公開することです。これがpigweed.devが行っていることです。内部的には、pigweed.devは3つの別々のドキュメントサイトを寄せ集めたものです。C/C++ APIリファレンスはDoxygenで、Rust APIリファレンスはrustdocで、それ以外のすべてはSphinxで生成しています。アーキテクチャ的には、これはタードゥッケン(七面鳥の中にアヒル、アヒルの中に鶏を詰めた料理)です。鶏をアヒルに詰め、アヒルを七面鳥に詰めたものです。pigweed.devの場合、それは鶏(Doxygen)とアヒル(rustdoc)が七面鳥(Sphinx)の中に横並びに詰められているようなものです。
明らかな問題は、サブサイトがすべて互いに異なって見えることです。
A Doxygen生成ページ
A rustdoc生成ページ
A Sphinx生成ページ
過剰な `!important` フラグを使えば、それらを互いに似たように見せることができます。そして、私はそうするつもりです。しかし、CSSハッキングでは、以下のより巧妙な問題は解決できません。
サブサイト間でのナビゲーションが困難です。例えば、rustdoc生成ページからpigweed.devのホームページに戻る方法がありません。
検索UXが断片的で不完全です。例えば、Sphinxによって生成されたページでサイト内検索を使用する場合、検索結果にはrustdoc生成ページの内容は含まれません。
サブサイト間でのリンクが困難です。壊れやすいハードコードされた手動の相対パスに依存することになります。
つまり、互いの存在を知らない3つの完全に分離されたサブサイトがあり、それらをより接続されていると感じさせる必要があります。
pigweed.devでは、ユニバーサルヘッダーと包括的な検索を導入することで、この点に進歩が見られました。
ユニバーサルヘッダー
すべてを支配する一つのヘッダー、すべてを見つける一つの検索
パスをマッピングする一つのナビゲーションと、それらを思い出させるパンくずリスト
サイト上のすべてのページには、同じトップレベルのリンク、検索、パンくずリストUIがあります。テーマ選択もサブサイト間で同期を保ちます。
Doxygen生成ページは現在:
The rustdoc生成ページ:
そしてSphinx生成ページ:
実装面では、これは多くの後処理です。Doxygen、rustdoc、Sphinxのビルド中に、すべてのページに `<!-- pw-sentinel -->` コメントを注入します。DoxygenとrustdocのビルドはSphinxビルドの前に実行されます。ビルド完了イベントにフックするSphinx拡張機能があり、これらのコメントをヘッダーの完全なHTML、CSS、JSで置き換えます。この拡張機能には、トップレベルのリンクとパンくずリストを構築するための多くのロジックが含まれています。
包括的な検索
ユニバーサルヘッダー内には、サイト内検索にアクセスするための統一されたUIがあります。検索はモーダルとして開き、入力するにつれて結果が表示されます。大きな利点は、検索結果が包括的になったことです。つまり、Doxygen、rustdoc、Sphinxのすべてのコンテンツがインデックス化され、結果に表示されます。
1番目の結果はDoxygenから、2番目と3番目はSphinxから、4番目はrustdocからです。
真のヒーローはPagefindです。このライブラリは、サイト内検索のニーズすべてに対応するワンストップショップです。Pagefindを出力ディレクトリにポイントするだけで、ビルドされたHTMLに基づいて検索インデックスが作成されます。インデックス化されるコンテンツを微調整するための許可リストと拒否リストのAPIの両方があります。検索UIには、PagefindのデフォルトのWebコンポーネントにカスタマイズを少し加えて使用しています。UXを完全にカスタマイズしたい場合は、JS APIがあります。PagefindはWebワーカー、WebAssembly、インデックスチャンクのオンデマンドロードなど、クールなことを行っているようですが、公式ドキュメントでは内部構造の優れた概要を見つけることができませんでした。