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.

Info

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)
Tip

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:

OutputDefault languageOther languages
Index pagedist/index.htmldist/{lang}/index.html
RSS feeddist/feed.xmldist/{lang}/feed.xml
LLM discoverydist/llms.txtdist/{lang}/llms.txt
Search indexdist/search-index.jsondist/{lang}/search-index.json
Sitemapdist/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

VariableDescription
{{ 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 %}

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