Guide
Internationalisation (i18n)
i18n is opt-in. Existing sites, including sites using the legacy compact
languages option, keep their current behaviour until i18n.enabled is set.
Configuration
languages:
- code: pl
locale: pl-PL
name: Polski
timezone: Europe/Warsaw
- code: en
locale: en-GB
name: English
timezone: Europe/London
default_language: pl
i18n:
enabled: true
prefix_default_language: false
translations_dir: i18n
dictionary_fallback: true
missing_translation: warn # error, warn, fallback, empty
invalid_language: fail # fail, warn
duplicate_translation: fail # fail, warn
fallback_languages:
pl: [en]
en: []
The compact form remains valid:
languages: [pl, en]
default_language: pl
language_timezones:
pl: Europe/Warsaw
With prefix_default_language: false, Polish /o-nas/ and English
/en/about/ are generated. Setting it to true produces /pl/o-nas/ and
/en/about/.
Content translations
Connect translated files with a stable key; translated slugs may differ:
title: About
slug: about
lang: en
translation_key: about
status: publish
When lang is absent it resolves to default_language. If
translation_key is absent, SSG derives it deterministically from the source
filename. Duplicate (translation_key, lang) pairs, unknown languages,
fallback cycles, invalid timezones and output collisions are validated before
rendering.
Templates receive .Page.Lang, .Page.Locale, .Page.TranslationKey,
.Page.Translations, .Site.Language, .Site.Languages,
.Site.DefaultLanguage, .Site.LanguagePages and .Site.LanguagePosts.
{{range .Page.Translations}}
<a href="{{.URL}}" hreflang="{{.Lang}}" {{if .IsCurrent}}aria-current="page">{{.Lang}}</a>
Helpers: hasTranslation, translationURL, languageURL, localizeDate and
t. Existing helpers such as formatDatePL remain available.
Translation dictionaries
Place pl.yaml, en.yaml or JSON equivalents in translations_dir:
navigation:
home: Strona główna
post:
reading_time: " minut"
Use {{t "navigation.home"}} or
{{t "post.reading_time" (dict "count" .ReadingTime)}}. Values are returned
as ordinary strings and remain subject to html/template escaping. Only named
placeholders are substituted; catalog values are never executed as templates.
Generated output
Pages, aliases, home pages, pagination, JSON records, Atom feeds and search
indexes follow the configured prefix rule. Sitemap page entries contain XHTML
language alternates and x-default; opt-in SEO output includes Open Graph
locale metadata and JSON-LD inLanguage.
Internal Markdown links
rewrite_md_links is language-aware: a link like installation.md resolves to
the active language's translation of that document. An explicit
language-suffixed link (installation.en.md) keeps the author's choice. When
the active-language translation does not exist, the content_fallback chain
(fallback_languages → default language) applies only when
i18n.content_fallback: true; otherwise the link is left untouched and a
warning names the link and language (once per pair).
Deferred features
Planned follow-ups, not yet implemented: language-scoped taxonomy pages
(category/tag/author/series listings are still cross-language), a language
selector and t labels inside the built-in themes (the output <html lang> is
already corrected at render time), localized month names in localizeDate,
and plural rules.
Migration
Add the i18n section only when ready. Start by assigning lang and a shared
translation_key to variants, then add dictionaries and a language selector.
Run a clean build after enabling i18n so obsolete unprefixed artifacts do not
remain in the output directory.