CARLOS rastrillo docs

🤖 locales

amadan.net/rastrillo/rastrillo/locales

The CLDR data that writes and reads money in each language. The core is this package. Each language's data is a pack of its own, locales/<lang>, which your app links with a blank import. Go links only what is imported, so a language you do not offer costs nothing. Localization is the guide.

Packs

One pack covers one base language and every modern regional variant of it: locales/de has de, de-AT, de-CH, de-LI and the rest. A generated app gets the packs it needs from manifest/app.toml. rastrillo generate writes them to gen/locales/packs.go:

import (
	_ "amadan.net/rastrillo/rastrillo/locales/en"
)

A hand-written app imports the packs for the languages it offers. If one is missing, the app refuses to start and names the import to add.

func PacksFor(offered []string) ([]string, error)

PacksFor is the packs a set of language codes needs, without duplicates: en, zh-Hans, zh-Hant, pt-PT and yue need en, pt, yue and zh. A code is what your app offers, routes and keeps catalogs under; a pack is only where its formatting data lives.

The en pack adds about 55 KB to a binary, and most packs far less. locales/modern imports all 101, about 500 KB, for a tool that has to format in any language.

CheckConfig

func CheckConfig(defaultLocale string, offered []string) (Config, error)
func CanonicalIdentity(code string) (string, error)

type Config struct {
	Default       string            // the declared effective default: an offered identity
	DefaultFormat string            // the linked locale that formats it
	Offered       []string          // as declared, canonical
	Formats       map[string]string // offered identity -> formatting locale
	Packs         []string          // derived, deduplicated
	LinkedPacks   []string          // what this binary actually links
}

CheckConfig checks the languages an app offers, in the binary that will serve them. Serve and Handler run it before they open the database, and the app refuses to start on any problem. It checks that:

A wrong configuration is never repaired, because a code that only works once corrected would be stored and routed under its wrong spelling. The error says what to write:

locale configuration: "en_US" is not in canonical form; write "en-US"
locale configuration: "de" needs the de pack: import _ "amadan.net/rastrillo/rastrillo/locales/de"

Config keeps your codes and their formatting locales apart. zh-TW is the code your app routes and keeps catalogs under; Formats says zh-Hant writes its money.

Resolve

func Resolve(tag string) Resolution

type Resolution struct {
	ID          string
	Wanted      string
	MissingPack string
	Malformed   bool
	UsedDefault bool
}

Resolve finds the linked data for any language tag, such as one from Accept-Language or a stored preference. Unlike CheckConfig, it is forgiving. It checks the tag's grammar, applies CLDR's aliases and likely scripts, and matches every locale rastrillo ships, linked or not. That match is Wanted. zh-TW is not a CLDR locale, so it matches zh-Hant; cutting it to zh would pick Simplified Chinese data.

If Wanted's pack is not linked, Resolve walks up CLDR's parents to a linked one, and names the pack it lacked in MissingPack: hi-Latn falls back to en-IN, ht to fr-HT, nb and nn to no. With no linked parent it uses the app's default language and sets UsedDefault. ID is the locale that writes the amount. Only the conventions fall back; the currency never changes.

Malformed is a tag that is not a well-formed language tag, such as de--CH.

The configuration probe

Run your app with RASTRILLO_CHECK_CONFIG=1 and it checks its languages, prints one JSON line and exits. It opens no database, runs no migration and serves nothing, so it is safe to run beside a live copy:

RASTRILLO_CHECK_CONFIG=1 go run ./cmd/shop
{"rastrillo_check_config":1,"ok":true,"default":"en","default_format":"en","offered":[],"formats":{"en":"en"},"packs_required":["en"],"packs_linked":["en"],"problems":[]}
RASTRILLO_CHECK_CONFIG=1: checked the configuration and exited without serving; unset it to serve

It exits 0 when the configuration is good and 1 when problems lists something. Any other value of the variable exits 2:

RASTRILLO_CHECK_CONFIG="yes": set it to 1 to check the configuration, or unset it to serve

It always exits, even in production. A server that inherits the variable stops at once, with that line in its log, rather than serving.

rastrillo.Run and rastrillo.Resolve run the probe first. A main that calls neither calls rastrillo.ProbeConfig first. rastrillo doctor builds your app and runs the probe for you; see The CLI.

func Probe(env, defaultLocale string, offered []string, stdout, stderr io.Writer) (run bool, exit int)

type ProbeReport struct {
	Version       int               `json:"rastrillo_check_config"`
	OK            bool              `json:"ok"`
	Default       string            `json:"default"`
	DefaultFormat string            `json:"default_format"`
	Offered       []string          `json:"offered"`
	Formats       map[string]string `json:"formats"`
	PacksRequired []string          `json:"packs_required"`
	PacksLinked   []string          `json:"packs_linked"`
	Problems      []string          `json:"problems"`
}

Probe is the logic ProbeConfig runs. ProbeReport is the line it prints; every field is always there, so a script can read it.

The rest

Most apps never call these. The framework uses them, and so can a test or a tool.

func Install(cfg Config)
func SetDefault(id string)
func SetDefaultForTest(id string) (restore func())
func FormatFor(identity string) string

Install makes an accepted Config the process's own, so request-time formatting uses the mapping boot checked. rastrillo.Handler calls it. SetDefault sets only the default formatting locale. SetDefaultForTest replaces the default for one test and returns a function that puts it back. FormatFor is the formatting locale for a code: the installed mapping when the code is offered, else what Resolve finds.

func Linked(id string) bool
func LinkedIDs() []string
func LinkedPacks() []string
func PackOf(id string) (string, bool)
func Parent(id string) string
func DigitSets() []string
func StorageCodes() []string
func Storage(code string) (Currency, bool)
const CLDRVersion = "48.0.0"

Linked, LinkedIDs and LinkedPacks say what this binary links. PackOf names the pack that carries a locale, linked or not. Parent is a locale's CLDR parent, which may be in another pack. DigitSets is every numbering system's digits, so text in any of them can be read. StorageCodes lists the storage set, and Storage gives one code's Currency: its Exponent, and whether it is Eligible for new records. CLDRVersion is the CLDR release the data comes from.

type Locale struct{ /* the marks, digits, patterns and symbols of one locale */ }
type Override struct{ Pattern, Decimal, Group string }
func Data(id string) (Locale, bool)
func Merge(child, parent Locale) Locale
func Register(pack string, locs map[string]Locale)

Locale is one locale's money data, stored in its pack as what differs from its parent. Override is a pattern or mark that applies to one currency only. Data merges a locale with its parents, and Merge merges one pair. Register is how a pack adds its locales when it is imported; you never call it.

Two test packages sit under locales/localestest. gallery links only the twelve languages the framework's strings ship in. crosspack links only en, fr and no, to test the fallbacks across packs.

Read this page as markdown — exact, unstyled, and cheap for an agent to fetch.