Multilingual
Language sets - sibling per-language sites the engine understands.
Multilingual
A multilingual lazysite is a set of sibling domains - one per language - each a multi-site content root, declared as members of one language set (SM179). The engine knows the siblings, so it supplies every layout with ready-made switcher data, emits correct hreflang alternates in heads and sitemaps, and reports translation coverage. Translation itself stays in plain files, editable by tools and humans alike.
Configuration
Each member host declares its own lang (a BCP 47 tag) and a shared
lang_group (the set name), settable in lazysite.conf, via
domain-set, or on the Domains Add/Configure forms:
lang: en
lang_group: providers
alias_hosts: providers.example, de.providers.example
alias.providers.example.content_root: sites/providers
alias.providers.example.site_url: https://providers.example
alias.providers.example.lang: en
alias.providers.example.lang_group: providers
alias.de.providers.example.content_root: sites/providers-de
alias.de.providers.example.site_url: https://de.providers.example
alias.de.providers.example.lang: de
alias.de.providers.example.lang_group: providers
lang alone marks a monolingual site that knows its language: it lands
in <html lang> and the Content-Language header (a page may override
with front-matter lang:; both are sanitised to a bare tag first). A
set needs at least two members; the base host, when it is a member, is
the source language, otherwise the first member is.
The sibling-mirror convention
Sibling roots mirror the source root's file layout: compare.md in one
language corresponds to compare.md in every other, and a missing file
means "not translated yet". No manifest, no mapping table - the tree is
the mapping. Layout, theme and assets are shared; front matter, body
Markdown and data JSON are translated per root (values, never keys or
structure).
The switcher and hreflang
For every rendered page the engine supplies [% languages %]: an
ordered list of { lang, url, current, exists }, where url is the
sibling's site_url plus the same path and exists stats the
counterpart page. A layout renders a switcher from it in a few lines of
TT; the default layout includes one, listing only languages whose
counterpart exists. The same data drives <link rel="alternate"
hreflang> pairs in the head (plus x-default on the source language),
and per-domain sitemaps gain xhtml:link alternates for entries whose
counterparts exist - so search engines see the set correctly with no
hand-rolled markup.
Shared data and strings
json:page-variable sources resolve against the requesting domain's content root first, then the docroot - sott_page_var: nav: json:/data/nav.jsonis byte-identical across roots, and a per-language data file transparently overrides a shared one;::: includeis likewise resolved within the content root- Layout chrome resolves through
[% t %]:layouts/<layout>/strings/<lang>.jsonoverlaysstrings/en.json, so a key missing from the site language falls back to English rather than vanishing - The engine's own emitted pages (the bare 404, auth reject pages) are
localised from a built-in English table overlaid by
lazysite/i18n/<lang>.json, fail-closed to English
Translation coverage
The lang-status action (control API and MCP, gated manage_content)
compares each sibling root to the source root, file by file, and
reports each page as current, stale or missing, with totals per
root. Staleness is mtime-based by default; a translation tool may
record translated_from: <content-hash> in front matter for exact
staleness. An agent re-translates exactly the reported set - safe to
re-run, no bookkeeping of its own.
Notes
- Language keys ride the alias-override whitelist: operator configuration, never request-selectable beyond declared hosts
- A configuration-only change invalidates the page cache, so a language or chrome edit is never served stale
- There is no
Accept-Languageredirect and no silent fallback serving: a missing translation is omitted from the switcher, not papered over whoamiand the MCP discovery surface report the language set, its members and the source language, so a connecting agent learns the conventions without reading the docs