i18n

package
v1.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

Documentation

Overview

Package i18n provides a lightweight internationalization (i18n) framework for Go applications, supporting translation of strings into multiple languages.

It supports language parsing, validation, translation lookup with fallback, formatted translations, and loading translations from embedded filesystems or raw JSON data.

Consumers are responsible for supplying their own translation files. The package does not embed any locale data itself.

Translation files must be nested JSON objects. Dot-separated lookup keys are derived from the nesting hierarchy:

{
    "user": {
        "created": "User created successfully",
        "not_found": "User not found"
    },
    "error": {
        "internal": "An internal error occurred."
    }
}

The keys above are accessed as "user.created", "user.not_found", and "error.internal" respectively. Flat JSON (e.g. {"user.created": "..."}) is no longer supported.

Context-aware translation

The Bundle.TCTX and Bundle.TfCTX methods (and their global counterparts TCTX and TfCTX) resolve the language automatically from a context.Context. The resolution priority is:

  1. A Language stored directly via WithLanguage.
  2. An *net/http.Request stored via WithRequest; its Accept-Language header is parsed and matched against the bundle's loaded languages.
  3. Default as a last resort.

The Middleware function provides a standard HTTP middleware that parses the Accept-Language header on every request and injects the resolved language into the context, so downstream handlers can simply call TCTX.

Example translation file (locales/en.json):

{
    "user": {
        "created": "User created successfully",
        "not_found": "User not found"
    },
    "error": {
        "internal": "An internal error occurred.",
        "details": "Error in %s at line %d."
    }
}

Example usage:

package main

import (
    "embed"
    "fmt"

    "github.com/valentin-kaiser/go-core/i18n"
)

//go:embed locales/*.json
var localesFS embed.FS

func main() {
    // Load translations from an embedded filesystem
    bundle, err := i18n.New(i18n.WithFS(localesFS, "locales"))
    if err != nil {
        panic(err)
    }

    // Translate a key derived from the JSON nesting hierarchy
    fmt.Println(bundle.T(i18n.German, "user.created"))

    // Translate with format arguments
    fmt.Println(bundle.Tf(i18n.English, "error.details", "file.txt", 42))
}

Index

Constants

This section is empty.

Variables

View Source
var (
	// LanguageKey is the context key used to store and retrieve the resolved
	// Language directly. It takes priority over the request key when both are set.
	LanguageKey any = ContextKey("Language")

	// RequestKey is the context key used to store and retrieve an *http.Request.
	// The request's Accept-Language header is parsed to determine the language
	// when no direct Language is found in the context.
	RequestKey any = ContextKey("Request")
)

Functions

func GlobalMiddleware

func GlobalMiddleware() func(http.Handler) http.Handler

GlobalMiddleware returns an HTTP middleware that uses the global default Bundle. See Middleware for details.

func Init

func Init(opts ...Option) error

Init initialises the global default bundle with the given options, replacing any previously loaded translations. This function is safe for concurrent use.

func Middleware

func Middleware(b *Bundle) func(http.Handler) http.Handler

Middleware returns an HTTP middleware that resolves the caller's preferred language from the request's Accept-Language header, stores the resolved Language in the request context (retrievable via LanguageFromContext), and also stores the *http.Request itself (retrievable via RequestFromContext).

The resolved language is the best match among the languages loaded in the given Bundle. If the Accept-Language header is missing or no match is found, the Default language is used.

Usage with a standard http.ServeMux:

bundle, _ := i18n.New(i18n.WithFS(localesFS, "locales"))
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    msg := bundle.TCTX(r.Context(), "greeting")
    fmt.Fprintln(w, msg)
})
http.ListenAndServe(":8080", i18n.Middleware(bundle)(mux))

func RequestFromContext

func RequestFromContext(ctx context.Context) (*http.Request, bool)

RequestFromContext extracts the *http.Request stored by WithRequest. It returns the request and true if found, or nil and false otherwise.

func SetDefault

func SetDefault(b *Bundle)

SetDefault replaces the global default bundle. It should be called early during application startup before any T/Tf calls are made. This function is safe for concurrent use.

func T

func T(lang Language, key string) string

T translates a key using the global default bundle. This function is safe for concurrent use.

func TCTX

func TCTX(ctx context.Context, key string) string

TCTX translates a key using the language resolved from the given context and the global default bundle. See Bundle.TCTX for the resolution order. This function is safe for concurrent use.

func Tf

func Tf(lang Language, key string, args ...any) string

Tf translates and formats a key using the global default bundle. This function is safe for concurrent use.

func TfCTX

func TfCTX(ctx context.Context, key string, args ...any) string

TfCTX translates and formats a key using the language resolved from the given context and the global default bundle. See Bundle.TCTX for the resolution order. This function is safe for concurrent use.

func Valid

func Valid(lang string) bool

Valid checks if a language code has translations loaded in the global bundle.

func WithLanguage

