i18n

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 22, 2026 License: MIT Imports: 7 Imported by: 0

README

go/kit/i18n

CI Go Reference Module version

Framework-agnostic Go translation engine. The TypeScript twin lives at libs/ts-i18n with an identical API surface so the same key constants, fallback semantics, and {{name}} interpolation travel round-trip between services and frontends.

Install

go get github.com/fromforgesoftware/forge/go/kit/i18n

Usage

import (
    "embed"

    "github.com/fromforgesoftware/forge/go/kit/i18n"
)

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

func main() {
    i18nInstance, err := i18n.LoadFromFS(localesFS, "locales", "en", "en")
    if err != nil {
        panic(err)
    }

    i18nInstance.Translate("es_MX", "common::mobile")              // "Celular"
    i18nInstance.Translate("es_MX", "common::save")                // "Guardar" (base es)
    i18nInstance.Translate(
        "en",
        "welcome::message",
        map[string]string{"name": "Ada"},
    )                                                              // "Welcome, Ada!"

    // Use generated constants once you've run `cmd/i18n-gen` (see below):
    i18nInstance.Translate("en", i18n.SharedEnumRoleOrgadmin)      // "Customer Administrator"

    // Enum-shaped UI helper:
    roles := i18nInstance.GetKeyValues("en", "shared::enum::role")
    // map[string]string{"org.admin":"Customer Administrator", ...}
}

Use LoadFromDir instead of LoadFromFS for filesystem reads during local development.

Fallback chain

exact locale -> base language (en from en_US) -> fallback language -> the key itself.

Key shape

:: separates namespaces so dots / underscores / hyphens are free to live inside key names (shared::enum::role::org.admin, planner::roster-section-heading). The generator strips in-key punctuation only from the constant identifier — the original key value is preserved.

Constants generator

The forge CLI generates compile-time-safe key constants:

go install github.com/fromforgesoftware/forge/go/cmd/forge@latest

# In your service root (the directory with locales/):
forge i18n generate-constants --lang go

Writes i18n_constants.go (pkg i18n) from locales/en.json. Override with --out, --pkg, --primary if you need different paths.

The output gives you leaf-key constants, parent-key constants, and AllTranslationKeys / AllTranslationParentKeys slices. Wire it into your service's go generate so the constants stay in sync with the canonical locale file:

//go:generate forge i18n generate-constants --lang go

forge i18n validate will also lint the locales directory for typos, shape mismatches, and (with --strict) missing translations.

API

Method Returns Notes
Translate(locale, key, params...) string exact -> base -> fallback -> key
HasTranslation(locale, key) bool walks the same fallback chain
GetAvailableLanguages() []string sorted list of loaded locale codes
GetKeys(locale, parentKey) []string leaf keys with full namespace, merged across fallbacks
GetKeyValues(locale, parentKey, params...) map[string]string direct string children only — enum-shaped UI helper
GetValues(locale, parentKey, params...) []KeyValue sorted-by-id slice variant of GetKeyValues

Test + bench

cd go/kit && go test ./i18n/...                          # tests
cd go/kit && go test -race ./i18n/...                    # race-clean
cd go/kit && go test -bench=. -benchmem -run=^$ ./i18n/  # benchmarks

Documentation

Overview

Package i18n provides a framework-agnostic translation engine.

Translations are stored as nested JSON objects keyed by namespace. The "::" delimiter separates namespaces so that key names may contain dots, underscores, or hyphens (e.g. "shared::enum::role::org.admin").

Lookups follow a fallback chain: exact locale (e.g. "en_US") -> base language ("en") -> configured fallback language -> the key itself.

The package does not embed any locale files of its own; consumers bring their own translations via NewI18n, LoadFromFS, or LoadFromDir. A separate generator (cmd/i18n-gen) produces compile-time-safe key constants from a primary locale file.

Index

Constants

View Source
const NamespaceDelimiter = "::"

NamespaceDelimiter separates namespaces in a translation key. Dots, underscores, and hyphens are preserved as part of key names.

