◈ MEADOW LARK - ACTIVE SESSION Floor 1 · 6 entries logged

🐩 Meadow Lark

Floor Log — Recent Entries

Morgen kaufen wir den Prompt

Drei Artikel haben mich diese Woche beschĂ€ftigt. Robin Sloan beschrieb 2020, wie eine App wie ein selbst gekochtes Essen sein kann – gebaut fĂŒr vier Menschen, fĂŒr niemanden sonst. FĂŒnf Jahre spĂ€ter ruft David Pierce auf The Verge die persönliche Software-Revolution aus. Und Thomas Ptacek zeigt auf sockpuppet.org, wie er sich in 30 Minuten einen eigenen Markdown-Viewer baut. Alle drei beschreiben dasselbe PhĂ€nomen – von außen. Ich stecke mittendrin.

Ich entwickle gerade ein Tool zur Überwachung von Webseiten. Es soll Änderungen an Inhalten erkennen und mich benachrichtigen, wenn etwas passiert. Es gibt bereits Dienste, die genau diese Funktion anbieten, und einige davon sind sehr gut. Allerdings verlangen alle dafĂŒr eine GebĂŒhr.

Ich werde keines dieser Produkte kaufen. Nicht, weil sie schlecht wĂ€ren, sondern weil ich am Wochenende mit Claude zusammengearbeitet habe und wir in nur wenigen Stunden eine Lösung entwickelt haben, die meine Anforderungen besser erfĂŒllt als jedes dieser Produkte. Nicht trotz, sondern gerade wegen dieser Tatsache.

Denn gekaufte Software muss fĂŒr viele passen. Sie trifft Entscheidungen zugunsten von AnschlussfĂ€higkeit, nicht zugunsten meines Prozesses. Sie ist so gebaut, dass sie in hundert verschiedene Workflows passt – und deshalb in keinen perfekt. Meine Software muss nur fĂŒr einen einzigen Kontext funktionieren: meinen. Das ist kein Kompromiss. Das ist ein struktureller Vorteil.

Und dann kam die Frage, die mich seitdem nicht loslĂ€sst: Wenn ich das kann – was kaufe ich dann eigentlich noch?

Von der Idee zum Prompt – ein Prozess, kein Moment

Die Antwort ist unbequemer als erwartet: weniger als heute – aber nicht wegen des Preises.

Der Prompt ist nicht der Startpunkt. Er ist das Ergebnis.

Das Bauen ist die Methode, mit der man das Verstehen erzwingt. Man fĂ€ngt an, stĂ¶ĂŸt auf Fragen, die man vorher nicht hatte, und trifft Entscheidungen, die die Idee erst scharf machen. Am Ende steht nicht nur ein Tool – sondern ein VerstĂ€ndnis des Problems, das prĂ€zise genug ist, um es zu beschreiben.

Erst jetzt entsteht der Prompt, der sich weitergeben lÀsst.

Die eigentliche Arbeit ist nicht das Bauen. Sie ist das Verstehen. Wer nie selbst iteriert, wer nie gegen die Grenzen einer vagen Idee gestoßen ist, hat keinen Prompt – er hat eine Wunschliste.

Und Wunschlisten lassen sich nicht verkaufen.

Die unsichtbare HĂŒrde

WĂ€hrend ich diesen Artikel schreibe, fĂ€llt mir auf: Ich brauche ein kleines Tool. Einen Markdown-Editor, der mir erlaubt, Anmerkungen direkt im Text zu hinterlassen – nicht in einem separaten Dokument, nicht per Copy-Paste, sondern inline, neben den AbsĂ€tzen. Sowas wie Kommentare in Google Docs, nur fĂŒr meinen lokalen Workflow.

Ich weiß, dass ich es bauen könnte. Ich weiß ungefĂ€hr, wie es aussehen wĂŒrde. Und trotzdem zögere ich.

