The Wild West of polyglot docs sites
The Frontier of Polyglot Documentation Sites
When a project supports libraries across different programming languages, the documentation site typically has to juggle or distinct documentation generators. This happens because every language possesses its own specialized API reference tool.
While the underlying code libraries might be decoupled, the documentation site requires tight coupling to ensure a professional user experience. Specifically:
- Visual Consistency: Uniform fonts, color palettes, and branding.
- Unified Search: A comprehensive search UX that spans all language references.
- Cohesive Navigation: A seamless flow between guides and API refs.
There isn't a standardized industry term for this struggle—the act of wrestling disparate generator outputs into a single, harmonious site. To put it bluntly, polyglot docs sites are the Wild West a sparsely explored frontier, plagued by "rattlesnakes and tumbleweeds."
High-Level Strategies
There are essentially two top-down architectural patterns for structuring these sites. While both are viable, neither is without significant flaws.
1. The Transformation Approach
This strategy involves parsing the output of various API generators and converting that data into a markup format compatible with a primary documentation engine.
Example Workflow:
In a previous role, I processed Doxygen HTML, utilized XSLT (yes, really!) to strip it down to basic HTML fragments, and then embedded those fragments into a Sphinx site using the raw directive.
The Trade-offs
The primary risk here is the erosion of tool-specific expertise. Tools like javadoc, rustdoc, or Doxygen are built by people who understand the nuances of their respective languages far better than a technical writer does.
- Loss of UX Nuance: By "simplifying" the output, you risk deleting critical information.
- Loss of Advanced Features: For instance,
rustdocsupports sophisticated search queries.- Example: A query like
vec - usizespecifically filters for functions that accept avecand return ausize. A custom transformation would likely kill this functionality.
- Example: A query like
- Ecosystem Friction: You are forcing users to learn a proprietary UI that doesn't match the standard experience they expect for that language.
Case Study: Breathe
Breathe is a prime example of the transformation method. It takes Doxygen XML and makes it available to Sphinx.
In the pigweed.dev project (from 2022–2024), we used Breathe to handle C/C++ references. We used directives like .. doxygenclass:: pw::Foo to tell Sphinx where to inject the API data. However, we eventually abandoned this due to:
- Performance Degradation: Breathe was a major bottleneck.
- Build time with Breathe:
- Build time without Breathe:
- Fragile Glue Code: The system suffered from "silent failures." A developer might document their code perfectly in the headers, but if they forgot the
.. doxygenclassdirective in the Sphinx file, the docs simply never appeared. - Poor Layout Logic: Some contributors simply listed classes alphabetically on one page, inserting a reference to
pw::Fooright in the middle of a conceptual guide, breaking the narrative flow.
2. The "Turducken" Approach
Instead of transforming the data, this strategy defers to the original tools and publishes their output exactly as-is.
At pigweed.dev, the site is essentially three separate documentation sites stitched together. It's like a chicken (Doxygen) and a duck (rustdoc) stuffed inside a turkey (Sphinx).
| Component | Tool Used | Role |
|---|---|---|
| C/C++ API | Doxygen | Reference |
| Rust API | rustdoc | Reference |
| General Docs | Sphinx | Guides & Home |
The Challenges
The most immediate problem is visual dissonance. A Doxygen page looks nothing like a rustdoc page, which looks nothing like a Sphinx page. While I used an aggressive amount of !important flags in CSS to align the aesthetics, deeper issues remained:
- Broken Navigation: It is difficult to move between subsites. For example, a
rustdocpage provides no native way to return to the mainpigweed.devhomepage. - Siloed Search: The Sphinx search engine is blind to the content inside the
rustdocorDoxygendirectories. - Brittle Linking: You are forced to rely on manual, hardcoded relative paths.
The Solution: The Universal Header
To bridge these isolated islands, we implemented a universal header and a global search mechanism.
One header to rule them all, one search to find them One nav to map the path and breadcrumbs to remind them
Now, every page—regardless of the generator—shares the same top-level navigation, search bar, and breadcrumb UI.
How it works (The Implementation): We use a post-processing step. First, we inject "sentinel comments" into the builds of all three generators.
<!-- SITE_HEADER_START -->
<!-- This is a placeholder for the universal header -->
<!-- SITE_HEADER_END -->
During the post-processing phase, a script scans the HTML files and replaces everything between those sentinels with the actual, unified header HTML.
Summary Checklist for Polyglot Docs
- Decide between Transformation vs. Aggregation.
- Evaluate the impact on build times.
- Ensure search is comprehensive across all generators.
- Implement a universal navigation layer to prevent "dead ends."
- Balance visual consistency with the native UX of the language tools.