Multi-Language
Add translations to your site with filename-based i18n — per-language URLs, RSS, sitemaps, and discovery files.
Overview
seite uses a filename-based translation system. It's fully backward compatible — single-language sites work identically with no configuration.
Single-language sites need zero i18n config. Everything works identically whether or not you add [languages.*] sections. i18n is purely additive.
How It Works
Default language content uses plain filenames:
content/posts/hello-world.md → /posts/hello-world
content/pages/about.md → /about
Translations add a language suffix before the extension:
content/posts/hello-world.es.md → /es/posts/hello-world
content/pages/about.es.md → /es/about
Items with the same slug across languages are automatically linked as translations.
Configuration
Enable multi-language support by adding language sections to seite.toml:
[site]
language = "en"
title = "My Site"
[languages.es]
title = "Mi Sitio"
description = "Un sitio web estático"
[languages.fr]
title = "Mon Site"
description = "Un site web statique"
Each language can override title and description. The default language is set in [site].language.
Creating Translations
Use the --lang flag with seite new:
seite new post "Hello World" # English (default) seite new post "Hola Mundo" --lang es # Spanish translation
Or create files manually — the language suffix must match a configured language code:
about.md → English (default)
about.es.md → Spanish
about.fr.md → French
about.xx.md → Ignored (xx not configured)
Files with a .xx.md suffix are safely ignored if xx isn't a configured language. You won't accidentally create broken translations from random filename patterns.
Per-Language Output
When multi-language is enabled, seite generates per-language versions of:
| Output | Default language | Other languages |
|---|---|---|
| Index page | dist/index.html | dist/{lang}/index.html |
| RSS feed | dist/feed.xml | dist/{lang}/feed.xml |
| LLM discovery | dist/llms.txt | dist/{lang}/llms.txt |
| Search index | dist/search-index.json | dist/{lang}/search-index.json |
| Sitemap | dist/sitemap.xml (all languages, with alternates) |
URL Structure
Non-default languages get a /{lang}/ prefix:
/posts/hello-world # English
/es/posts/hello-world # Spanish
/fr/posts/hello-world # French
hreflang Tags
All bundled themes automatically emit <link rel="alternate" hreflang="..."> tags when translations exist, helping search engines serve the right language.
Language Switcher
Bundled themes include a language switcher UI when translations is non-empty. It shows links to all available translations of the current page.
Template Variables
| Variable | Description |
|---|---|
{{ lang }} | Current page language code |
{{ translations }} | Array of {lang, url} for available translations |
{{ site.language }} | Configured default language (from seite.toml) |
{{ default_language }} | Configured default language code |
{{ lang_prefix }} | URL prefix for current language (empty for default, "/es" for others) |
{{ t }} | UI translation strings (see Templates & Themes docs) |
Use in templates:
{% if translations %} <nav class="lang-switcher"> {% for t in translations %} {% if t.lang == lang %} <strong>{{ t.lang }}</strong> {% else %} <a href="{{ t.url }}">{{ t.lang }}</a> {% endif %} {% endfor %} </nav> {% endif %}
Navigation and Footer Links
Data file links (data.nav, data.footer) are automatically prefixed with the current language in all bundled themes. Mark external links with external: true so they are not prefixed:
# data/nav.yaml - title: Blog url: /posts - title: GitHub url: https://github.com/user/repo external: true
Internal links render as {{ lang_prefix }}{{ item.url }}. External links render with target="_blank".
UI String Translations
Bundled themes use {{ t.key }} for all interface text (search placeholder, pagination, 404 text, etc.). Override defaults per language by creating data/i18n/{lang}.yaml:
# data/i18n/es.yaml search_placeholder: "Buscar…" skip_to_content: "Ir al contenido principal" no_results: "Sin resultados" newer: "Más recientes" older: "Más antiguos"
See Templates & Themes for the full list of available keys.
Next Steps
- Collections — how translations work across posts, docs, and pages
- Templates & Themes — language switcher template code and theme blocks
- Deployment — per-language output files and how they deploy