Wenn Sie AsciiDoc-Dokumentation als HTML für Ihre Website exportieren, möchten Sie oft, dass sie sich wie ein nativer Teil Ihrer Seite anfühlt – komplett mit Navigation, Footer und einheitlichem Styling.

Der docinfo.html-Mechanismus in AsciiDoc bietet genau diese Möglichkeit. In diesem Artikel zeigen wir, wie wir das adoc Studio Manual in die Hauptwebsite integriert haben.

Grundlagen

Bevor wir uns mit docinfo befassen, sollten wir verstehen, wie der Website-Export von adoc Studio standardmäßig strukturiert ist. Dieses Wissen ist essenziell für eine erfolgreiche Integration.

Die Standard-HTML-Struktur

Wenn Sie Dokumentation als HTML für die Website exportieren, generiert adoc Studio Seiten mit einer spezifischen Struktur:

<body data-is-website>
    <nav class="home">...</nav>           <!-- Breadcrumb/Home-Navigation -->
    <nav class="search">...</nav>         <!-- Suchergebnis-Panel -->
    <nav class="search-toggle">...</nav>  <!-- Such-Button -->
    <nav class="language">...</nav>       <!-- Sprachwechsler -->
    <main>...</main>                      <!-- Dokumentinhalt -->
    <nav class="toc">...</nav>            <!-- Inhaltsverzeichnis -->
</body>

Das body-Element verwendet CSS Grid für das Layout und enthält das data-is-website-Attribut, das website-spezifisches Styling aktiviert.

Eingebaute Navigationselemente

adoc Studio bietet mehrere Navigationselemente standardmäßig an:

Element Klasse Zweck
Home-Navigation nav.home Breadcrumb-Pfad zur Dokumentationswurzel
Such-Panel nav.search Zeigt Suchergebnisse an
Such-Toggle nav.search-toggle Button zum Öffnen/Schließen der Suche
Sprachwechsler nav.language Wechsel zwischen Sprachversionen
Inhaltsverzeichnis nav.toc Dokumentnavigation in der Seitenleiste

Diese Elemente sind standardmäßig am oberen Rand der Seite positioniert und dienen als primäre Navigation für die Dokumentation.

Warum für Website-Integration reorganisieren?

Bei der Integration von Dokumentation in eine Website möchten Sie typischerweise eine Hauptnavigationsleiste ganz oben hinzufügen – dieselbe Navbar, die auf Ihrer gesamten Website verwendet wird. Das erzeugt ein Problem:

  1. Die Standard-Nav-Elemente belegen die obere Position
  2. Ihre Site-Navbar muss ebenfalls oben sein
  3. Visuelle Hierarchie erfordert die Hauptnavbar zuerst, dann dokumentationsspezifische Navigation

Die Lösung ist eine SubNav – eine sekundäre Navigationsleiste unterhalb Ihrer Hauptnavbar, die die reorganisierten Dokumentationsnavigationselemente enthält.

┌─────────────────────────────────────────┐
│  Haupt-Site-Navbar (injiziert)          │  ← Ihre Website-Navigation
├─────────────────────────────────────────┤
│  SubNav: Home | Suche | Sprache         │  ← Reorganisierte Doc-Elemente
├─────────────────────────────────────────┤
│  Hauptinhalt           │  TOC-Sidebar   │
│                        │                │
└─────────────────────────────────────────┘

Dieser Ansatz bewahrt alle eingebauten Funktionen (Suche, Sprachwechsel, Breadcrumbs) und integriert sich nahtlos in das Design Ihrer Website.

Doch wie fügen wir diese eigene Navigation hinzu? Die Lösung ist eine docinfo-Datei.

Was ist docinfo?

Der docinfo-Mechanismus in AsciiDoc ermöglicht es, benutzerdefinierten Inhalt in Ihre exportierten HTML-Dokumente einzufügen. Eine docinfo.html-Datei wird automatisch am Ende des <head>-Bereichs jeder exportierten Seite eingefügt – ideal für Meta-Tags, Scripts und Stylesheets. Wie wir in diesem Artikel zeigen, können Sie JavaScript im Head verwenden, um Inhalte an beliebiger Stelle im Dokument einzufügen.

