serve language variants under a prefix
The default locale stays at the root; every other language is the same key under
/{lang}/ (ADR-0009). /en/… is never live and redirects to the root form so the URL
space cannot fork. Lookup now takes a language and reports which one it served,
following requested → default → any rather than 404ing when a translation is
missing.
That is the second routing case, so the resolver is extracted to resolve.go and
the mux keeps one entry: URL shape is the resolver's business. A leading segment
counts as a language only when some bundle is written in it, so an unknown prefix
is a 404 rather than a stripped path — and a section may not be named after a
language in use, now recorded in content-model.md.
Because the served variant can differ from the URL requested, Page gained
.Canonical (the variant actually served) and .Alternates for hreflang. A theme
must never build a path, so both come from the engine.
Evidence: /bn/pages/about/ serves the Bengali body with lang="bn" and canonical
/bn/pages/about/; /bn/posts/hello-world/ falls back to English with canonical
/posts/hello-world/; /en/pages/about/ 301s to /pages/about/; /fr/… is 404.
This commit is contained in:
+4
-3
@@ -1,6 +1,6 @@
|
||||
# State
|
||||
|
||||
**Verified against:** `HEAD` on 2026-07-30 — update this line every change.
|
||||
**Verified against:** `51143d3` on 2026-07-30 — update this line every change.
|
||||
If this file disagrees with the code, the code is right and this file is a bug.
|
||||
|
||||
## Inventory
|
||||
@@ -11,7 +11,8 @@ If this file disagrees with the code, the code is right and this file is a bug.
|
||||
| `internal/content/content.go` | site root → bundles: `os.Root` open, walk, frontmatter split, key/lang derivation, NFC, collision drop, key index | 245 |
|
||||
| `internal/render/render.go` | goldmark + the embedded reference theme; `Page` is what templates receive | 92 |
|
||||
| `internal/render/templates/` | reference theme: `base.html`, `theme.css` (ADR-0026) | — |
|
||||
| `internal/web/web.go` | one routing case: path → bundle key, canonical trailing slash, 404, degrade on render failure | 62 |
|
||||
| `internal/web/resolve.go` | URL → (key, lang) or a canonical redirect: language prefix, `/en/…` fork guard, trailing slash | 56 |
|
||||
| `internal/web/web.go` | handler: resolve, look up with fallback, render, degrade on failure | 62 |
|
||||
| `cmd/khosra/main.go` | flags, wiring, startup — the only place things are assembled | 50 |
|
||||
| `*_test.go` | table-driven; symlink escape, permalink, redirect, 404 | 245 |
|
||||
|
||||
@@ -28,7 +29,7 @@ this change*.
|
||||
| Counter | Now | Extraction due at | What it buys |
|
||||
|---|---|---|---|
|
||||
| Render transforms | 0 | **3** | Stage pipeline (ordered `func(ctx,*Page) error`) |
|
||||
| Routing cases | 1 | **2** | Resolver extraction |
|
||||
| Routing cases | 2 | **2** — done | Resolver extracted at `internal/web/resolve.go` |
|
||||
| Collection pages | 0 | **1** | Query primitive |
|
||||
| Views / output formats | 1 | **2** | View layer (contract per `theme-contract.md`) |
|
||||
| Effects | 0 | **2** | Effect runner + trigger wiring (change / schedule / demand) |
|
||||
|
||||
Reference in New Issue
Block a user