Documentation
¶
Overview ¶
Package i18n owns every locale decision in the program: which display languages exist, which one is active, and how a localized string is looked up. No other layer interprets a language value — the command layer resolves the language with Detect and hands the result to Activate, and rendering code calls T.
English is both the default and the reference language: a T key IS the English text, so a key with no registered translation renders as-is. That is a deliberate drift property — when English copy changes, its old translation goes dead and the UI falls back to English instead of showing stale text (and the dead entry fails TestLocaleEntriesAreLive).
Translations live in flat per-locale JSON files under locales/<code>/, embedded at build time and merged into one table per locale: each file maps keys (the English call-site literal, verbatim) to that locale's rendering templates. The files split by SURFACE so a human reviews one surface's translations in one place — tui.json (TUI chrome), setup.json (interactive setup + shared key guidance), doctor.json (health-check diagnostics), preplace.json (pre-place order warnings) — and the guards enforce the placement (a key must be live in its file's source scope, and no key may appear in two files). locales/en/ exists only for display overrides — the rare key whose on-screen English differs from the key itself because one English word translates differently by context (e.g. key "market order" displays "market", but Korean needs 시장가 주문 distinct from a venue's 마켓). Everything else the catalog needs is enforced by tests in this package: every i18n.T literal in the tree has a ko entry, every entry maps to a live key, and a translation's verbs match its key's (guards_test.go).
The pre-place warnings (internal/ops) are keyed on runtime values (PlaceWarning.Format / .Code), not literals, so the call sites cannot be extracted — they render through the PreplaceWarnLabel/PreplaceWarnMessage wrappers, and their keys are kept live by matching the quoted literals under internal/ops instead. Owning the ops surface's translation here keeps ops itself free of any localization dependency — it emits the English source plus the raw template/args, and a consumer renders the localized form.
Two language values matter, kept deliberately distinct:
- The DETECTED language — what the run resolved from the global --lang flag (else the host locale, else DefaultLang). Resolve computes it once at startup, validating --lang and erroring on an unsupported code, and records it; Detected reports it. Language selection is a program-wide concern, so --lang is a global flag and Resolve runs regardless of command.
- The ACTIVE language — what T renders. It stays English until a surface that localizes calls Activate(Detected()); any surface that does not activate stays English regardless of the host locale. Agent- and machine-facing output does not localize.
Reading the host locale is confined to Detect/Resolve; Activate merely sets a resolved code and never touches the environment.
Trading vocabulary (side/type/tif values, channel names), symbols, numbers, and API error codes/descriptions are never localized either — those pass through T untouched or bypass it entirely.
Formatting rule: T interpolates with fmt.Sprintf, so a template may use any fmt verb — %d and %f render exactly as fmt renders them, never locale digit-grouped (money stays a pre-formatted decimal string passed as %s regardless). A translation must use exactly its key's verbs; it may reorder them for the target language's word order via explicit indices (%[2]s). TestLocaleEntriesUseSaneVerbs enforces this for every entry.
Index ¶
- Constants
- func Activate(lang string) error
- func Active() string
- func Detect(explicit string, getenv func(string) string) (string, error)
- func Detected() string
- func Languages() []string
- func PreplaceWarnLabel(code string) string
- func PreplaceWarnMessage(format string, args ...any) string
- func Resolve(explicit string, getenv func(string) string) (string, error)
- func T(key string, args ...any) string
Constants ¶
const DefaultLang = "en"
DefaultLang is the language of the source strings, active until Activate picks another. All T keys are written in it.
Variables ¶
This section is empty.
Functions ¶
func Activate ¶
Activate sets the active display language. Empty input activates DefaultLang. An unsupported code is an error naming the valid ones; the active language is left unchanged. Callers resolve the language with Detect first (which is where the host locale is read); Activate itself never consults the environment.
func Detect ¶
Detect resolves the display language for a run without changing the active one — a caller activates the result (only the tui does). An explicit choice (the --lang value) wins and, if it names an unsupported language, is an error so a typo is reported rather than silently ignored. With no explicit choice the host locale is consulted — the POSIX locale env ($LC_ALL/$LC_MESSAGES/ $LANG) on Unix, else the user's preferred UI languages on Windows — and an absent or unrecognized locale yields DefaultLang.
func Detected ¶
func Detected() string
Detected reports the run's detected display language — DefaultLang until Resolve runs.
func Languages ¶
func Languages() []string
Languages lists the supported language codes, DefaultLang first — the single source for the --lang enum and any future language picker.
func PreplaceWarnLabel ¶
PreplaceWarnLabel localizes a pre-place warning's code label (ops.PlaceWarning.Code) for display. It is T with a runtime key: the code value is the lookup key, so English (and any untranslated label) renders the stable code itself. This wrapper — not a T("literal") call site — is the sanctioned dynamic entry point for warning labels; the extraction guard keys their locale entries to the quoted literals under internal/ops instead.
func PreplaceWarnMessage ¶
PreplaceWarnMessage localizes and renders a pre-place warning message. format is the English template (ops.PlaceWarning.Format), the lookup key at runtime; args interpolate into the translation (or into the template itself when none is registered — a partial table falls back to English by design, so a newly added warning ships English until its locales/<code>/preplace.json entry lands; unlike a T literal, no guard forces that entry to exist). Like PreplaceWarnLabel, this is the sanctioned dynamic T entry point for warning messages.
func Resolve ¶
Resolve determines the run's display language once, at startup, from the global --lang value (else the host locale, else DefaultLang), records it as the detected language, and returns it. A non-empty value that names an unsupported language is an error — this is where the global --lang flag is parsed and rejected. Resolve does NOT activate the language; a surface that localizes activates it with Activate(Detected()).
Types ¶
This section is empty.