Nicht wegen der Kosten. Nicht wegen fehlender Kompetenz. Sondern wegen der 20 Minuten. Oder sind es zwei Stunden? Oder doch mehr? Das Zögern entsteht nicht aus UnfĂ€higkeit, sondern aus Unsicherheit ĂŒber den Aufwand – und aus der leisen Angst, mitten in einer anderen Aufgabe steckenzubleiben.

Das ist die neue HĂŒrde. Sie ist nicht mehr technisch. Sie ist psychologisch.

Wer heute mit KI-Assistenten arbeitet, merkt schnell: Das Bauen selbst ist nicht mehr das Problem. Das Problem ist zu wissen, was man will. PrĂ€zise genug, um es zu beschreiben. Konkret genug, um den ersten Schritt zu machen. Die Barriere hat sich verschoben – vom Können zum Wollen. Und das ist eine FĂ€higkeit, die man ĂŒben muss.

Die Kehrseite: Was verkaufst du dann noch?

Wenn ich ein Tool bauen kann, das meine BedĂŒrfnisse besser erfĂŒllt als ein kommerzielles Produkt, was bedeutet das fĂŒr die Anbieter solcher Produkte? Was bedeutet es fĂŒr jeden, der Software verkauft?

Nehmen wir mein Monitoring-Tool als Gedankenexperiment: Angenommen, ich wĂŒrde es anbieten wollen. Was wĂ€re mein Produkt? Die Software selbst? Die ist in wenigen Stunden replizierbar. Der Code? Bedeutungslos, sobald jemand denselben Prompt ausfĂŒhrt. Die Infrastruktur? Commodity.

Was bleibt, ist die Idee. Die Beobachtung, dass ein bestimmtes Problem existiert. Die genaue Beschreibung, wie eine Lösung aussehen soll. Das Urteilsvermögen, zu erkennen, ob das Ergebnis gut ist.

Das klingt abstrakt. Ist es aber nicht.

Ein Prompt fĂŒr ein gutes Monitoring-Tool zu schreiben ist keine triviale Aufgabe. Er muss das Problem prĂ€zise beschreiben: Welche Art von Änderungen interessieren mich? Wie soll Benachrichtigung funktionieren? Was sind GrenzfĂ€lle? Was ist explizit nicht gewĂŒnscht? Ein vager Prompt produziert vage Software. Die Arbeit hat sich verschoben – vom Tippen von Code zum Denken ĂŒber das Problem.

Die These: Die Idee wird zum Asset

Ideen brauchen immer ein Medium. Musik braucht eine Aufnahme, ein Buch braucht einen Verlag, ein Film braucht ein Studio. In all diesen FĂ€llen trennt die Produktionsbarriere die Idee von ihrer Umsetzung – und wer die Mittel zur Produktion kontrolliert, kontrolliert den Zugang zum Markt.

Software war dasselbe – aber mit einem entscheidenden Unterschied. Bei Musik oder BĂŒchern musste jemand produzieren. Bei Software musste jemand ĂŒbersetzen. Die Idee existierte in einer Sprache, die Maschinen nicht verstanden. Entwickler waren keine Produzenten – sie waren Dolmetscher. Und Dolmetscher sind schwer zu ersetzen.

Der Code war das Asset, weil die Übersetzung das Eigentliche war.

Das stimmt nicht mehr.

Code ist heute so leicht herzustellen wie Text. Was Wert hat, ist die Beschreibung dessen, was der Code tun soll. Der Kontext. Das Wissen ĂŒber den Nutzer, das Problem, die EinschrĂ€nkungen. Die FĂ€higkeit, ein Problem so prĂ€zise zu formulieren, dass eine Maschine es lösen kann.

Mit anderen Worten: Die Spezifikation ist das Produkt.

Das ist eine radikale Verschiebung. Nicht weil Entwickler ĂŒberflĂŒssig werden – wer beurteilt, ob das Ergebnis gut ist, braucht nach wie vor tiefes VerstĂ€ndnis. Aber die Wertschöpfung sitzt woanders. Sie sitzt beim Menschen, der das Problem kennt, der die richtigen Fragen stellt, der erkennt, wann die Lösung fertig ist.

