Package i18n (stdlib only) loads one JSON catalog per locale from resources/lang/<locale>.json (en.json, pt-BR.json; pt_BR.json is accepted). Keys are nested objects or dotted names; values are strings with {name} placeholders, or plural objects.
{
"welcome": "Hello, {name}!",
"posts": {
"created": "Post created.",
"count": { "zero": "No posts", "one": "{count} post", "other": "{count} posts" }
}
}app, err := vukem.New(
vukem.WithTranslations(os.DirFS("resources/lang")), // app.I18n; APP_LOCALE is the default (en)
vukem.WithViews(resources.Views()),
vukem.WithRoutes(routes), // routes call vukem.DefaultMiddleware
)WithTranslations loads the bundle at boot (bad JSON or file names fail New), gives views the t and locale functions and makes vukem.DefaultMiddleware install the locale middleware after sessions.
Translating#
c.Flash("status", i18n.T(c.Context(), "posts.created")) // the request's locale
b.T("pt-BR", "posts.count", "count", 3) // a Bundle directly<html lang="{{locale}}">
<p>{{t "welcome" "name" .User.Name}} · {{t "posts.count" "count" (len .Posts)}}</p>- Fallback chain: the locale, its shorter forms, then the default (
pt-BR→pt→en). A missing key returns the key and callsOptions.OnMissingwhen set; translating never panics. - Arguments: key/value pairs (
"name", "Ana") or onemap[string]any; unknown placeholders stay as written. Output is plain text, sohtml/templateescapes it. - Plurals: an object keyed by CLDR categories (
zero,one,two,few,many,other;otheris required), chosen by the numericcountargument. Rules cover most European languages, Arabic, Hebrew and the CJK/South-East Asian languages; others use the English rule. An explicitzeroform wins for 0 in every language.i18n.PluralCategory(locale, n)exposes the rules.
Locale resolution#
i18n.Middleware(bundle, i18n.MiddlewareConfig{}) picks the locale, in order, from:
?lang=- the
langcookie - the session value
locale(when sessions run first) Accept-Language(q-values, at most 16 entries)- the default locale
Candidates must be valid tags matching an available catalog (exact, shorter form, then same language); invalid input is ignored. The middleware stores the bundle and locale in the context (i18n.Locale, i18n.WithLocale, i18n.BundleFrom), sets Content-Language, and adds Vary: Accept-Language when the header was consulted. Rename or disable ("-") the sources with Query, Cookie and SessionKey. To remember a user's choice, set the cookie or session value yourself.
Validation messages#
The middleware also translates web.Parse/Bind validation messages from validation.* keys in your catalogs, so validation errors follow the request's locale.