Algroveon-Mini-SSG – How a script became a tool

From a simple Python script to a complete static site generator with multi-language support, translation, and an admin UI.

Zusammenfassung

The Algroveon-Mini-SSG has evolved from a simple Python script into a comprehensive tool for generating static websites from Markdown files. The system offers features such as a browser-based admin UI, AI-powered translations, and structured handling of multilingualism.


Diese Zusammenfassung wurde mit KI-Unterstützung erstellt.

It started with an idea and a small Python script. I wanted to move away from the complex WordPress CMS sites I usually use—towards a leaner system with more control and less bloat. Over time, this evolved into a full-fledged Static Site Generator: a system that produces a finished static website from Markdown files, templates, and configuration. Such systems have been on the market for a long time, such as Hugo, Eleventy, Jekyll, or Astro. However, my system took a slightly different direction over time—with a browser-based admin UI, AI-powered translation, and direct deployment to the web server. Everything runs in the browser.


Phase 1: The Script

The original requirement was simple: a personal website, content in Markdown, a few Jinja2 templates—templates used to fill fixed HTML structures with variable content. I didn't need Hugo, Eleventy, or a massive framework—just a Python script that reads Markdown files and generates HTML from them. I'll explain later why that wouldn't be enough.


Phase 2: Multilingualism

The website needed to support German and English. That sounds like little effort at first glance.

The real problem isn't the translation, but the underlying structure. The German version sits under /de/, and the English version under /en/. Navigation must be rendered correctly for each language. Links between language versions must be accurate. And if an English translation is missing, a sensible fallback logic should kick in instead of simply ending up on a 404 page.

Then there were the filenames. 2024-11-17-my-article.md—the date is already embedded there. But what happens if a date is also maintained in the frontmatter—the header section of the Markdown file containing metadata like date, title, or tags? Which one takes precedence? And how do you handle European date formats like 15.03.2025 or written-out dates in German or English? As a result, the parse_date system has grown gradually and now supports six formats across four languages.


Phase 3: The Admin UI

Originally, I just wanted to write a blog post. Instead, I had to open the terminal, navigate to the correct folder, create a Markdown file, edit it in an editor, run python build.py, and then check in the browser if everything looked right. For occasional work, that's fine, but it's not a sensible workflow in the long run.

So the idea was simple: a small web interface that can list, open, and save files. It was supposed to be a one-afternoon project. In the end, it was the decision that had the greatest impact on the entire project.

Because as soon as there was an admin interface, it was only natural to trigger the build from there as well. Then the deploy. Then creating new pages. Then displaying the build output directly. Then starting translations. The scope grew and grew. Today, the Admin UI is the actual heart and soul of the SSG. Build, deploy, translation, and the entire editorial process all converge there.

What unexpectedly required a lot of effort was the Markdown editor. A simple textarea would have been technically sufficient. But with split-live-preview and a toolbar for the most common formatting options, it becomes a real tool. This is exactly why the editor eventually became its own sub-project—the story behind it can be found in the separate Markdown Editor Article.


Phase 4: Deploy without a Terminal

I implemented the build quickly—a button that calls build.py and writes the output to the browser via an SSE stream.

The deploy process became more complex. FTP and SFTP are two different protocols and require different Python libraries. An incremental sync is necessary so that not all files are re-uploaded with every deploy. MD5 hashes fundamentally solve this, but it requires logic that cleanly compares local and remote files. I didn't want to store passwords in plain text in the configuration—so I used the OS keychain via keyring. Additionally, I added a connection test before the actual deploy so that you don't realize the credentials are wrong only after a minute has passed.

None of this is particularly spectacular on its own. But it consists of many small building blocks that must work together reliably. In the end, there is a deploy button in the browser that reports after a short time how many files were uploaded and which ones were skipped because they remained unchanged.


Phase 5: AI-Powered Translation

In practice, multilingualism primarily means double the editorial effort. Writing every article manually in English as well is simply too much for a hobby project. That's why translate.py was created.

The first version was appropriately direct: send the entire Markdown content to the Ollama API, receive the translation, and write it to the target file. This worked—at least for shorter texts. With longer articles, the quality suffered, and some models began translating code blocks or altering the frontmatter.

The subsequent logic therefore works in blocks and stores a SHA-256 hash of the source content in the target file. If the source text changes, the system recognizes the translation as outdated and specifically re-translates only that one file—without touching anything else. Code blocks are preserved, and frontmatter fields are either selectively translated or intentionally excluded. The entire process runs with live output in the browser.

What actually surprised me: how useful local models have become for this kind of task. Gemma-4 on a local Ollama instance provides translations for technical texts that are good enough to work with. Tone and naturalness sometimes still require a second look—but as a first draft, it is already quite decent. All English translations on this website were created this way using Gemma-4.


What I Would Do Differently Today

Define the scope of the Admin UI earlier. Feature creep was the project's biggest time-waster. At the same time, this exact development is what made the tool truly useful in daily life—instead of just remaining a clean but too small script.

Keep templates cleanly separated from the start. The division between themes/ and site-specific template overrides in sites/<site>/templates/ came relatively late. Before that, a theme was effectively tied to a single site. Separating this cleanly earlier would have saved a lot of refactoring.


Where It Stands Today

Algroveon-Mini-SSG is long no longer the 80-line script it was at the beginning. It is a complete tool with a clear purpose: building your own website, multilingual, without external services and without a classic build toolchain.

Is this useful for others? Perhaps. Anyone who just wants to build a personal website is very likely better served by WordPress, especially as a beginner. Writing your own generator is not a rational standard way to build a website. In my case, it was a learning project that became productive over time.

What is still missing: an incremental build, a formal plugin system, and more robust error handling for broken Markdown files. These are the points that will eventually come once the pressure to have them becomes great enough. Until then, the rule is: it works.

Sebastian Software Engineer &amp; Wildlife Photographer
← ← Back to blog