Heute kaufen wir Software. Morgen kaufen wir den Prompt.

WofĂŒr gilt das – und wofĂŒr nicht?

Das ist die Frage, die ich nicht abschließend beantworten kann. Und ehrlich gesagt: die ich auch nicht beantworten will. Denn ich glaube, dass die richtige Antwort vom Kontext abhĂ€ngt – und dass sie sich gerade erst herausschĂ€lt.

Meine Beobachtung: Es gilt ĂŒberall dort, wo Software ein Werkzeug ist. Wo sie eine klar umrissene Aufgabe erfĂŒllt, die jemand beschreiben kann, der das Problem versteht.

Es gilt vielleicht weniger dort, wo Software ein Netzwerk ist. Wo der Wert nicht in der FunktionalitĂ€t liegt, sondern in der kritischen Masse der Nutzer. Wo Integration, Vertrauen und VerlĂ€sslichkeit ĂŒber Jahre aufgebaut wurden.

Und es gilt möglicherweise gar nicht dort, wo Software regulierte Infrastruktur ist – wo Zertifizierungen, Haftung und Compliance die eigentliche Leistung sind, nicht der Code.

Aber das sind meine vorlÀufigen Antworten. Ich bin neugierig auf eure.

Wo in eurer Arbeit kauft ihr heute noch Software, die ihr morgen selbst bauen wĂŒrdet? Und umgekehrt: Wo entwickelt ihr noch etwas per Hand, obwohl ein gut formulierter Prompt es genauso gut könnte?

Was bleibt

Ich werde mein Monitoring-Tool weiterentwickeln. Nicht weil ich glaube, dass ich damit ein GeschĂ€ft aufbauen kann. Sondern weil es genau das tut, was ich brauche – und weil ich dabei gelernt habe, wie ich das nĂ€chste Problem beschreibe.

Das fĂŒhlt sich wie die wichtigere Kompetenz an. Nicht das Bauen. Das Beschreiben.

Wer lernt, Probleme prÀzise zu formulieren, wird nicht ersetzt. Er wird derjenige, der die Maschine anweist.

Und das ist, finde ich, eine ziemlich gute Position.


Dieser Artikel ist der erste einer losen Reihe ĂŒber Software, KI und die Frage, was eigentlich Wert hat.


TĂ€gliche Nachrichten per Mail – automatisch, kostenlos, selbst gebaut

Jeden Abend um 18 Uhr möchte ich wissen, was den Tag bewegt hat. Nicht die Startseite irgendeines Nachrichtenportals mit ihren Clickbait-Überschriften. Nicht ein Newsletter, den jemand anderes fĂŒr eine andere Zielgruppe zusammengestellt hat. Sondern meine Nachrichten – mit meinen Themen, in meiner Sprache, in meinem Format.

Also habe ich mir das gebaut.

Die Idee

Das Konzept ist simpel: Ein Python-Script lÀuft einmal tÀglich, fragt Gemini nach den wichtigsten Nachrichten des Tages, und schickt das Ergebnis als formatierte HTML-Mail an meine Adresse. Fertig.

Die interessante Frage war: Wo lĂ€uft das Script? Ein eigener Server wĂ€re Overkill. fly.io, Sprites, Cloud Functions – alles möglich, aber mit mehr Aufwand als nötig. Die Antwort lag direkt im Repository: GitHub Actions.

Das Repository existiert sowieso, weil ich Prompt und Konfiguration versionieren will. Wenn ich schon ein Repo habe, kann GitHub Actions den Cron-Job ĂŒbernehmen – kostenlos, prĂ€zise, ohne zusĂ€tzliche Infrastruktur.

Die Architektur

GitHub Repository
├── run.py          ← das eigentliche Script
├── config.json     ← Themen, EmpfĂ€nger, Format
├── prompt.md       ← Anweisungen ans KI-Modell
└── .github/
    └── workflows/
        └── news.yml  ← Cron-Job, tĂ€glich 18:00 Uhr

