# Generated 495 achievement pages across three languages from a read‑only SQLite export, with the page address authored as data so that correcting a sentence no longer moved the page and broke the link.

2025

**Situation.** The company's portfolio content is authored in a separate database — the same one that produces the CV — and the website has to publish it as pages, in three languages, without the two copies drifting apart. The naive approach, writing the pages by hand and keeping them in step, fails on the first correction.

**Task.** The website had to generate its content from the database as a build input, with page addresses that survive the sentences on them being rewritten.

**Action.** An exporter reads the database read‑only and writes one page per achievement per language — 495 pages — plus the taxonomy and services data the templates need. It uses only the standard library, so the site's build has no dependency on the generator's environment, and the committed output means the site builds standalone. The single most consequential decision in it concerns addresses. The site used to derive a page's URL from the leading words of its English statement, so correcting a sentence silently moved the page and broke every link to it — a site whose argument for itself is that it corrects things, charging itself a dead link every time it did. The address is now authored as data: one row per address per achievement, the first is current and every later one is a retired address the site emits as a redirect. Two other traps are recorded from the same work. A period comparison against a text column silently matched all thirty‑two rows because of how the database assigns type affinity across a comparison. And the language list is now the single axis the write loop, the data files and the queries all derive from, so adding a fourth language is one map entry rather than a search.

**Result.** Three hundred and twenty‑one pages generate from one source, correcting a statement costs nothing, and the addresses this site has ever published continue to answer. The exporter also owns exactly one presentation decision — how services group into themed sections — and that is deliberate: everything else it writes is the database's, and a group heading missing a language fails the export rather than rendering English over translated content.

---

- Role: Test and Quality Engineer
- Categories: [Frontend Engineering](https://engineer.company/categories/frontend/), [Web Development](https://engineer.company/categories/web-development/), [Databases](https://engineer.company/categories/databases/), [SQL](https://engineer.company/categories/sql/), [Data Pipelines (ETL/ELT)](https://engineer.company/categories/data-pipelines/), [Python](https://engineer.company/categories/python/), [Internationalization](https://engineer.company/categories/i18n/)
- Services: [Website Development & CMS](https://engineer.company/services/web-development/), [Internationalization & Localization](https://engineer.company/services/i18n-localization/), [Data Pipeline Development (ETL/ELT)](https://engineer.company/services/data-pipeline-development/), [Brand, Marketing & SEO](https://engineer.company/services/brand-marketing/)

<https://engineer.company/portfolio/generated-321-achievement-pages-from-a-sqlite-export-132/>