Variables

This section is empty.

Functions

This section is empty.

Types

type I18n

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

I18n is a stateless translation engine. Safe for concurrent use; the internal state is built once in NewI18n and never mutated after that.

Internally each locale is held in two shapes:

  • the original nested tree, used by GetKeys / GetKeyValues which need subtree access;
  • a flattened map[fullKey]value built at construction time, used by the hot Translate / HasTranslation paths so each lookup is a single map probe with no string splitting or tree-walking.

This costs roughly one extra string pointer per (locale, leaf-key) pair — kilobytes for a typical app — and turns Translate into an allocation-free O(1) call once the value is found.

func LoadFromDir

func LoadFromDir(dir, defaultLang, fallbackLang string) (*I18n, error)

LoadFromDir is the os.DirFS equivalent of LoadFromFS — useful for loading translations at runtime from disk during development.

func LoadFromFS

func LoadFromFS(fsys fs.FS, dir, defaultLang, fallbackLang string) (*I18n, error)

LoadFromFS reads every *.json file directly under dir in fsys and returns an I18n instance. File names (minus .json) become locale codes. Consumers typically pass an embed.FS so locale files ship inside the binary:

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

i18nInstance, err := i18n.LoadFromFS(localesFS, "locales", "en", "en")

func NewI18n

func NewI18n(defaultLanguage, fallbackLanguage string, translations map[string]Translation) *I18n

NewI18n creates a new I18n instance from an in-memory map of translations keyed by locale code. The nested trees are walked once to build per-locale flat lookup tables; after this call the result is safe for concurrent reads.

func (*I18n) GetAvailableLanguages

func (i *I18n) GetAvailableLanguages() []string

GetAvailableLanguages returns the sorted list of loaded locale codes.

func (*I18n) GetDefaultLanguage

func (i *I18n) GetDefaultLanguage() string

GetDefaultLanguage returns the default language code.

func (*I18n) GetFallbackLanguage

func (i *I18n) GetFallbackLanguage() string

GetFallbackLanguage returns the fallback language code.

func (*I18n) GetKeyValues

func (i *I18n) GetKeyValues(locale, parentKey string, params ...map[string]string) map[string]string

GetKeyValues returns direct string children of parentKey as a {localKey: translation} map, suitable for populating dropdowns and enum-like UIs. Nested objects are ignored. Translation values are resolved using the same fallback chain as Translate, with optional placeholder interpolation.

func (*I18n) GetKeys

func (i *I18n) GetKeys(locale, parentKey string) []string

GetKeys returns every leaf key under parentKey, with the full namespace prefix preserved. Results are merged across exact locale, base language, and the fallback language so the caller sees the complete set of available keys regardless of which file defines them. Pass an empty parentKey to walk the full tree.

func (*I18n) GetValues

func (i *I18n) GetValues(locale, parentKey string, params ...map[string]string) []KeyValue

GetValues is the slice-of-pairs variant of GetKeyValues, sorted by id. Convenient for API responses where map ordering is unwanted.

func (*I18n) HasTranslation

func (i *I18n) HasTranslation(locale, key string) bool

HasTranslation reports whether a translation exists for key in the requested locale (including base-language fallback).

func (*I18n) Translate

func (i *I18n) Translate(locale, key string, params ...map[string]string) string

Translate resolves key for the requested locale, falling back to the base language (e.g. "en" for "en_US") and then the configured fallback language. If no translation is found the key is returned unchanged. When params is supplied, {{name}} placeholders are interpolated; passing no params (or a nil map) skips interpolation entirely.

type KeyValue

type KeyValue struct {
	ID    string `json:"id"`
	Value string `json:"value"`
}

KeyValue is an ordered (id, value) pair returned by GetValues.

type Translation

type Translation map[string]any

Translation is a nested map of translation values. Leaf values are strings; intermediate nodes are nested Translation maps.

Jump to

Keyboard shortcuts

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