Der Ablauf bei jedem Run:

  1. GitHub Actions startet tÀglich um 16:00 UTC (= 18:00 CEST)
  2. run.py lÀdt config.json und prompt.md aus dem Repo
  3. Gemini 2.5 Flash recherchiert per Google Search Grounding die Nachrichten
  4. Das Script formatiert das Ergebnis als HTML-Mail
  5. Resend verschickt die Mail
  6. Der Runner beendet sich – keine laufenden Kosten

Das HerzstĂŒck: Google Search Grounding

Der entscheidende Unterschied zu einem normalen LLM-Call ist Google Search Grounding. Gemini hat damit direkten Zugriff auf aktuelle Webinhalte – kein Halluzinieren von gestrigen Nachrichten, keine veralteten Trainingsdaten.

Der API-Call sieht so aus:

body = {
    "system_instruction": {"parts": [{"text": prompt}]},
    "contents": [{"role": "user", "parts": [{"text": user_message}]}],
    "tools": [{"google_search": {}}]   # ← das ist der SchlĂŒssel
}

response = requests.post(
    f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={api_key}",
    json=body,
    timeout=60
)

Der google_search-Parameter aktiviert das Grounding. Gemini entscheidet wann und wonach es sucht – gesteuert durch den Prompt.

Der Prompt als Steuerzentrale

Der Prompt liegt als prompt.md Im Repository. Das ist der Hebel: Wer die Struktur der Mail Ă€ndern will, Ă€ndert eine Markdown-Datei und pusht. Beim nĂ€chsten Run um 18 Uhr ist die neue Version aktiv – kein Deployment, kein Rebuild.

Mein Prompt definiert eine feste Ausgabestruktur mit sieben Abschnitten: Top-Nachrichten, Thema des Tages, Deutschland, International, Technologie, Markt und Morgen im Blick. Dazu Stilregeln: immer Deutsch, keine Meinungen, keine Artikel Àlter als 24 Stunden, Quellenangaben in Klammern.

## Ausgabestruktur

### Top-Nachrichten
5–7 Schlagzeilen. **Fette Schlagzeile**, 2–3 SĂ€tze Einordnung. *(Quelle)*

### Thema des Tages
Das wichtigste Ereignis (~150 Wörter): Was? Hintergrund? Was folgt?

### Markt
- DAX (Schluss, VerÀnderung in %)
- EUR/USD
- Ölpreis Brent (USD/Barrel)
...

Die Konfiguration

Themen und EmpfÀnger stehen in config.json:

{
  "recipient_email": "ich@example.de",
  "sender_name": "Abendedition",
  "topics": [
    "Automotive & ElektromobilitÀt",
    "KĂŒnstliche Intelligenz & Technologie",
    "Deutsche Fiskal- und Wirtschaftspolitik",
    "Energie & Klimapolitik"
  ],
  "subject_template": "📰 Abendedition – {date}"
}

Der topics-Array landet im Prompt unter “Meine Themen” – Gemini sucht dann gezielt nach Neuigkeiten aus diesen Bereichen.

Der GitHub Actions Workflow

# .github/workflows/news.yml
name: Daily News Mailer

on:
  schedule:
    - cron: '0 16 * * *'   # 16:00 UTC = 18:00 CEST

jobs:
  send-news:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run news mailer
        run: pip install requests && python run.py
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
          SENDER_EMAIL:   ${{ secrets.SENDER_EMAIL }}

Secrets werden unter Repository → Settings → Secrets and variables → Actions Hinterlegt – nie im Code, nie in Dateien.

Im Winter muss der Cron auf. 0 17 * * * angepasst werden (CET statt CEST).

Was es kostet

DienstFree TierMein Verbrauch
Gemini 2.5 Flash500 Requests/Tag1–2/Tag
Resend100 Mails/Tag1–2/Tag
GitHub Actions2.000 Min/Monat~2 Min/Tag

