Translate the whole site to English: English routes with 301 redirects from the Dutch paths, English UI, docs, seeds and captions, one-time live content migration

This commit is contained in:
2026-10-05 11:25:41 +02:00
parent bbdecdc95b
commit ff547e1677
153 changed files with 1095 additions and 774 deletions
+17
View File
@@ -0,0 +1,17 @@
# Admin and services
*Admin, Services* lists every background service. Each service has an overview, configuration and logs. Start, stop, run now and clear logs are database writes the service picks up within a second.
| Service | Purpose |
|---------|---------|
| presence | publishes the online set |
| housekeeping | clears expired sessions, prunes the audit log |
| backup | works through the backup queue and schedules automatic backups |
## Intro
The intro at the top of the home page lives in the `site_intro` setting (markdown, line breaks are kept). Edit it under *Settings*; the first paragraph is set large.
## Maintenance mode
Set `maintenance_mode` to `1` under *Settings*. Administrators keep seeing everything.
+17 -13
View File
@@ -1,24 +1,28 @@
# API en authenticatie
# API and authentication
Elke route heeft vier gezichten: HTML, JSON, documentatie en (waar geselecteerd) een assistent-tool. Vraag JSON met `Accept: application/json`.
Every route has four faces: HTML, JSON, documentation and (where selected) an assistant tool. Ask for JSON with `Accept: application/json`.
## Authenticatie
## Authentication
Volgorde van resolutie:
Resolution order:
1. `session` cookie (64 hex tekens) na inloggen op `/auth/login`.
2. `X-API-KEY` header met je API-sleutel.
3. `Authorization: Bearer <sleutel>`.
4. `Authorization: Basic` met naam of e-mail en wachtwoord.
1. `session` cookie (64 hex characters) after logging in at `/auth/login`.
2. `X-API-KEY` header with your API key.
3. `Authorization: Bearer <key>`.
4. `Authorization: Basic` with name or email and password.
Er is geen JWT en geen OAuth.
There is no JWT and no OAuth.
## Voorbeeld
## Example
```bash
curl -H 'Accept: application/json' https://molodetz.nl/rol
curl -H 'Accept: application/json' https://molodetz.nl/roll
```
## Fouten
## Errors
Fouten hebben de vorm `{"error": {"status": 404, "message": "..."}}`. Validatiefouten geven `422` met `{"error": "validation", "fields": [...], "messages": [...]}`.
Errors have the shape `{"error": {"status": 404, "message": "..."}}`. Validation errors return `422` with `{"error": "validation", "fields": [...], "messages": [...]}`.
## Old paths
The Dutch paths from before (`/rol`, `/standaard`, `/mensen`, `/binnen`, `/voorwaarden`) answer with a 301 to their English replacement. Query strings are kept.
@@ -0,0 +1,18 @@
# Architecture
FastAPI with Jinja2, rendered on the server. SQLite through `dataset`, synchronous and deliberately without a threadpool. Sessions with PBKDF2-SHA256 passwords.
## Layers
- `molodetz/database/`: one file per subject, always import from the package root.
- `molodetz/routers/`: mirrors the URL path.
- `molodetz/services/`: background services under one supervisor with a file lock.
- `molodetz/templates/`: one Jinja environment in `templating.py`.
## Deleting
Everything is soft deleted with `deleted_at` and `deleted_by`. Only cleanup jobs delete for real.
## Static files
Assets go through `static_url()`, which versions them under `/static/v<version>/`. Those are immutably cacheable for a year.
@@ -1,18 +0,0 @@
# Architectuur
FastAPI met Jinja2, server-side gerenderd. SQLite via `dataset`, synchroon en bewust zonder threadpool. Sessies met PBKDF2-SHA256 wachtwoorden.
## Lagen
- `molodetz/database/`: één bestand per onderwerp, importeer altijd vanaf de package-root.
- `molodetz/routers/`: spiegelt het URL-pad.
- `molodetz/services/`: achtergrondservices onder één supervisor met een bestandsslot.
- `molodetz/templates/`: één Jinja-omgeving in `templating.py`.
## Verwijderen
Alles wordt zacht verwijderd met `deleted_at` en `deleted_by`. Alleen opruimtaken verwijderen hard.
## Statische bestanden
Assets lopen via `static_url()`, wat ze versioneert onder `/static/v<versie>/`. Die zijn een jaar onveranderlijk cachebaar.
+3 -3
View File
@@ -1,6 +1,6 @@
# Back-ups en onderhoud
# Backups and maintenance
Doelen: `database`, `uploads`, `keys`, `full`. Archieven worden gecomprimeerd met zstd of brotli en staan onder `data/backups/`. Alleen de primaire beheerder kan downloaden.
Targets: `database`, `uploads`, `keys`, `full`. Archives are compressed with zstd or brotli and live under `data/backups/`. Only the primary administrator can download them.
## CLI
@@ -11,4 +11,4 @@ molodetz backups prune --keep 14 --dry-run
molodetz system prune --dry-run
```
Het onderhoudsscript `bin/maintenance.sh` draait alle prunes en toont schijfgebruik voor en na.
The maintenance script `bin/maintenance.sh` runs every prune and shows disk usage before and after.
-15
View File
@@ -1,15 +0,0 @@
# Beheer en services
Onder *Beheer, Services* staan alle achtergrondservices. Elke service heeft een overzicht, configuratie en logs. Starten, stoppen, nu draaien en logs wissen zijn databaseschrijfacties waar de service binnen een seconde op reageert.
| Service | Doel |
|---------|------|
| presence | publiceert de online-set |
| housekeeping | ruimt verlopen sessies op, snoeit het auditlog |
| backup | verwerkt de back-upwachtrij en plant automatische back-ups |
## Onderhoudsmodus
De intro bovenaan de homepage staat in de instelling `site_intro` (markdown, regelovergangen blijven staan). Bewerk hem onder *Instellingen*; de eerste alinea wordt groot gezet.
Zet `maintenance_mode` op `1` onder *Instellingen*. Beheerders blijven alles zien.
-5
View File
@@ -1,5 +0,0 @@
# Binnenkomen
Het formulier op [/binnen](/binnen) slaat een aanmelding op: naam, contact, optioneel een link naar je werk en een bericht. Beheerders krijgen een melding en zien de aanmelding onder *Beheer, Aanmeldingen*.
Er is geen automatische toegang. retoor leest elke aanmelding en neemt zelf contact op.
@@ -1,5 +1,5 @@
# dp-avatar
Toont een deterministische SVG-avatar plus presence-stip. Attribuut `seed`, `size`, `uid`.
Shows a deterministic SVG avatar plus a presence dot. Attributes `seed`, `size`, `uid`.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-chat
Eenvoudige log met invoerregel. Geeft een `dp-chat-send` event.
Simple log with an input line. Emits a `dp-chat-send` event.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-code
Markeert een codeblok met highlight.js en voegt een Kopieer-knop toe.
Highlights a code block with highlight.js and adds a Copy button.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-content
Rendert live markdown aan de clientkant via marked, daarna DOMPurify. Alleen voor live inhoud, zoals het editorvoorbeeld.
Renders live markdown on the client through marked, then DOMPurify. Only for live content, such as the editor preview.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-context-menu
Klein contextmenu met knoppen, gepositioneerd bij de cursor.
Small context menu with buttons, positioned at the cursor.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-dialog
Bevestigingsdialoog. Formulieren met `data-confirm` lopen hierlangs.
Confirmation dialog. Forms with `data-confirm` go through it.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-lightbox
Volledig scherm voor afbeeldingen met `data-lightbox`. Escape of klik sluit.
Full screen view for images with `data-lightbox`. Escape or a click closes it.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-title
Inline-variant van dp-content voor titels.
Inline variant of dp-content for titles.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-toast
Host voor korte meldingen. Gebruik `Toast.flash(el, bericht)` of `app.toast(bericht)`.
Host for short notices. Use `Toast.flash(el, message)` or `app.toast(message)`.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
@@ -1,5 +1,5 @@
# dp-upload
Sleepvlak voor bestanden. Geeft een `dp-files` event met de gekozen bestanden.
Drop zone for files. Emits a `dp-files` event with the chosen files.
Light DOM, geen shadow DOM. Registreert zichzelf met `customElements.define`.
Light DOM, no shadow DOM. Registers itself with `customElements.define`.
+4 -4
View File
@@ -1,7 +1,7 @@
# Frontend en componenten
# Frontend and components
ES6-modules, één klasse per bestand, geen framework en geen bundler. `Application.js` maakt alle singletons aan en zet `window.app`.
ES6 modules, one class per file, no framework and no bundler. `Application.js` creates all singletons and sets `window.app`.
Componenten zijn custom elements met het voorvoegsel `dp-`, zonder shadow DOM. Zie de componentpagina's in de zijbalk.
Components are custom elements with the `dp-` prefix, without shadow DOM. See the component pages in the sidebar.
Clientrendering met marked en DOMPurify is alleen voor live inhoud: het voorbeeld in de editor. Gepubliceerde berichten worden op de server gerenderd.
Client rendering with marked and DOMPurify is only for live content: the preview in the editor. Published posts are rendered on the server.
+5
View File
@@ -0,0 +1,5 @@
# Joining
The form at [/join](/join) stores a join request: name, contact, an optional link to your work and a message. Administrators get a notification and see the request under *Admin, Join requests*.
There is no automatic access. retoor reads every request and gets in touch himself.
@@ -1,6 +0,0 @@
# Voorwaarden en privacy
- [Voorwaarden](/voorwaarden)
- [Privacy](/privacy)
Molodetz heeft geen advertenties, geen tracking over apps heen, geen betalingen en geen sociale login.
+6
View File
@@ -0,0 +1,6 @@
# Terms and privacy
- [Terms](/terms)
- [Privacy](/privacy)
Molodetz has no ads, no cross-app tracking, no payments and no social login.
@@ -1,17 +0,0 @@
# Schrijven op de rol
Berichten zijn markdown en staan in SQLite. Alleen beheerders schrijven. Een bericht heeft een titel, een onderwerp (`rol` of `standaard`), een status (`draft` of `published`) en optioneel de markering *placeholder*.
## Markdown
De server rendert met GFM: tabellen, doorhalen, harde regeleinden. HTML in de tekst wordt ge-escaped. Links mogen alleen `http`, `https`, `mailto` en `tel` zijn. Emoji schrijf je als `:shortcode:`, bijvoorbeeld `:fire:`.
Een losse afbeeldings-URL wordt een afbeelding, `@naam` wordt een link naar het profiel.
## Placeholders
De eerste notities zijn overgenomen uit de oude Molodetz-pagina en dragen het label *Placeholder*. retoor vervangt ze door eigen verhalen. Haal het vinkje weg bij het bewerken zodra een bericht echt is.
## Slugs
Een slug is permanent: titel plus de laatste twaalf tekens van de uid. Bewerken verandert de slug niet.
+15
View File
@@ -0,0 +1,15 @@
# Welcome to Molodetz
Molodetz is a quiet blog roll. Notes about the work, finishing it, and judgement. This documentation describes how the site works, how to use the API and how admin keeps the whole thing running.
## What is here
- **Roll**: short notes, newest first.
- **Standard**: what Molodetz stands for.
- **Flyers** and **Memes**: two galleries.
- **People**: who writes here.
- **Join**: the form to write along.
## Where to start
Read [Writing on the roll](/docs/writing) for the content, [API and authentication](/docs/api) for integrations, and [Architecture](/docs/architecture) if you want to know how it is built.
-15
View File
@@ -1,15 +0,0 @@
# Welkom bij Molodetz
Molodetz is een rustige blogrol. Notities over het werk, het afmaken en het oordeel. Deze documentatie beschrijft hoe de site werkt, hoe je de API gebruikt en hoe beheer de boel draaiende houdt.
## Wat er is
- **Rol**: korte notities, nieuwste eerst.
- **Standaard**: waar Molodetz op staat.
- **Flyers** en **Memes**: twee galerijen.
- **Mensen**: wie er schrijft.
- **Binnen**: het formulier om mee te schrijven.
## Waar te beginnen
Lees [Schrijven op de rol](/docs/schrijven) voor de inhoud, [API en authenticatie](/docs/api) voor integraties, en [Architectuur](/docs/architectuur) als je wilt weten hoe het gebouwd is.
+17
View File
@@ -0,0 +1,17 @@
# Writing on the roll
Posts are markdown and live in SQLite. Only administrators write. A post has a title, a topic (`roll` or `standard`), a status (`draft` or `published`) and an optional *placeholder* marker.
## Markdown
The server renders GFM: tables, strikethrough, hard line breaks. HTML in the text is escaped. Links may only be `http`, `https`, `mailto` and `tel`. Write emoji as `:shortcode:`, for example `:fire:`.
A bare image URL becomes an image, `@name` becomes a link to the profile.
## Placeholders
The first notes were carried over from the old Molodetz page and carry the *Placeholder* label. retoor replaces them with his own stories. Untick the box while editing once a post is real.
## Slugs
A slug is the title plus the last twelve characters of the uid. Editing does not change the slug. If a title is ever rewritten in the database, the old slug still resolves through its uid tail and redirects with 301 to the canonical one.