Internationalization

Translate your templates with gettext (.po) files.

The i18n package loads gettext translations and resolves the best locale per request. It implements the middleware.LocaleProvider seam, so wiring it up makes ProcessLocale — and the renderer’s t/tDefault template functions — work end to end.

Layout and loading

One top-level directory per locale, each containing a default.po:

locales/
├── en/default.po
├── es/default.po
└── pt_BR/default.po

Embed and load it:

//go:embed locales
var localesFS embed.FS

sub, _ := fs.Sub(localesFS, "locales")
locales, err := i18n.Load(sub)               // options: WithDefaultLocale, WithReloading
if err != nil { /* handle */ }

r.Use(middleware.ProcessLocale(locales))

ProcessLocale resolves the locale from the ?lang= query parameter, then the Accept-Language header (full q-list matching via x/text/language, with region fallback — pt-br finds pt_BR). It stores the translator on the context and populates locale, textLanguage, and textDirection (RTL-aware) on the template map.

Using translations in templates

<label>{{ tDefault .locale "Leave a message" "form.label" }}</label>
  • tDefault falls back to the given default string when the key is missing — ideal while translations are incomplete.
  • t errors on unknown keys — ideal once your catalogs are complete.

With a locales/es/default.po containing:

msgid "form.label"
msgstr "Deja un mensaje"

…requesting /?lang=es renders the Spanish label. The example app demonstrates the full flow.