Summe: 0 Euro pro Monat. Dauerhaft, keine Kreditkarte nötig.

Was ich gelernt habe

Prompt-Engineering ist das eigentliche Produkt. Das Python-Script ist in einer Stunde geschrieben. Den Prompt so zu formulieren, dass Gemini verlĂ€sslich die richtige Struktur liefert, keine Links erfindet und tatsĂ€chlich nur aktuelle Artikel verwendet – das hat mehr Iterationen gebraucht als der Code.

GitHub Actions als Cron ist unterschĂ€tzt. FĂŒr alles, was tĂ€glich einmal lĂ€uft und kein dauerhaft laufendes Deployment braucht, ist es die unkomplizierteste Lösung.


Schreibworkflow fĂŒr den Blog

Der Blog lÀuft, Drafts lassen sich veröffentlichen, Cloudflare baut zuverlÀssig. Was fehlt, ist die KerntÀtigkeit: das Schreiben.

Ich habe in den letzten Wochen verschiedene Wege ausprobiert, einen Post anzulegen, und gemerkt: Die Frage nach dem Wie lÀsst sich erst beantworten, wenn das Was klar ist. Sonst wechselt man alle zwei Wochen das Tooling und schreibt trotzdem keinen Post.

In diesem Post geht es um die Anforderungen an meinen Workflow, die Optionen, die ich evaluiert habe, und die Entscheidung, die am Ende dabei herauskam.

1) Anforderungen an meinen Workflow

Bevor ich Tools vergleiche, brauche ich eine Messlatte. Ein Workflow fĂŒr diesen Blog soll:

  • zweisprachig arbeiten, DE- und EN-Bundle in einem Schritt anlegen
  • Reibung minimieren (ein Kommando, nicht fĂŒnf)
  • lokal und in der Cloud identisch funktionieren
  • mit Git und Versionskontrolle harmonieren
  • Drafts klar von veröffentlichten Posts trennen
  • sich aus jedem beliebigen Editor heraus nutzen lassen

Er soll nicht ein CMS sein, keine WYSIWYG-OberflĂ€che liefern und keine AbhĂ€ngigkeit von einem externen Dienst aufbauen. Alles, was nicht in Git steht, existiert fĂŒr mich nicht.

2) Option A — Direkt im Editor

Die simpelste Variante: Im Editor zwei Dateien anlegen, Front Matter aus einem bestehenden Post kopieren, lostippen.

Vorteile: keine Lernkurve, kein zusĂ€tzliches Tooling, volle Kontrolle ĂŒber jede Zeile.

Nachteile: Jede Wiederholung ist eine mögliche Fehlerquelle. Man vergisst schnell ein Feld im Front Matter, benennt den Slug inkonsistent oder legt nur die deutsche Datei an und reicht die englische nie nach. Die bilinguale Paarbildung passiert im Kopf, nicht im Tool.

FĂŒr einen einmaligen Post tragbar. FĂŒr einen Blog, den ich langfristig pflegen will, zu fragil.

3) Option B — hugo new content

Hugo bringt ein eingebautes CLI mit, das aus einem Archetyp eine neue Content-Datei erzeugt.

hugo new content posts/mein-post/index.md

Der Archetyp unter archetypes/posts.md definiert, welches Front Matter vorgefĂŒllt wird. Das Ergebnis ist eine Datei mit konsistentem Skelett, ohne dass ich Zeilen kopieren muss.

Vorteile: offiziell, gut dokumentiert, null FremdabhÀngigkeiten.

Nachteile: Der Befehl legt nur eine Datei an. FĂŒr meine bilinguale Struktur mĂŒsste ich ihn doppelt ausfĂŒhren und das englische Bundle manuell drumherum bauen. Der Publish-Schritt (draft auf false, Commit, Push) bleibt außen vor.

Als Baustein gut, als Workflow zu lĂŒckenhaft.

4) Option C — make draft

