In v2, i18n is an opt-in module, not loaded by default. When you need it, attach it to the App with WithI18n and pass the template function i.Funcs() to the template engine. Locale matching uses the stdlib golang.org/x/text (BCP-47).
The message store is the simplest form locale → key → text (no singular/plural); this module does not do date/number/currency/timezone formatting.
| Capability | Entry point |
|---|---|
| locale detection | app.Use(AcceptLanguage("en", "zh", ...)) middleware |
| read locale in a Handler | c.Locale() |
| translate in a Handler | c.Translate(locale, key, args...) |
| message store | I18n (NewI18n / Add / Set / LoadJSON) |
| translate in a template | T (injected by i.Funcs()) |
app := httpsrv.New()
app.Use(httpsrv.AcceptLanguage("en", "zh", "ja")) // first is the default/fallback- Parses the
Accept-Languageheader and matches a supported locale by BCP-47 (e.g.en-US→en,zh-Hans→zh). - Falls back to the first argument (the default) when nothing matches.
- The result is stored in the request context; read it with
c.Locale().
i := httpsrv.NewI18n("en") // default language
// batch
i.Add("zh", map[string]string{"welcome": "欢迎", "hi": "你好 %s"})
i.Add("en", map[string]string{"welcome": "Welcome", "hi": "Hi %s"})
// single
i.Set("en", "bye", "Bye")
// load from JSON (flat {"key": "text"})
i.LoadJSON("en", []byte(`{"welcome": "Welcome", "hi": "Hi %s"}`))Translate:
i.Translate("en", "welcome") // "Welcome"
i.Translate("zh", "missing") // fall back to default → then the key: "missing"
i.Translate("zh", "hi", "Alice") // "你好 Alice" (args via fmt.Sprintf)- Lookup chain: requested locale → default locale → the key itself.
- Locales are case-insensitive.
app := httpsrv.New(httpsrv.WithI18n(i))
app.Get("/", func(c httpsrv.Ctx) error {
return c.SendString(c.Translate(c.Locale(), "welcome"))
})- Without
WithI18n:c.Translate(...)returns the key unchanged (i18n module not loaded). - Without the
AcceptLanguagemiddleware:c.Locale()falls back to the i18n default, then the empty string.
Pass i.Funcs() as extra template functions to the template engine:
r, _ := httpsrv.TemplatesFS(views, i.Funcs())
app := httpsrv.New(httpsrv.WithViews(r), httpsrv.WithI18n(i))Template: <h1>{{T .Lang "welcome"}}</h1>
package main
import (
"embed"
"io/fs"
"github.com/hooto/httpsrv/v2"
)
//go:embed views
var views embed.FS
func main() {
i := httpsrv.NewI18n("en")
i.Add("en", map[string]string{"welcome": "Welcome"})
i.Add("zh", map[string]string{"welcome": "欢迎"})
sub, _ := fs.Sub(views, "views")
r, err := httpsrv.TemplatesFS(sub, i.Funcs())
if err != nil {
panic(err)
}
app := httpsrv.New(httpsrv.WithViews(r), httpsrv.WithI18n(i))
app.Use(httpsrv.AcceptLanguage("en", "zh")) // detect locale
app.Get("/", func(c httpsrv.Ctx) error {
return c.Render("home.html", map[string]any{"Lang": c.Locale()})
})
_ = app.Run(":8080")
}home.html: <h1>{{T .Lang "welcome"}}</h1>