diff --git a/AGENTS.md b/AGENTS.md index 1fba27f..12ab4dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,8 +20,8 @@ Pagerite is a CMS. See `docs` for the full design and implementation details. Ke - `feeds.py` — machine-readable exports: `/llms.txt` (compact index linking each article's `.md` Markdown URL), `/feed.json` (JSON Feed 1.1), `/feed.xml` (RSS 2.0 + atom:link); the feeds carry all published articles with full content, linked from every page `` and the sitemap, recorded in analytics. - `data.py` — msgspec Structs for the kanta database. - `chunks.py` — block-level Markdown chunking and content-hash keys for the chunk stores (docs/migrate.md). - - `i18n.py` — language selection, translation assembly (chunks + overrides) and translated-edit recording (per-chunk user overrides in `Data.overrides`, per-language title overrides, refresh). - - `translate.py` — translator service protocol (msgspec structs), the connected-client `Dispatcher` (job pipeline, result validation) and pending/store core for the `/_translate/{key}` WebSocket (docs/localization.md); api.py only registers the route. + - `i18n.py` — language selection, translation assembly (chunks + overrides) and translated-edit recording (per-chunk user overrides in `Data.overrides`, per-language title overrides, refresh), plus the site-string translations (`site_string`/`set_string_translation`) for the contact label/subject (docs/messages.md). + - `translate.py` — translator service protocol (msgspec structs), the connected-client `Dispatcher` (job pipeline, result validation) and pending/store core for the `/_translate/{key}` WebSocket (docs/localization.md); api.py only registers the route. Pending title/nav work includes the site contact strings, so the contact chrome localizes automatically. - `segments.py` — the translation round trip: fragments split into pure-prose wire segments (via markdown.make_md's verbatim parser; link- and formatting-carrying blocks stay whole, link/formatted texts inline, Markdown stripped) and translations spliced back by source offset, link/formatting markdown re-inserted at weight-mapped positions (docs/localization.md). - `migrations.py` — kanta migrations (`migrate_vN`); ALL schema/storage upgrades live here (raw state dict before struct decoding), never in the app lifespan: v1 moves legacy in-db file blobs to the on-disk store and rebuilds the legacy flat `pages` as the menu tree, v2 rewrites `/_f/{hash}.ext` image links to the extension-less form, backfills AVIF/WebP/JPEG derivatives on disk and drops the obsolete `version` field. - `markdown.py` — markdown-it-py renderer. diff --git a/docs/localization.md b/docs/localization.md index 19ebeb8..edbd0cd 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -503,6 +503,13 @@ offerable to clients of another approach. placeholders lost is skipped individually and likewise stays pending for a scoped title job. +Site-level settings strings (the contact-messaging label/subject, +docs/messages.md) localize through the same machinery: they are emitted +as title-kind fragments by `pending_items` and ride nav jobs as extra +top-level list items; `i18n.site_string` looks them up at render time, +and manual overrides are edited in the site editor's messaging block +(`i18n.set_string_translation`). + `scripts/llm_translator.py` is the reference markdown+article+nav client (instruct LLMs via an OpenAI Chat Completions endpoint or ollama's native API); `scripts/translator.py` (Seed-X) is untouched and announces diff --git a/docs/messages.md b/docs/messages.md index 5455d24..f5ab9fb 100644 --- a/docs/messages.md +++ b/docs/messages.md @@ -11,12 +11,16 @@ existing WebSockets; there is no REST polling anywhere. - `pagerite/messages.py` — the feature's core: the strict message renderer, thread store ops, wire payloads, both socket protocols, the admin unread count and the `/_contact/files` upload route. -- `pagerite/data.py` — `Data.contact` (`ContactThread`/`ContactMessage`). +- `pagerite/data.py` — `Data.contact` (`ContactThread`/`ContactMessage`) + and the site settings (`contact_enabled`, `contact_label`, + `contact_email`, `contact_subject`). - `pagerite/tracking.py` — the sockets the protocol rides (see below). - `pagerite/api.py` — `/_api/settings` carries `contact_unread` (the admin - icon badge seed — the probe runs on every page load, no polling). -- `pagerite/views.py` — the `{contact:}` directive (`_contact_tag`) and the - on-demand bundle's asset config. + icon badge seed — the probe runs on every page load, no polling) and the + contact configuration (with `?lang=` overrides for the label/subject). +- `pagerite/views.py` — the `{contact:}` directive (`_contact_tag`), the + localized contact strings (`_contact_config`) and the on-demand bundle's + asset config. - `frontend/src/contact-main.js` + `ContactOverlay.vue` — the overlay entry and shell (paskia's shared blur backdrop, centered dialog, full-screen on mobile), imported on demand by `pagerite.js` on icon/trigger clicks. @@ -30,12 +34,40 @@ existing WebSockets; there is no REST polling anywhere. A lone `{contact: Label}` line in article Markdown (the block-directive machinery of `markdown.py`) renders -`` ("Contact us" when the -label is empty). `pagerite.js` click-delegates these buttons and lazy-loads +``. An empty label falls +back to the configured site label (`Data.contact_label`, localized — +below), ultimately the built-in "Contact us". `pagerite.js` +click-delegates these buttons and lazy-loads the contact bundle; the editor preview renders the same button inert, and -description/card mining resolves the tag to nothing. The label lives in the -chunk, so translated pages localize it like any other text. Feeds render the -tag as its literal source (no directive context). +description/card mining resolves the tag to nothing. A per-button label +lives in the chunk, so translated pages localize it like any other text. +Feeds render the tag as its literal source (no directive context). When +messaging is disabled the tag renders nothing at all. + +## Site settings and localization + +The site editor's messaging block holds the feature's configuration +(`/_api/settings`): `contact_enabled` (off = no trigger buttons, no +visitor corner icon, sends rejected; admins keep the inbox for existing +threads), `contact_email` (a mailto link in the visitor dialog, its +subject prefilled) and the `contact_label`/`contact_subject` strings +(the label doubles as the dialog's title and the buttons' default; the +subject defaults to the label). + +The two strings localize like titles: translations live in the +chunk-keyed `Data.trans` store (`i18n.site_string` looks them up at +render time — the *effective* label, so the built-in default localizes +too) and the translator service picks them up automatically +(`pending_items` emits them as title-kind fragments; nav-mode jobs carry +them as extra top-level list items). Manual overrides are edited in the +same messaging block: its language selector switches the label/subject +fields to a translation, saved via `PUT /_api/settings?lang=` into the +same store (`i18n.set_string_translation`; empty or the original's text +drops the override). The localized strings reach the client through the +page's asset config (`pagerite:contact-label` / `-subject` / `-email`, +absent entirely when disabled — pagerite.js reads that as "visitors get +no icon"); `contact-main.js` re-reads them on every open, so a language +switch is reflected immediately. ## Visitor side @@ -179,4 +211,6 @@ Appends are single transactions (`contact` / `contact:reply` / - Message source ≤ 8000 chars; uploads ≤ 20 MB and ≤ 10 pending per session; payloads cap at 100 threads × last 200 messages. - No thread moderation (delete/block), no orphan-upload GC for files - orphaned by a server restart, and panel strings are English-only. + orphaned by a server restart, and the panels' own UI strings (composer + hints, "you", inbox labels) are English-only — the *configurable* site + strings (label, subject) do localize (above).