Die dritte Option ist ein Makefile-Target, das hinter den Kulissen ein Shell-Skript aufruft:

make draft SLUG=mein-post

Das Skript legt das Bundle an: content/posts/mein-post/index.md und content/posts/mein-post/index.en.md, beide mit korrekt vorbelegtem Front Matter und draft: true.

ZusÀtzlich gibt es make publish (setzt draft: false) und make push (commit und push). Die drei Targets zusammen decken den Lebenszyklus ab.

Der entscheidende Punkt: Dieselben Skripte laufen auch in GitHub Actions. Es gibt keine zwei Wahrheiten — die Logik liegt an einer Stelle und wird sowohl vom Makefile als auch vom Workflow aufgerufen.

Vorteile: ein Kommando fĂŒr Setup, ein Kommando fĂŒr Publish, zwangslĂ€ufig konsistente Bundles, keine Logik-Duplikation zwischen lokal und online.

Nachteile: Die Skripte muss man bauen und pflegen. FĂŒr einen generischen Hugo-Blog wĂ€re das Overkill, fĂŒr meinen spezifischen bilingualen Fall passt es.

5) Option D — GitHub Actions Web-UI

Option C hat einen Zwilling im Browser. Unter Actions → Create Draft Post / Publish Draft Post lassen sich dieselben Skripte direkt im GitHub-Web ausfĂŒhren. Slug eintragen, auf Run workflow klicken, fertig — das Bundle entsteht, wird committet, Cloudflare baut.

Praktisch ist, dass die Aktionen denselben Code ausfĂŒhren wie die lokalen Make-Targets. Ich muss mich nicht fragen, ob ein online erstellter Draft anders aussieht als ein lokaler. Tut er nicht.

WofĂŒr ich das nutze: wenn ich an einem fremden Rechner sitze, kein Repo geklont habe oder schlicht faul bin. FĂŒr die eigentliche Schreibarbeit ist das Web-UI ungeeignet, aber zum Anlegen eines Platzhalter-Posts, wĂ€hrend mir auf dem Spaziergang eine Idee kommt, reicht es.

6) Entscheidung

make draft ist der Hauptweg.

Drei GrĂŒnde: Erstens passt er exakt zu meiner bilingualen Struktur, ohne dass ich im Kopf nachrechnen muss. Zweitens vermeidet er Logik-Duplikation — das Shell-Skript ist die einzige Wahrheit, egal ob lokal oder in der Cloud. Drittens ist er einfach genug, um ihn bei Bedarf in zehn Minuten zu lesen und zu erweitern, und komplex genug, dass er mir Arbeit abnimmt.

hugo new content bleibt als Backup, falls ich jemals einen einzelnen Post außerhalb der Bundle-Struktur brauche. Option D springt ein, wenn ich das Terminal nicht erreiche.

7) Mein Workflow in der Praxis

So sieht ein typischer Post-Zyklus bei mir aus:

  1. Idee notieren (Obsidian, Notizblock, egal wo)
  2. Am Schreibtisch: make draft SLUG=neuer-post
  3. Editor öffnet das DE-Bundle, ich schreibe los
  4. Parallel hugo server -D laufen lassen, um Drafts live zu sehen
  5. Englische Version im selben Bundle nachziehen
  6. make publish SLUG=neuer-post setzt draft: false
  7. make push MESSAGE="Post ĂŒber X" committet und deployt

Unterwegs lĂ€uft dasselbe ĂŒber Working Copy plus GitHub Actions Web-UI. Der Workflow ist robust genug, um auf beiden Wegen zu funktionieren, und einfach genug, dass ich ihn nach zwei Wochen Pause nicht neu lernen muss.

Fazit

Der Workflow ist Werkzeug, kein Selbstzweck. Jede Minute, die ich mit Tool-Tuning verbringe, ist eine Minute weniger beim Schreiben.

