A site with two languages used to mean rolling your own routing. Now it is a
languages: declaration and an ordinary mirrored content folder.
language.en.label: English
language.en.default: true
language.hr.label: Hrvatski
That is the whole configuration. code is both the URL prefix and the
content-tree folder name: the default language’s content stays exactly where it
already is (content/index.md publishes at /), and every other language gets
a subtree named after its code (content/hr/index.md publishes at /hr/,
content/hr/about/index.md at /hr/about/). There is no translation step or
build-time machinery: each language’s page is an ordinary, independently
authored Markdown file, mirroring the folder-to-URL convention TileDown already
uses for every page.
Two switchers, one header
The language switcher sits right next to the appearance toggle in both built-in
layouts, the same corner of the header, the same visual weight. It links only to
translations that actually exist: a page with no translation yet shows no link
for that language, rather than a 404. <html lang> follows the current page’s
language automatically.
The top-nav and sidebar section lists are language-scoped too. A language’s own root page is never a section (you reach it through the switcher, not the nav), and one language’s pages never leak into another’s section list.
Try it in this corner
If this site ever ships a second language, its switcher will render right here, next to the dark-mode button, using exactly this mechanism.
See the config reference on Codeberg for the full key list, including what happens with zero, one, or many declared languages.