Pular para o conteúdo
Esta página ainda não foi traduzida. O conteúdo abaixo está em inglês. Ler em inglês

Digging deeper

Localization

JSON translation catalogs with CLDR plurals, locale resolution from the request, a t view function and translated validation messages.

Última atualização:

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.

resources/lang/en.json
{
  "welcome": "Hello, {name}!",
  "posts": {
    "created": "Post created.",
    "count": { "zero": "No posts", "one": "{count} post", "other": "{count} posts" }
  }
}
bootstrap/app.go
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#

Go
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
<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 calls Options.OnMissing when set; translating never panics.
  • Arguments: key/value pairs ("name", "Ana") or one map[string]any; unknown placeholders stay as written. Output is plain text, so html/template escapes it.
  • Plurals: an object keyed by CLDR categories (zero, one, two, few, many, other; other is required), chosen by the numeric count argument. Rules cover most European languages, Arabic, Hebrew and the CJK/South-East Asian languages; others use the English rule. An explicit zero form wins for 0 in every language. i18n.PluralCategory(locale, n) exposes the rules.
  • Vue / React / Svelte (Inertia): inertia.I18n() shares the catalog with your components; see Translations in Inertia.

Locale resolution#

mw, err := i18n.Middleware(bundle, i18n.MiddlewareConfig{}) builds a middleware that picks the locale, in order, from:

  1. ?lang=
  2. the lang cookie
  3. the session value locale (when sessions run first)
  4. Accept-Language (q-values, at most 16 entries)
  5. 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: Cookie when the cookie or session was consulted and Vary: Accept-Language when the header was (a locale from the query needs no Vary: the URL is the cache key). Rename or disable ("-") the sources with Query, Cookie and SessionKey. To remember a user's choice, set the cookie or session value yourself.

Order picks the sources and their order (a source left out is off), and Locales limits the locales a request can select (it must include the default, which stays the fallback). cfg.Validate(bundle) reports unknown or repeated sources and locales without a catalog. An invalid config fails closed: i18n.Middleware returns the error (i18n.NewMiddleware is a deprecated alias that go fix rewrites). vukem.DefaultMiddlewareWith takes the config as MiddlewareOptions.I18n (and APIMiddlewareWith as APIMiddlewareOptions.I18n) and returns the error at boot:

Go
err := vukem.DefaultMiddlewareWith(app, r, vukem.MiddlewareOptions{
	I18n: &i18n.MiddlewareConfig{
		Order:   []i18n.Source{i18n.SourceQuery, i18n.SourceHeader}, // no cookie, no session
		Locales: []string{"en", "pt-BR"},
	},
})

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.

Framework messages#

Errors from auth, social and orm.Unique/Exists carry message keys and keep their English text as the fallback: auth.failed, auth.unauthenticated, auth.unverified, auth.token_ability ({ability}), social.unknown_provider, social.state, social.denied, social.failed, validation.unique and validation.exists ({field}). WithTranslations adds built-in English and Brazilian Portuguese catalogs for them (Bundle.AddBuiltin) to the catalogs your app already has (en; pt-BR and pt); a key in your own catalog always wins. Your own errors can do the same with errs.NewKey(status, key, fallback, params) or errs.Translate(ctx, key, fallback, params).