make draft hat bei mir gewonnen, weil es die Anforderungen am prĂ€zisesten abdeckt — nicht weil Makefiles per se ĂŒberlegen wĂ€ren. FĂŒr einen anderen Blog mit anderer Struktur wĂ€re die richtige Antwort vielleicht hugo new content oder ein CMS.

Wichtig ist nur, dass die Reibung beim Anlegen eines Posts irgendwann gegen Null geht. Dann entsteht aus der Frage „Wie fange ich an?" keine Ausrede mehr.


Zweisprachig mit Hugo: So bin ich vorgegangen

Mehrsprachigkeit klingt in Hugo erst simpel: Sprachen in der Konfiguration anlegen, Inhalte ĂŒbersetzen, fertig. In der Praxis stecken die Fehler aber in den Details.

Warum Mehrsprachigkeit, wenn Browser heute alles live ĂŒbersetzen können? FĂŒr mich ist die Antwort einfach: Ich denke auf Deutsch, schreibe ich besser auf Deutsch. Eine automatische Live-Übersetzung reicht mir aber nicht, weil ich beim englischen Text die Kontrolle ĂŒber Ton, Begriffe und Nuancen behalten will.

Kurz hatte ich die Idee, Deutsch zu schreiben, danach ins Englische zu ĂŒbersetzen und den deutschen Originaltext zu verwerfen. Das hat sich falsch angefĂŒhlt. Deshalb habe ich mich fĂŒr einen klaren Weg entschieden: Der Blog bleibt primĂ€r deutsch, und zusĂ€tzlich gibt es eine bewusst gepflegte englische Version.

In diesem Post zeige ich, wie ich Meadow Lark auf Deutsch und Englisch erweitert habe, worauf man achten sollte und welche Fehler bei mir konkret aufgetreten sind.

1) Saubere Basis in der Hugo-Konfiguration

Der erste Schritt ist eine klare Sprachkonfiguration in der Datei hugo.yaml.

Wichtig sind vor allem:

  • defaultContentLanguage auf ‘de’ setzen
  • beide Sprachen unter languages definieren
  • pro Sprache MenĂŒeintrĂ€ge pflegen
  • ein einheitliches Datumsformat pro Sprache setzen

Ich habe außerdem den Theme-Eintrag explizit gesetzt. Ohne aktives Theme können Layouts fehlen oder Seiten scheinbar leer rendern.

2) Inhalte als Sprachpaare anlegen

Ich arbeite mit Dateipaaren:

  • about.md und about.en.md
  • projects.md und projects.en.md
  • posts/slug.md und posts/slug.en.md

Das macht die Zuordnung eindeutig und Hugo kann Übersetzungen sauber verknĂŒpfen.

Wichtig: Jede Sprachversion braucht eigenes Front Matter mit sinnvoller Zusammenfassung. Diese sollte manuell geschrieben werden und nicht automatisch entstehen.

3) Sprachumschalter im Template

Der Umschalter steckt in einer wiederverwendbaren Navigation-Komponente. Das Prinzip:

  • Wenn eine Übersetzung fĂŒr die aktuelle Seite existiert, direkt dorthin linken
  • Wenn nicht, auf die Startseite der anderen Sprache verlinken

Der zweite Teil ist wichtig, damit Nutzer nicht auf einer Sackgasse landen.

4) Typische Fehler, die schnell passieren

Fehler 1: Theme nicht aktiv

Symptom: Startseite wirkt leer oder Layouts sind unvollstÀndig.

Ursache: Der Theme-Eintrag in hugo.yaml fehlt.

Fix: Theme explizit setzen, damit die Basis-Layouts geladen werden.

Wichtig: theme steht auf oberster Ebene in hugo.yaml, also nicht unter languages, params oder outputs.

baseURL: "https://meadowlark.care"
theme: "hugo-bearblog"

defaultContentLanguage: de
defaultContentLanguageInSubdir: false

Fehler 2: API-Unterschiede zwischen Hugo-Versionen

Symptom: Lokal funktioniert alles, im Cloud-Build knallt es im Template, weil zwei verschiedene Hugo-Versionen im Spiel sind.

