aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
diff options
context:
space:
mode:
Diffstat (limited to 'AGENTS.md')
-rw-r--r--AGENTS.md33
1 files changed, 31 insertions, 2 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 4ff23f8..97798dc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -52,6 +52,8 @@ internal/store/ Store = control DB (users, blog registry, create/delete bl
BlogStore = one blog's DB (settings, pages, posts, images, sections, modules, menu)
internal/auth/ bcrypt, JWT issue/parse, cookie, HMAC CSRF
internal/markdown/ Render(md) → sanitized HTML
+internal/i18n/ languages, T/Tf (English keys → catalog), FormatDate/Month, Accept-Language Match;
+ el.go is the Greek catalog, el_months.go the month tables
internal/slug/ Make/Valid/WithSuffix (Greek transliteration included)
internal/web/ server.go (host router, middleware, render helpers)
routes.go (all routes), handlers_*.go (sections = announcements, layout = modules + menu)
@@ -65,7 +67,7 @@ internal/web/ server.go (host router, middleware, render helpers)
- **One database per blog**: the registry row (`blogs`: id, owner, subdomain,
`db_name`) lives in the control DB; everything else — a one-row `settings`
- table (title, tagline, theme) plus pages, posts, images, sections, modules,
+ table (title, tagline, language, theme) plus pages, posts, images, sections, modules,
menu_items — lives in `blog_<sub>` (`db.DBName`: dashes → underscores, so
subdomains are capped at 58 chars). No table in a blog DB carries a
blog id; the database is the scope. `store.Store` (control) hands out a
@@ -189,6 +191,33 @@ internal/web/ server.go (host router, middleware, render helpers)
`notice notice-<style>` use translucent colours so they fit any theme.
- **Dashboard nav**: `dashnav.html` marks the current tab with `hasPrefix
.Path`; a new management section needs a tab there.
+- **Languages** (`internal/i18n`, `settings.language`): every blog has a
+ language (`en` by default, `el` = Greek) chosen on the Settings tab. Public
+ pages use the blog's language; management pages use the **logged-in user's
+ own blog's** language (a user has one blog, so a superadmin editing someone
+ else's blog keeps theirs); anonymous pages (`/webadmin`) match
+ `Accept-Language`. `server.go`: `hostBlog`/`withBlog` put it in `ctxLang`,
+ `s.lang(r)` resolves it (lazily looking the user's blog up otherwise), the
+ `view` carries it as `.Lang` (also the `<html lang>`).
+ Keys are the English strings: templates write `{{t "Save"}}` /
+ `{{tf "Page %d of %d" .N .Last}}`, handlers `s.tr(r, "Post deleted.")` /
+ `s.trf(r, "Uploaded %s.", name)`; an unknown key renders as English.
+ Templates are compiled once **per language** (`templates.get(lang, name)`)
+ with `funcsFor(lang)`, whose `t`, `tf`, `date` (dashboard, long style),
+ `postdate` (`style, time` for the theme's `DateFormat`), `month`
+ (archive headings) and `modsummary` are closed over the language — so
+ templates need no data plumbing. A sentence with a link in the middle is
+ split into fragments around the `<a>`. `moduleNames`/`areaNames`/preset
+ names stay English maps and are translated where used (`{{t (index …)}}`,
+ `s.tr(r, moduleNames[k])`). Content the blogger typed is never translated;
+ default module headings are created in the blog's language.
+ `TestGreekCatalogComplete` scans the templates and handlers for keys and
+ fails when the Greek catalog misses one or keeps a stale one — reword an
+ English string and the catalog entry must follow. Keys reached through a
+ variable (`{{t .Role}}`) are listed in that test by hand.
+ Adding a language: an entry in `i18n.languages`, a catalog file, a month
+ table in `monthTables` (nominative, in-date and short forms), and the test
+ gets a copy per language if its rules differ.
- **Slugs**: auto-generated from the title; on collision generated slugs
get `-2`, `-3`…, user-typed slugs return a 409 with a message.
- **Errors**: `store.ErrNotFound` / `store.ErrConflict` are the sentinels
@@ -217,7 +246,7 @@ superadmin password to `admin`. Production refuses both.
(runs in every blog database — it must not reference `users`/`blogs`) and
`internal/db/migrations/control/` for users and the registry; goose
`-- +goose Up/Down` sections; they run automatically at startup. Never
- edit an applied migration. Both chains were re-baselined at 00001 after
+ edit an applied migration. Blog chain so far: `00001_init`, `00002_language`. Both chains were re-baselined at 00001 after
the move to per-blog databases; deployments from before it have
`goose_db_version` rows 2–7 in the control DB that must be deleted once
(README "Upgrading from a single database") or the next control migration