Private vs. Shared Docinfo

AsciiDoc unterscheidet zwischen zwei Arten von docinfo-Dateien:

Private docinfo – Nach Ihrem Dokument benannt:

mein-dokument.adoc
mein-dokument-docinfo.html    ← Nur für dieses Dokument

Shared docinfo – Wird von allen Dokumenten im Ordner verwendet:

docinfo.html                  ← Geteilt von allen .adoc-Dateien
kapitel1.adoc
kapitel2.adoc

Für Dokumentationsprojekte ist shared docinfo typischerweise die bessere Wahl, da Sie eine einheitliche Navigation und Styling über alle Seiten hinweg wünschen.

Docinfo in Ihrem Dokument aktivieren

Um die docinfo-Verarbeitung zu aktivieren, fügen Sie das :docinfo:-Attribut zu Ihrem Dokumentkopf hinzu:

= Meine Dokumentation
:docinfo: shared

Dies ist mein Dokumentinhalt...

Das :docinfo:-Attribut akzeptiert diese Werte:

Wert Verhalten
shared Verwendet docinfo.html (geteilte Datei)
private Verwendet {docname}-docinfo.html (dokumentspezifisch)
shared-head Nur docinfo.html für Head
private-head Nur {docname}-docinfo.html für Head
shared,private Sowohl geteilte als auch private Dateien

Für Website-Integration verwenden Sie:

:docinfo: shared

Weitere Docinfo-Attribute

Neben :docinfo: gibt es zwei weitere Attribute zur Feinsteuerung:

Attribut Werte Beschreibung
:docinfodir: Verzeichnispfad Speicherort der docinfo-Dateien. Standard ist das Verzeichnis der Quelldatei.
:docinfosubs: Komma-getrennte Substitutionen AsciiDoc-Substitutionen, die auf den docinfo-Inhalt angewendet werden. Standard: attributes

Mit :docinfosubs: können Sie steuern, welche AsciiDoc-Ersetzungen im docinfo-Inhalt durchgeführt werden – z.B. attributes für Attribut-Ersetzung oder specialchars für Sonderzeichen.

Docinfo in adoc Studio einbinden

In adoc Studio platzieren Sie die docinfo.html-Datei im Dokumentordner Ihres Projekts neben Ihren .adoc-Dateien:

Manual.ads/
├── Manual/
│   ├── docinfo.html      ← Geteilte docinfo-Datei
│   ├── attributes.adoc   ← Hat :docinfo: shared (muss ganz oben stehen!)
│   ├── chapter1.adoc
│   ├── chapter2.adoc
│   └── ...

Wichtig: Das Attribut :docinfo: shared muss oberhalb der H1-Titelzeile (=) stehen, damit es im Dokumentenkopf definiert ist und für alle Dokumente gilt.

Empfohlene Struktur:

Learn.ads/
├── Learn/
│   ├── start.adoc        ← Attribut-Datei (ganz oben in der Seitenleiste!)
│   ├── docinfo.html
│   ├── intro.de.adoc
│   ├── chapter1.de.adoc
│   └── ...

Die start.adoc enthält nur Attribute:

// Shared attributes for all Learn documents
:docinfo: shared
:icons: font

Wenn Sie nach HTML exportieren, bindet adoc Studio den docinfo-Inhalt automatisch in jede generierte Seite ein.

Die Grundstruktur einer docinfo.html

Eine docinfo.html für Website-Integration enthält typischerweise drei Teile:

1. CSS-Links

Laden externer Stylesheets für einheitliches Styling:

<link rel="stylesheet" href="/assets/css/site.css">

2. HTML-Templates

Definition der zu injizierenden Elemente als JavaScript-Strings:

const navbarHTML = '<nav class="nav">...</nav>';
const footerHTML = '<footer class="footer">...</footer>';