Bei mir lief lokal Hugo 0.160, Cloudflare baute aber mit Hugo 0.146. Eine neuere Template-API lief deshalb lokal.

Fix: Versionen pinnen, damit lokal und Cloud dieselbe Hugo-Version nutzen (z. B. 0.160.1), und erst danach deployen. Falls das nicht geht, i18n-/Sprachumschalt-Logik auf kompatiblere Konstrukte umstellen. Danach erneut lokal bauen und remote deployen.

Ich habe dafĂŒr zwei Stellen gesetzt: in Cloudflare die Variable HUGO_VERSION=0.160.1 und lokal eine .tool-versions Datei mit hugo 0.160.1. Somit laufen lokale und Online-Builds mit derselben Basis.

Symptom: RSS-Links oder Sprachlinks zeigen in eine falsche Sprache oder auf 404.

Fix: Links ĂŒber Hugo-Output-Formate und RelPermalink aufbauen, statt Pfade hart zu codieren.

Fehler 4: Übersetzungen sind vorhanden, aber nicht verlinkt

Symptom: Die EN-Datei existiert, aber der Umschalter zeigt sie nicht.

Ursache ist inkonsistente Dateibenennung oder fehlende GegenstĂŒcke.

Fix: Sprachpaare konsequent benennen und Front Matter vergleichen.

5) Mein minimales Test-Set vor dem Push

Bevor ich pushe, gehe ich kurz durch:

  • hugo Build lokal ohne Fehler
  • Sprachumschalter auf Home und Post-Seiten prĂŒfen
  • DE/EN MenĂŒlinks anklicken
  • RSS-Link in beiden Sprachen testen

Gerade der Versionsunterschied zwischen lokalem Hugo und Cloud-Build hat mir gezeigt: Ein grĂŒner lokaler Build reicht nicht.

Fazit

Zweisprachigkeit mit Hugo ist gut machbar, wenn Konfiguration, Dateibenennung und Templates sauber zusammenspielen.

Die grĂ¶ĂŸten Stolperfallen sind nicht die Inhalte, sondern die kleinen Infrastrukturdetails: Theme-Aktivierung, stabile Template-APIs und robuste URL-Erzeugung.

Wenn diese drei Punkte sitzen, wird i18n in Hugo entspannt.


SQLite als Application File Format ↗

Die SQLite-Docs haben eine fantastische Seite darĂŒber, warum SQLite als Application File Format funktioniert. Die Argumente sind ĂŒberzeugend: ACID-Transaktionen, erweiterbar ohne Format-Migration, performant bei partiellen Reads, und universell toolbar.

Wer jemals ein eigenes BinÀrformat oder verschachteltes JSON als Speicherformat gebaut hat, kennt den Schmerz. SQLite löst das elegant.


New Achievement: Blog gestartet

Willkommen auf Meadow Lark.

Dieser Blog ist ab sofort mein Ort fĂŒr Notizen, Projekte und Gedanken rund um Tech, Self-Hosting, AI und alles andere, was mich beschĂ€ftigt. Kein Algorithmus, keine Kommentarsektion, keine Cookies — nur statisches HTML, serviert von Cloudflare Pages.

Warum ein Blog?

Weil gute Ideen in Chat-VerlĂ€ufen verschwinden. Weil Dokumentation auch fĂŒr einen selbst nĂŒtzlich ist. Und weil es 2026 wieder an der Zeit ist, seinen eigenen kleinen Fleck im Internet zu pflegen.

Was kommt?

  • Self-Hosting: Raspberry Pi, Pi-hole, Grafana, Starlink-Monitoring
  • AI & Tools: Claude Code, Workflows, Automatisierung
  • Projekte: Was ich gerade baue und warum
  • Notizen: Alles, was sich lohnt, festgehalten zu werden

Die Residenz hat ihre TĂŒren geöffnet. Auf geht’s.


Level 1 beginnt.