Documentation
¶
Overview ¶
Package translation resolves a key into a sentence in a locale.
It holds Translator, MessageSelector, Selector, ArrayLoader, FileLoader, Loader, Lines, Replace, PotentiallyTranslatedString, the four English groups the framework produces sentences from, and Negotiate, Middleware, InLocale and Locale for the locale of a request.
The translator is a value ¶
Translator is built once where the application is wired and passed to whatever renders a sentence. There is no package level translator and no set of free functions that read one.
It is safe for concurrent use: every field is behind a lock, so one instance answers every request at once while Translator.AddLines or Translator.SetLocale is called on it.
The locale comes first on Translator.Get, Translator.Choice and Translator.Has. An empty locale means the translator's own, so a caller that has no request still works.
Two catalogue formats ¶
A group key is "group.item": "auth.failed" is the item "failed" of the group "auth", and "validation.min.string" is the item "min.string", because everything after the first dot is the item. Translator.ParseKey is what splits one, and a "namespace::" prefix names a module's own catalogue.
A JSON key is the sentence itself, read out of one file per locale -- lang/pt-BR.json holding {"Save changes": "Salvar alterações"}. Translator.Get checks it before the groups, because a key with no dot names no item and would otherwise resolve to nothing.
FileLoader reads both, out of an fs.FS: a group at <path>/<locale>/<group>.json, a JSON catalogue at <path>/<locale>.json, and an override an application published for a module at <path>/vendor/<namespace>/<locale>/<group>.json. A language file is data, and nested objects flatten to dotted items, so the four size shapes of a validation message stay grouped in the file and are read as "min.string".
It parses every file under its initial paths before returning and reports a malformed one as an error there: a language file that cannot be parsed is a boot failure, not a sentence that goes missing on the one request that needed it. ArrayLoader is the same catalogue written in Go, which is what a test uses.
The English lines for validation, auth, passwords and pagination are embedded here and answered after the application catalogue and its fallback locale. An application overrides one of them by defining the same key; it never has to publish a file to get a sentence.
Plurals ¶
Translator.Choice takes the count and hands the line to the Selector, which is MessageSelector unless Translator.SetSelector replaced it. A segment may open with an explicit condition -- "{0} none|[1,19] some|[20,*] many" -- and the first one that matches the count wins. With no condition, the count chooses by the plural rule of the locale: two forms in English, one in Japanese, three in Russian, six in Arabic.
Only the segment a condition selected is trimmed, which is worth knowing before writing "one | :count many": the second segment renders with its leading space, because the plural rule is what chose it.
MessageSelector.GetPluralIndex carries the rule table. It is keyed by the language subtag alone, because no region changes the plural forms of its language, and because an Accept-Language header carries a hyphenated tag.
Replacements ¶
Replace fills the placeholders of a line. Three spellings of every name are replaced: ":name" with the value, ":Name" with its first letter uppercased and ":NAME" with it uppercased, so one argument serves "The :attribute field is required." and ":Attribute is required.". A value of type func(string) string is applied differently: it replaces the text between <name> and </name> rather than a placeholder.
A placeholder no argument names is left as it stands, and the longest name that does match wins at each position.
The locale of a request ¶
The locale is a parameter of Translator.Get, Translator.Choice and Translator.Has rather than something the caller sets on the translator, because the locale belongs to the request and one shared translator serves all of them. Middleware negotiates it once from Accept-Language and puts it in the request context, and InLocale puts there a locale the route already decided; Locale reads back whichever of them ran. An application uses one or the other, because a language decided twice is a page with two addresses. Translator.SetLocale sets the default for a caller that passes none -- a console command, or a queued job -- and returns an error for a locale holding a path separator.
PotentiallyTranslatedString carries a validation message that may or may not need translating. PotentiallyTranslatedString.ToString is what writes it back, and the validator calls it explicitly: nothing here runs when the value falls out of scope.
Index ¶
- Constants
- func InLocale(locale string) func(http.Handler) http.Handler
- func Locale(ctx context.Context) string
- func Middleware(supported []string, fallback string) func(http.Handler) http.Handler
- func Negotiate(header string, supported []string, fallback string) string
- func WithLocale(ctx context.Context, locale string) context.Context
- type ArrayLoader
- func (l *ArrayLoader) AddJSONPath(path string)
- func (l *ArrayLoader) AddMessages(locale, group string, messages Lines, namespace string) *ArrayLoader
- func (l *ArrayLoader) AddNamespace(namespace, hint string)
- func (l *ArrayLoader) Load(locale, group, namespace string) Lines
- func (l *ArrayLoader) Namespaces() map[string]string
- type FileLoader
- func (l *FileLoader) AddJSONPath(p string)
- func (l *FileLoader) AddNamespace(namespace, hint string)
- func (l *FileLoader) AddPath(p string)
- func (l *FileLoader) JSONPaths() []string
- func (l *FileLoader) Load(locale, group, namespace string) Lines
- func (l *FileLoader) Namespaces() map[string]string
- func (l *FileLoader) Paths() []string
- type Lines
- type Loader
- type MessageSelector
- type PotentiallyTranslatedString
- func (s *PotentiallyTranslatedString) Original() string
- func (s *PotentiallyTranslatedString) String() string
- func (s *PotentiallyTranslatedString) ToString() string
- func (s *PotentiallyTranslatedString) Translate(replace Replace, locale string) *PotentiallyTranslatedString
- func (s *PotentiallyTranslatedString) TranslateChoice(number int, replace Replace, locale string) *PotentiallyTranslatedString
- type Replace
- type Selector
- type Translator
- func (t *Translator) AddJSONPath(path string)
- func (t *Translator) AddLines(lines map[string]string, locale, namespace string)
- func (t *Translator) AddNamespace(namespace, hint string)
- func (t *Translator) AddPath(path string)
- func (t *Translator) Choice(locale, key string, number int, replace Replace) string
- func (t *Translator) DetermineLocalesUsing(callback func(locales []string) []string)
- func (t *Translator) Get(locale, key string, replace Replace) string
- func (t *Translator) GetFallback() string
- func (t *Translator) GetLoader() Loader
- func (t *Translator) GetLocale() string
- func (t *Translator) GetSelector() Selector
- func (t *Translator) HandleMissingKeysUsing(...) *Translator
- func (t *Translator) Has(locale, key string) bool
- func (t *Translator) HasForLocale(locale, key string) bool
- func (t *Translator) Load(namespace, group, locale string)
- func (t *Translator) Locale() string
- func (t *Translator) ParseKey(key string) (namespace, group, item string)
- func (t *Translator) SetFallback(fallback string)
- func (t *Translator) SetLoaded(loaded map[string]map[string]map[string]Lines)
- func (t *Translator) SetLocale(locale string) error
- func (t *Translator) SetSelector(selector Selector)
- func (t *Translator) Stringable(class any, handler func(any) string)
Constants ¶
const AppNamespace = "*"
AppNamespace is the namespace of the application's own lines, the one a key with no "::" resolves in. It is the key the lines are stored under, and it travels through Translator.AddLines.
const JSONGroup = "*"
JSONGroup is the group the per locale JSON catalogue is loaded under. It is paired with AppNamespace, so the JSON lines of a locale live at loaded["*"]["*"][locale] -- which is where Translator.Get looks first.
Variables ¶
This section is empty.
Functions ¶
func InLocale ¶ added in v0.18.0
InLocale puts a locale the route already decided on every request that reaches it, and says so in the response.
It goes on the route group of one language: the group under /es carries InLocale("es"), and the group with no prefix carries whichever language the unprefixed addresses are written in. The path is the whole input -- no header is read here, and no cookie and no query parameter, because each of those would be a second answer to a question the address already answered.
It writes Content-Language and does not write Vary. What these pages say is a function of the path and of nothing else: one address is in one language for everybody who asks for it, so a shared cache in front of the application may keep one copy of it. Vary here would be false, and it would be paid for on every response rather than on the one that negotiated.
Locale reads the locale back, spelled the way it was passed, so it indexes the catalogue directly. An application that has more to say about a language than its catalogue key -- a BCP 47 tag for hreflang, a label for a selector -- keeps that beside its own list of languages and passes the key here.
func Locale ¶
Locale reads the locale Middleware negotiated for the request, and returns the empty string when there is none.
The empty string is what [Translator.T] reads as "the default locale", so a caller passes this straight through without checking it.
func Middleware ¶
Middleware negotiates the locale of every request from its Accept-Language header and puts it in the context, where Locale reads it.
This is one of the two ways an application decides a language, and InLocale is the other. Here the header decides and one address serves every language; there the path decides and each language has its own address. An application picks one of them: carrying both means one page has two addresses and a reader can be handed either, which is the thing that goes wrong, not the path itself.
The header is the only input on this side. A locale read from a query parameter or a cookie on top of it is a third answer to a question already answered, and each of them is another cache key for one page.
It answers with Content-Language, and adds Accept-Language to Vary: a page whose text depends on a request header and does not say so is a page a shared cache will serve in the wrong language. Vary is the truth about a negotiated response and belongs on every response this wraps -- which is also why it must not be reached for when nothing was negotiated, and why InLocale exists rather than a flag here.
func Negotiate ¶
Negotiate picks the locale of an Accept-Language header out of the ones the application supports, and returns fallback when none of them fit.
Tags are read in the order of their quality value, highest first, and ties keep the order the client wrote. A tag matches a supported locale exactly, case insensitively and whichever separator either spells it with; failing that it matches by language, so a client asking for pt-BR is served pt and a client asking for pt is served the first pt-* on offer. "*" takes the first supported locale. A tag at q=0 is refused rather than matched.
The answer is always spelled the way the application spells it, so it indexes the catalogue directly.
Types ¶
type ArrayLoader ¶
type ArrayLoader struct {
// contains filtered or unexported fields
}
ArrayLoader is a catalogue written in Go rather than read from files. It is what a test translates against.
It is safe for concurrent use: one loader answers every request, and ArrayLoader.AddMessages may be called while it does.
func NewArrayLoader ¶
func NewArrayLoader() *ArrayLoader
NewArrayLoader answers ArrayLoader::__construct(). The loader starts empty; ArrayLoader.AddMessages fills it.
func (*ArrayLoader) AddJSONPath ¶
func (l *ArrayLoader) AddJSONPath(path string)
AddJSONPath answers ArrayLoader::addJsonPath(), which does nothing for the same reason.
func (*ArrayLoader) AddMessages ¶
func (l *ArrayLoader) AddMessages(locale, group string, messages Lines, namespace string) *ArrayLoader
AddMessages stores one group of one locale, and returns the loader so that calls chain.
An empty namespace means AppNamespace. The lines are copied, so the caller keeps no way to write into a loader that requests are reading.
func (*ArrayLoader) AddNamespace ¶
func (l *ArrayLoader) AddNamespace(namespace, hint string)
AddNamespace answers ArrayLoader::addNamespace(), which does nothing: an array loader holds its namespaced lines already and has no path to hint at.
func (*ArrayLoader) Load ¶
func (l *ArrayLoader) Load(locale, group, namespace string) Lines
Load reads one group of one locale. An empty namespace is read as AppNamespace.
func (*ArrayLoader) Namespaces ¶
func (l *ArrayLoader) Namespaces() map[string]string
Namespaces is always empty for an ArrayLoader.
type FileLoader ¶
type FileLoader struct {
// contains filtered or unexported fields
}
FileLoader is a catalogue read from a filesystem.
A group lives at <path>/<locale>/<group>.json, a namespaced group at the hint registered for the namespace, an override of a namespaced group at <path>/vendor/<namespace>/<locale>/<group>.json, and the JSON catalogue of a locale at <path>/<locale>.json. Those are the four shapes it reads. A language file is data, never code.
Every path is read through one fs.FS. A caller reading the project's lang directory passes os.DirFS; the English lines that ship with this package are a FileLoader over the embedded catalogue.
It is safe for concurrent use.
func NewFileLoader ¶
func NewFileLoader(files fs.FS, paths ...string) (*FileLoader, error)
NewFileLoader answers FileLoader::__construct(). It reads paths out of files, and every path holds the locale directories.
It parses everything under those paths before returning, and reports a malformed file as an error here: a language file that cannot be read is a boot failure, not a sentence that goes missing on the one request that needed it. FileLoader.AddPath and FileLoader.AddJSONPath cannot report that way -- they answer void methods -- so a file added later that does not parse is skipped when it is asked for.
func (*FileLoader) AddJSONPath ¶
func (l *FileLoader) AddJSONPath(p string)
AddJSONPath registers a directory holding per locale JSON catalogues.
func (*FileLoader) AddNamespace ¶
func (l *FileLoader) AddNamespace(namespace, hint string)
AddNamespace answers FileLoader::addNamespace(). The hint is the path the namespace's locale directories sit in.
func (*FileLoader) AddPath ¶
func (l *FileLoader) AddPath(p string)
AddPath registers a directory of catalogues. The path is read the first time a group under it is asked for; a file there that does not parse is skipped, because there is nowhere here to report it from. NewFileLoader is where a malformed catalogue is an error.
func (*FileLoader) JSONPaths ¶
func (l *FileLoader) JSONPaths() []string
JSONPaths answers FileLoader::jsonPaths(). It returns a copy of the registered JSON catalogue paths.
func (*FileLoader) Load ¶
func (l *FileLoader) Load(locale, group, namespace string) Lines
Load answers FileLoader::load(). The pair ("*", "*") asks for the JSON catalogue of the locale; an empty or "*" namespace asks the ordinary paths; anything else asks the hint registered for that namespace, and then the overrides the application published for it.
func (*FileLoader) Namespaces ¶
func (l *FileLoader) Namespaces() map[string]string
Namespaces answers FileLoader::namespaces(). It returns a copy, so a caller cannot register a namespace by writing into what it was handed.
func (*FileLoader) Paths ¶
func (l *FileLoader) Paths() []string
Paths answers FileLoader::paths(). It returns a copy of the registered catalogue paths, in the order they are read.
type Lines ¶
Lines is one group of a catalogue, in one locale: the item path of every line mapped to the line itself.
The item path is dotted. A file that nests "string" under "min" is read as the single item "min.string", so the file keeps the shape a human edits and the lookup keeps one flat form.
func Bundled ¶ added in v0.18.0
Bundled returns the lines the framework's own catalogue carries for one group of one locale, with nested objects flattened to dotted items, and nil for a group it does not carry.
It is for a package that produces one of these sentences without holding a Translator -- the validator answering with no catalogue configured, before an application has wired one. That package reads the sentences from here rather than keeping its own copy of them: two tables of the same English is two answers to what a message says, and which one a project reads is decided by which of them happened to be loaded.
The catalogue that ships is English, so "en" is the locale that answers.
The map is a copy. The catalogue is read once and shared by every Translator, and a caller able to write into it would be editing the sentences of all of them.
type Loader ¶
type Loader interface {
// Load reads one group of one locale. An empty namespace means
// AppNamespace, and the pair ("*", "*") asks for the JSON catalogue of
// the locale.
Load(locale, group, namespace string) Lines
// AddNamespace answers addNamespace(): it points a namespace at the place
// its lines are loaded from.
AddNamespace(namespace, hint string)
// AddJSONPath registers a directory holding per locale JSON catalogues.
AddJSONPath(path string)
// Namespaces is every registered namespace, mapped to its hint.
Namespaces() map[string]string
}
Loader answers with the lines of a group in a locale.
It returns nil for a group it does not carry, which is not an error: a catalogue is not required to translate everything, and the Translator falls through to the fallback locale and then to the English lines that ship here.
AddNamespace and AddJSONPath are on the contract because Translator forwards to them; a loader with no notion of a path implements them as no-ops, as ArrayLoader does.
type MessageSelector ¶
type MessageSelector struct{}
MessageSelector is the arithmetic that turns one line holding every plural form into the form a count needs.
The zero value is ready to use and holds nothing, so one instance serves every request.
func (MessageSelector) Choose ¶
func (s MessageSelector) Choose(line string, number int, locale string) string
Choose selects the segment of line that number calls for, out of the segments divided by "|".
There are two syntaxes and both are read, in this order:
- An explicit condition opens a segment: "{0}" matches one count, and "[1,19]" or "[20,*]" match a range, with "*" for an open end. The first segment whose condition matches wins, and only it is trimmed.
- With no condition matching, every condition is stripped and the plural rule of the locale indexes what is left: "one apple|:count apples" is two forms in English and "яблоко|яблока|яблок" is three in Russian.
Only the first path trims: the value extracted by a condition is trimmed and nothing else is, so "{1} one | :count many" answers "one" for one and " :count many", with its leading space, for four. A line whose segments are spaced out either side of the bar renders that space on the page.
A line with fewer segments than the rule has forms falls back to the first, which is what a catalogue translated only for the singular needs.
func (MessageSelector) GetPluralIndex ¶
func (MessageSelector) GetPluralIndex(locale string, number int) int
GetPluralIndex reports which segment of a line a count selects in a locale, counting from zero: English answers 0 for one and 1 for anything else, Japanese always answers 0, Russian has three forms and Arabic six.
A locale whose rule is not carried answers 0: that is the right answer for a language with no plural distinction and the safe one for a language nobody has written a rule for.
The plural rules are derived from code of the Zend Framework (2010-09-25), which is subject to the new BSD license.
The locale is matched on the language subtag alone, so pt-BR and pt_BR and pt resolve the same rule: no region changes the plural forms of its language, and an Accept-Language header carries a hyphenated tag. The count is read by magnitude, so -2 selects the form 2 does.
type PotentiallyTranslatedString ¶
type PotentiallyTranslatedString struct {
// contains filtered or unexported fields
}
PotentiallyTranslatedString is a message that is a key if the catalogue carries it and the message itself if it does not.
It is what a validation rule hands back when it fails. The rule writes "The :attribute must be a working URL." or "validation.custom.url", calls PotentiallyTranslatedString.Translate, and the caller reads PotentiallyTranslatedString.ToString without having to know which of the two it was given.
func NewPotentiallyTranslatedString ¶
func NewPotentiallyTranslatedString(s string, translator *Translator) *PotentiallyTranslatedString
NewPotentiallyTranslatedString answers PotentiallyTranslatedString::__construct().
func (*PotentiallyTranslatedString) Original ¶
func (s *PotentiallyTranslatedString) Original() string
Original answers PotentiallyTranslatedString::original(): the string as it was given, whether or not it has since been translated.
func (*PotentiallyTranslatedString) String ¶
func (s *PotentiallyTranslatedString) String() string
String satisfies fmt.Stringer. It is PotentiallyTranslatedString.ToString.
func (*PotentiallyTranslatedString) ToString ¶
func (s *PotentiallyTranslatedString) ToString() string
ToString answers PotentiallyTranslatedString::toString(): the translation when there is one, and the original string when there is not.
func (*PotentiallyTranslatedString) Translate ¶
func (s *PotentiallyTranslatedString) Translate(replace Replace, locale string) *PotentiallyTranslatedString
Translate resolves the string through the translator and keeps the result.
An empty locale means the translator's own. It returns the same value so that calls chain.
func (*PotentiallyTranslatedString) TranslateChoice ¶
func (s *PotentiallyTranslatedString) TranslateChoice(number int, replace Replace, locale string) *PotentiallyTranslatedString
TranslateChoice answers PotentiallyTranslatedString::translateChoice(): it resolves the string as a pluralised line and keeps the result.
type Replace ¶
Replace holds the arguments a line interpolates, keyed by the placeholder name without its colon: Replace{"attribute": "email"} fills ":attribute", ":Attribute" and ":ATTRIBUTE".
Values are rendered with fmt.Sprint, so a count may be given as an int and a duration as a time.Duration without the caller formatting it first. A value of type func(string) string is applied differently: it replaces the text between <name> and </name> rather than a placeholder.
type Selector ¶
type Selector interface {
// Choose selects the segment of line that number calls for.
Choose(line string, number int, locale string) string
// GetPluralIndex reports which segment a count selects in a locale.
GetPluralIndex(locale string, number int) int
}
Selector is what Translator.SetSelector takes: the thing that picks one segment of a pluralised line.
It is the extension point for an application with plural rules of its own. MessageSelector is the implementation that ships.
type Translator ¶
type Translator struct {
// contains filtered or unexported fields
}
Translator resolves a key into a sentence.
One instance answers every request. It caches the groups it has loaded, and the setters below are callable at any time, so every field is behind a lock.
func New ¶
func New(l Loader, locale, fallback string) *Translator
New returns a translator over a loader.
locale is the locale used when a caller passes none, and fallback the one consulted when a key is missing from the locale asked for. The English lines that ship with this package are answered after both, so auth, validation, passwords and pagination resolve with no catalogue at all -- l may be nil.
They are answered by a second loader rather than a second path, so that a translator built over an ArrayLoader still answers them.
func (*Translator) AddJSONPath ¶
func (t *Translator) AddJSONPath(path string)
AddJSONPath answers Translator::addJsonPath(): another directory of per locale JSON catalogues for the loader to read.
func (*Translator) AddLines ¶
func (t *Translator) AddLines(lines map[string]string, locale, namespace string)
AddLines answers Translator::addLines(): translation lines written straight into the loaded array, under keys of the form "group.item".
An empty namespace means AppNamespace. A group written this way is marked loaded, so the loader is never asked for it -- which is how a test, or a module registering its own text at boot, replaces a group rather than merging into one.
func (*Translator) AddNamespace ¶
func (t *Translator) AddNamespace(namespace, hint string)
AddNamespace answers Translator::addNamespace(): it points a namespace at the place its lines are loaded from, by forwarding to the loader.
func (*Translator) AddPath ¶
func (t *Translator) AddPath(path string)
AddPath registers another directory of locale directories for the loader to read. A loader with no notion of a path ignores it.
func (*Translator) Choice ¶
func (t *Translator) Choice(locale, key string, number int, replace Replace) string
Choice answers Translator::choice(): the segment of a line that a count selects, with its placeholders replaced.
The segments of a line are divided by "|" and may open with an explicit condition, "{0}" for a single count and "[1,19]" or "[20,*]" for a range. The first condition that matches wins; with no condition, the plural rule of the locale chooses. See MessageSelector.Choose.
":count" is filled with count unless replace already carries it. The count is the number itself; use len at the call site for a collection.
func (*Translator) DetermineLocalesUsing ¶
func (t *Translator) DetermineLocalesUsing(callback func(locales []string) []string)
DetermineLocalesUsing answers Translator::determineLocalesUsing(): it registers a callback that says which locales a key is looked for in, given the locale asked for and the fallback.
It is what a regional catalogue needs: a translator asked for pt-BR can answer out of pt before it reaches the fallback.
func (*Translator) Get ¶
func (t *Translator) Get(locale, key string, replace Replace) string
Get is the line stored under key, with its placeholders replaced.
The locale comes first and the replacements last. An empty locale means the translator's own.
A key no catalogue carries is returned unchanged, after the callback registered by Translator.HandleMissingKeysUsing has seen it, so a wrong key shows as itself on the page instead of as a blank.
A key naming a group and no item -- "messages" rather than "messages.welcome" -- returns the key, because the result is a sentence and a group is not one.
func (*Translator) GetFallback ¶
func (t *Translator) GetFallback() string
GetFallback answers Translator::getFallback(): the locale consulted when a key is missing from the one asked for.
func (*Translator) GetLoader ¶
func (t *Translator) GetLoader() Loader
GetLoader answers Translator::getLoader(): the loader this translator was built with.
It is not the loader the lookups go through -- that one is this loader followed by the English lines that ship with the package. Returning the pair would hand out a way to write into the bundled catalogue.
func (*Translator) GetLocale ¶
func (t *Translator) GetLocale() string
GetLocale answers Translator::getLocale(): the locale used when a caller passes none.
func (*Translator) GetSelector ¶
func (t *Translator) GetSelector() Selector
GetSelector answers Translator::getSelector(): the thing that picks a segment of a pluralised line.
func (*Translator) HandleMissingKeysUsing ¶
func (t *Translator) HandleMissingKeysUsing(callback func(key string, replace Replace, locale string, fallback bool) string) *Translator
HandleMissingKeysUsing answers Translator::handleMissingKeysUsing(): it registers what runs when no catalogue carries a key.
The translator still returns the key; this is the hook that reports it, to a logger in development or to a counter in production, and it may answer with a sentence of its own. A nil callback removes the one registered.
func (*Translator) Has ¶
func (t *Translator) Has(locale, key string) bool
Has answers Translator::has(): whether a line exists for key, in the given locale or in the fallback.
func (*Translator) HasForLocale ¶
func (t *Translator) HasForLocale(locale, key string) bool
HasForLocale answers Translator::hasForLocale(): whether a line exists for key in the given locale alone, with no fall through to the fallback locale.
func (*Translator) Load ¶
func (t *Translator) Load(namespace, group, locale string)
Load answers Translator::load(): it asks the loader for one group of one locale and keeps what comes back.
A group that is already loaded is not loaded again, which is what makes Translator.AddLines and Translator.SetLoaded stick.
func (*Translator) Locale ¶
func (t *Translator) Locale() string
Locale is the locale used when a caller passes none. It is Translator.GetLocale under the name that reads better in a sentence.
func (*Translator) ParseKey ¶
func (t *Translator) ParseKey(key string) (namespace, group, item string)
ParseKey answers Translator::parseKey(): a key split into namespace, group and item.
"messages.welcome" is the item "welcome" of the group "messages" in the application namespace; "shop::orders.title" names the namespace "shop"; and "validation.min.string" is the item "min.string", because everything after the first dot is the item. A key with no dot names a group and no item, and the item comes back empty.
A key with no namespace resolves in AppNamespace.
func (*Translator) SetFallback ¶
func (t *Translator) SetFallback(fallback string)
SetFallback answers Translator::setFallback().
func (*Translator) SetLoaded ¶
func (t *Translator) SetLoaded(loaded map[string]map[string]map[string]Lines)
SetLoaded answers Translator::setLoaded(): it replaces the loaded groups wholesale, keyed by namespace, then group, then locale.
Passing nil empties it, which is how a long lived process drops a catalogue it has finished with.
func (*Translator) SetLocale ¶
func (t *Translator) SetLocale(locale string) error
SetLocale answers Translator::setLocale(): it sets the locale used when a caller passes none.
A locale holding "/" or "\" is refused, because a locale reaches the filesystem as a directory name and one carrying a separator reads a catalogue somewhere else.
func (*Translator) SetSelector ¶
func (t *Translator) SetSelector(selector Selector)
SetSelector answers Translator::setSelector(). A nil selector is ignored: a translator with no selector cannot answer Choice at all.
func (*Translator) Stringable ¶
func (t *Translator) Stringable(class any, handler func(any) string)
Stringable registers how one type is rendered when it is passed as a replacement.
The type is named by a zero value of it: pass money.Amount{} to say how a money.Amount renders. The handler receives the value that was passed to Translator.Get and must assert it back.
A nil handler removes the one registered for the type.