3. JavaScript-Logik

Injektion und Initialisierung der Elemente beim Laden der Seite:

document.addEventListener('DOMContentLoaded', function() {
    document.body.insertAdjacentHTML('afterbegin', navbarHTML);
    document.body.insertAdjacentHTML('beforeend', footerHTML);
});

Mehrsprachigkeit einrichten

Spracherkennung

Für mehrsprachige Dokumentation müssen Sie die aktuelle Sprache erkennen, um die korrekten Übersetzungen anzuzeigen. Beim Website-Export von adoc Studio ist die Sprache im URL-Pfad eingebettet:

  • Deutsch: /help/Manual/de/page.html
  • Englisch: /help/Manual/en/page.html

Die Struktur ist: /{Zielordner}/{Projektname}/{Sprache}/page.html – wobei help der Ordner auf dem Webserver ist, in den das adoc Studio Projekt Manual exportiert wurde.

So erkennen Sie die Sprache:

const path = window.location.pathname;
const manualLangMatch = path.match(/\/Manual\/(de|en)\//);

let lang = 'en'; // Standard
if (manualLangMatch) {
    lang = manualLangMatch[1]; // 'de' oder 'en'
}

Dieselbe docinfo.html für mehrere Projekte

Wenn Sie mehrere AsciiDoc-Projekte haben (z.B. ein Manual und einen Lernpfad), können Sie dieselbe docinfo.html für alle verwenden. Kopieren Sie die Datei einfach in jeden Projektordner.

Damit die Spracherkennung und der Sprachwechsler für alle Projekte funktionieren, müssen Sie die Pfad-Erkennung erweitern:

var path = window.location.pathname;

// Erkennung für Manual UND Learn
var isManualPage = path.includes('/Manual/');
var isLearnPage = path.includes('/learn/');
var manualLangMatch = path.match(/\/Manual\/(de|en)\//);
var learnLangMatch = path.match(/\/learn\/(de|en)\//);

// Sprache ermitteln
var lang;
if (isManualPage && manualLangMatch) {
    lang = manualLangMatch[1];
} else if (isLearnPage && learnLangMatch) {
    lang = learnLangMatch[1];
} else {
    lang = (path.startsWith('/de/') || path === '/de') ? 'de' : 'en';
}

Für den Sprachwechsler müssen Sie ebenfalls beide Pfad-Typen berücksichtigen:

var enUrl, deUrl;

if (isManualPage && manualLangMatch) {
    var basePath = path.match(/^(.*\/Manual\/)/);
    if (basePath) {
        enUrl = basePath[1] + 'en/index.html';
        deUrl = basePath[1] + 'de/index.html';
    }
} else if (isLearnPage && learnLangMatch) {
    var basePath = path.match(/^(.*\/learn\/)/);
    if (basePath) {
        enUrl = basePath[1] + 'en/index.html';
        deUrl = basePath[1] + 'de/index.html';
    }
}

Tipp: Bei lokalisierten Ordnernamen (z.B. deutsche Ordner heißen anders als englische) ist es oft einfacher, beim Sprachwechsel zur Index-Seite der jeweiligen Sprache zu wechseln, statt die exakte Seite zu übersetzen.

Übersetzungen verwalten

Für die Lokalisierung gibt es zwei Ansätze: manuell (eingebettete Übersetzungen) oder automatisiert (API-basiert). Beide haben ihre Vor- und Nachteile.

Ansatz 1: Eingebettete Übersetzungen (Manuell)

Der einfachste Ansatz ist, alle Übersetzungen direkt in der docinfo.html zu speichern:

var translations = {
    de: {
        'nav.features': 'Funktionen',
        'nav.pricing': 'Preise',
        'footer.newsletter': 'Newsletter',
        'footer.subscribe': 'Abonnieren'
    },
    en: {
        'nav.features': 'Features',
        'nav.pricing': 'Pricing',
        'footer.newsletter': 'Newsletter',
        'footer.subscribe': 'Subscribe'
    }
};

// Hilfsfunktion
var t = function(key) {
    return translations[lang][key] || key;
};
Vorteile Nachteile
Funktioniert offline Übersetzungen müssen manuell synchron gehalten werden
Keine Server-Abhängigkeit Doppelte Pflege wenn Website bereits Übersetzungssystem hat
Einfach zu verstehen

Ansatz 2: Übersetzungen per API laden (Automatisiert)

Wenn Ihre Website bereits ein Übersetzungssystem hat (z.B. Kirby CMS Sprachdateien), können Sie diese per API teilen:

1. API-Endpoint erstellen (/api/translations.php):

<?php
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');

$lang = $_GET['lang'] ?? 'en';
$allowedLangs = ['de', 'en'];

if (!in_array($lang, $allowedLangs)) {
    http_response_code(400);
    exit;
}

$langFile = __DIR__ . '/../site/languages/' . $lang . '.php';
$langData = require $langFile;
echo json_encode($langData['translations'] ?? []);

2. In der docinfo.html Übersetzungen laden:

async function loadTranslations() {
    try {
        var response = await fetch('/api/translations.php?lang=' + lang);
        if (!response.ok) throw new Error('API-Fehler');
        return await response.json();
    } catch (err) {
        console.error('Fallback zu eingebetteten Übersetzungen');
        // Fallback-Übersetzungen zurückgeben
        return {
            'nav.features': lang === 'de' ? 'Funktionen' : 'Features',
            'nav.pricing': lang === 'de' ? 'Preise' : 'Pricing'
        };
    }
}

async function renderUI() {
    var translations = await loadTranslations();
    var t = function(key) { return translations[key] || key; };

    // HTML-Templates mit Übersetzungen bauen...
}
Vorteile Nachteile
Single Source of Truth für Übersetzungen Erfordert Netzwerkzugriff
Keine Doppelpflege API muss deployed sein
Änderungen automatisch synchron

Unsere Empfehlung: Für kleinere Projekte ist Ansatz 1 (eingebettet) ausreichend. Für größere Projekte mit bestehendem Übersetzungssystem empfehlen wir Ansatz 2 (API) mit Fallback-Übersetzungen für den Offline-Fall.

Template-Strings in docinfo.html

Wichtig: AsciiDoc interpretiert {variableName} als Attribute. Verwenden Sie daher String-Konkatenation statt JavaScript Template-Literals:

// FALSCH - AsciiDoc entfernt {lang}:
var url = `/api?lang=${lang}`;

// RICHTIG - String-Konkatenation:
var url = '/api?lang=' + lang;

Dann nutzen Sie die Übersetzungen in Ihren Templates:

var navbarHTML = '' +
'<nav class="nav">' +
'    <a href="#features">' + t('nav.features') + '</a>' +
'    <a href="#pricing">' + t('nav.pricing') + '</a>' +
'</nav>';

Website-Elemente integrieren

Eine Navigationsleiste einfügen

Die Navbar sollte ganz am Anfang des Body eingefügt werden:

const navbarHTML = `
<nav class="nav" id="site-nav">
    <div class="container">
        <a href="/" class="nav__logo">
            <img src="/assets/images/logo.png" alt="Logo">
        </a>
        <ul class="nav__links">
            <li><a href="/#features">${t('nav.features')}</a></li>
            <li><a href="/#pricing">${t('nav.pricing')}</a></li>
        </ul>
    </div>
</nav>`;

document.body.insertAdjacentHTML('afterbegin', navbarHTML);

Eingebaute Elemente in eine SubNav reorganisieren

Jetzt kommt der entscheidende Schritt: Die eingebauten Navigationselemente in eine SubNav verschieben, die unterhalb Ihrer Hauptnavbar sitzt. Dies bewahrt alle Funktionen und etabliert gleichzeitig die korrekte visuelle Hierarchie.

Warum dieser Schritt notwendig ist

Wenn Sie eine Navbar am Anfang des Body injizieren, bleiben die eingebauten Elemente (nav.home, nav.search-toggle, nav.language) an ihren ursprünglichen Positionen – was jetzt unterhalb Ihrer Navbar aber oberhalb des Hauptinhalts ist. Ohne Reorganisation hätten Sie:

  • Ihre Navbar oben
  • Zufällige Navigationselemente dazwischen
  • Den Hauptinhalt

Durch das Verschieben dieser Elemente in einen strukturierten SubNav-Container schaffen Sie eine saubere, organisierte Oberfläche.

Den SubNav-Container erstellen

Erstellen Sie zuerst einen Container-Div und positionieren Sie ihn direkt nach Ihrer Hauptnavbar:

// SubNav-Container erstellen
const docSubnav = document.createElement('div');
docSubnav.id = 'doc-subnav';
document.getElementById('site-nav').insertAdjacentElement('afterend', docSubnav);

Eingebaute Elemente verschieben

Verschieben Sie nun jedes eingebaute Element in die SubNav. Die Reihenfolge ist wichtig – ordnen Sie sie logisch an:

// Breadcrumb/Home-Navigation verschieben
const navHome = document.querySelector('body > nav.home');
if (navHome) {
    docSubnav.appendChild(navHome);
}

// Such-Toggle-Button verschieben
const navSearchToggle = document.querySelector('body > nav.search-toggle');
if (navSearchToggle) {
    docSubnav.appendChild(navSearchToggle);
}

// Sprachwechsler verschieben
const navLanguage = document.querySelector('body > nav.language');
if (navLanguage) {
    docSubnav.appendChild(navLanguage);
}

Suchfunktionalität bewahren

Der Such-Toggle-Button benötigt einen Click-Handler, um das Such-Panel ein-/auszublenden:

if (navSearchToggle) {
    navSearchToggle.addEventListener('click', function() {
        const navSearch = document.querySelector('nav.search');
        if (navSearch) {
            navSearch.classList.toggle('is-active');
        }
    });
}

Einen Footer hinzufügen

Der Footer sollte am Ende des Body angehängt werden:

const footerHTML = `
<footer class="footer">
    <div class="container">
        <!-- Ihr Footer-Inhalt -->
    </div>
</footer>`;

document.body.insertAdjacentHTML('beforeend', footerHTML);

Einen Sprachwechsler implementieren

Damit der Sprachwechsler innerhalb des Manuals korrekt funktioniert, müssen Sie das Sprachsegment in der URL austauschen:

let enUrl, deUrl;

if (path.includes('/Manual/')) {
    // /Manual/de/ ↔ /Manual/en/ tauschen
    enUrl = path.replace(/\/Manual\/de\//, '/Manual/en/');
    deUrl = path.replace(/\/Manual\/en\//, '/Manual/de/');
}

const langSwitcherHTML = `
<div class="lang-switcher">
    <a href="${enUrl}" class="${lang === 'en' ? 'active' : ''}">EN</a>
    <a href="${deUrl}" class="${lang === 'de' ? 'active' : ''}">DE</a>
</div>`;

Fazit

Der docinfo.html-Mechanismus bietet eine leistungsstarke Möglichkeit, AsciiDoc-Dokumentation nahtlos in Ihre Website zu integrieren. Je nach Projektgröße können Sie wählen zwischen:

  • Manuell: Selbstständige docinfo.html mit eingebetteten Übersetzungen – ideal für kleinere Projekte
  • Automatisiert: API-basierte Übersetzungen und Shared JavaScript – ideal für größere Projekte mit bestehendem CMS

Denken Sie daran: AsciiDoc interpretiert {...} als Attribute. Verwenden Sie daher String-Konkatenation (' + variable + ') statt Template-Literals in Ihren JavaScript-Strings.

Der Schlüssel ist, docinfo.html als Brücke zwischen Ihrer statischen Dokumentation und Ihrer dynamischen Website zu betrachten – damit beide als zusammenhängendes Ganzes funktionieren.

Weitere Ressourcen