func WithLanguage(ctx context.Context, lang Language) context.Context

WithLanguage returns a new context that carries the given Language. Downstream code can retrieve it with LanguageFromContext.

func WithRequest

func WithRequest(ctx context.Context, r *http.Request) context.Context

WithRequest returns a new context that carries the given *http.Request. The Bundle.TCTX method will parse the request's Accept-Language header as a fallback when no direct Language is present in the context.

Types

type Bundle

type Bundle struct {
	// contains filtered or unexported fields
}

Bundle holds loaded translations for one or more languages and provides lookup methods for retrieving translated strings.

func GetDefault

func GetDefault() *Bundle

GetDefault returns the global default bundle. This function is safe for concurrent use.

func New

func New(opts ...Option) (*Bundle, error)

New creates a new Bundle and applies the given options to load translations.

func (*Bundle) Has

func (b *Bundle) Has(lang Language, key string) bool

Has reports whether a translation exists for the given key and language.

func (*Bundle) HasLanguage

func (b *Bundle) HasLanguage(lang Language) bool

HasLanguage reports whether the bundle contains translations for the given language.

func (*Bundle) Languages

func (b *Bundle) Languages() []Language

Languages returns all languages that have at least one translation loaded.

func (*Bundle) Load

func (b *Bundle) Load(fsys fs.FS, dir string) error

Load loads translations from an fs.FS at runtime. It scans the directory for all *.json files and derives the language code from each filename.

func (*Bundle) Register

func (b *Bundle) Register(lang Language, translations map[string]string)

Register adds translations for a language from a Go map at runtime. It merges the new translations with any existing ones for that language.

func (*Bundle) RegisterJSON

func (b *Bundle) RegisterJSON(lang Language, data []byte) error

RegisterJSON adds translations for a language from raw JSON bytes at runtime. The JSON must be a nested object; keys are flattened to dot-separated strings internally (e.g. {"page": {"title": "Home"}} becomes "page.title").

func (*Bundle) T

func (b *Bundle) T(lang Language, key string) string

T translates a key for the given language. If the key is not found in the requested language, it falls back to Default. If still not found, the key itself is returned unchanged.

func (*Bundle) TCTX

func (b *Bundle) TCTX(ctx context.Context, key string) string

TCTX translates a key using the language resolved from the given context.

The resolution order is:

  1. A Language stored directly in the context via WithLanguage.
  2. An *http.Request stored in the context via WithRequest; the Accept-Language header is parsed and matched against the bundle's loaded languages.
  3. Default if neither of the above yields a result.

func (*Bundle) Tf

func (b *Bundle) Tf(lang Language, key string, args ...any) string

Tf translates a key and formats the result with the given arguments using fmt.Sprintf.

func (*Bundle) TfCTX

func (b *Bundle) TfCTX(ctx context.Context, key string, args ...any) string

TfCTX translates a key with format arguments using the language resolved from the given context. See Bundle.TCTX for the resolution order.

type ContextKey

type ContextKey string

ContextKey is the type used for context keys in this package. Using a distinct type helps avoid collisions with other context keys in the application.

type Language

type Language string

Language represents a BCP 47 language code.

const (
	// English represents the English language.
	English Language = "en"
	// German represents the German language.
	German Language = "de"
	// Default is the fallback language used when a requested translation is missing.
	Default Language = English
)

func LanguageFromContext

func LanguageFromContext(ctx context.Context) (Language, bool)

LanguageFromContext extracts the Language stored by WithLanguage. It returns the language and true if found, or "" and false otherwise.

func Parse

func Parse(lang string) Language

Parse normalizes a language string. If the language has no translations loaded in the global bundle, Default is returned.

func Supported

func Supported() []Language

Supported returns all languages that have translations loaded in the global default bundle. If no translations have been loaded yet the list may be empty.

type Option

type Option func(*Bundle) error

Option configures a Bundle during creation.

func WithEmbedFS

func WithEmbedFS(fsys embed.FS, dir string) Option

WithEmbedFS is an alias for WithFS that explicitly takes an embed.FS. It reads translation files from the given directory inside the embedded filesystem.

func WithFS

func WithFS(fsys fs.FS, dir string) Option

WithFS loads translation files from an embed.FS (or any fs.FS). It scans the given directory for JSON files named by language code (e.g. "en.json", "fr.json", "ja.json") and loads each one automatically. Any language is supported — the language code is derived from the filename. Files that cannot be read or parsed are silently skipped.

func WithJSON

func WithJSON(lang Language, data []byte) Option

WithJSON registers translations for a language from raw JSON bytes. The JSON must be a nested object; dot-separated lookup keys are derived from the nesting hierarchy (e.g. {"page": {"title": "Home"}} is accessed as "page.title"). If the JSON data is malformed, an error is returned when the bundle is created, as it indicates a programming error that should be caught during development.

func WithMap

func WithMap(lang Language, translations map[string]string) Option

WithMap registers translations for a language from a Go map.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL