forms

package
v2.17.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package forms is the error model of vuka's forms: what is wrong with a field or a whole form, as data — its path, the rule it broke, the rule's parameters and a message key — and the one function that turns an error into the words a visitor reads.

A form is a Go struct embedding ui.FormState; package ui binds, validates and renders it. This package holds what both a form's own methods and the rest of an app need without the component library: Errorf for a Clean method's error, Live to tell a check run while the visitor types from one run on submit, and Text for the message — through the app's message catalogue (Messages, Translations) in the request's language (WithLocale).

func (f *Signup) CleanEmail(ctx context.Context, users *Users) error {
	if forms.Live(ctx) {
		return nil // checked on submit only
	}
	if users.Exists(ctx, f.Email) {
		return forms.Errorf("That email address is already registered")
	}
	return nil
}

Every error carries one dotted path naming its field — "email", "address.city" — the same path the markup's ids, the error summary's links, tests and message keys use. A form-level error has the path "".

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultKey

func DefaultKey(e Error) string

DefaultKey is the message key of an error that names none: "validate." and its rule, with the kind of value the rule measured when the wording differs by it ("validate.max.string", "validate.required.choice").

func Detach

func Detach(ctx context.Context) context.Context

Detach is a context carrying only what ctx's messages are resolved with — its catalogues and languages — for a value that renders messages after ctx's request has ended.

func Live

func Live(ctx context.Context) bool

Live reports whether the check running with ctx is a live one — the visitor is typing, or left a field — rather than the submit's. A Clean method that is slow or has side effects returns nil early when it is: its error then shows on submit, and stays until the field changes.

func Locales

func Locales(ctx context.Context) []string

Locales are ctx's languages, the preferred first; none when ctx names none.

func Param

func Param(n float64) string

Param is n as a rule parameter's text: 80, not 80.000000.

func ParseAcceptLanguage

func ParseAcceptLanguage(h string) []string

ParseAcceptLanguage is the languages an Accept-Language header names, the preferred first: "sw-TZ, en;q=0.8, *;q=0.1" is ["sw-TZ", "en"]. A wildcard and a language weighted 0 are left out.

func PluralCategory

func PluralCategory(locale string, n float64, frac int) string

PluralCategory is the CLDR plural category of n, written with frac digits after its point, in locale's language.

func SetPlural

func SetPlural(lang string, rule PluralRule)

SetPlural sets the plural rule of a language ("ar", "cy"), for one the built-in rules don't cover or cover differently. The built-in rules follow CLDR for English and the languages counting as it does (one for 1, other for the rest: the default), French, Portuguese and Hindi (one for 0 and 1), Russian, Ukrainian and Belarusian, Polish, Czech and Slovak (one, few, many), and the languages with one form (Chinese, Japanese, Korean, Indonesian, Malay, Thai, Vietnamese).

func Text

func Text(ctx context.Context, e Error, label string) string

Text is the message a visitor reads for e, label being its field's label ("Email", "Full name"): e's key looked up in ctx's catalogues, the app's (WithBase), then the default wording; a key none has is the message itself. The label is translated (Translate), then put in the placeholders with e.Params: {label} is the label in a sentence ("full name"), {Label} as it starts one ("Full name"), {n} and the rule's other parameters their values. A plural form chooses its words by a number, in the message's language:

"{Label} must be {n, plural, one {# character} other {# characters}} or less"

It is the one place a message is made, so a catalogue (an app's, a translation's) changes every message the same way: the field's error, the error summary and tests' expectations.

func Translate

func Translate(ctx context.Context, text string) string

Translate is text in ctx's language: the catalogue's entry for it when it has one, text itself otherwise. A label, a hint or a legend is translated by its text ("Full name"), the form components' own words by their keys ("form.problem", "form.optional").

func WithBase

func WithBase(ctx context.Context, c Catalogue) context.Context

WithBase is ctx resolving message keys with c after its catalogues (WithCatalogue) and before the default wording: an app's catalogue, which a request's own override. Package ui adds its Config.Messages so.

func WithCatalogue

func WithCatalogue(ctx context.Context, c Catalogue) context.Context

WithCatalogue is ctx resolving message keys with c before the catalogues ctx had, and the default wording.

func WithLive

func WithLive(ctx context.Context) context.Context

WithLive is ctx for checks run while the visitor is typing, which Live reports. Package ui sets it; a test of a Clean method may too.

func WithLocale

func WithLocale(ctx context.Context, tags ...string) context.Context

WithLocale is ctx whose messages are in the languages tags name, the preferred first ("sw", "en-GB"): what Translations look up. Package web sets a request's from its Accept-Language header; a middleware choosing another (a signed-in visitor's setting) sets it before.

Types

type Catalogue

type Catalogue interface {
	// Message is key's text, its placeholders ({label}, {n}, plural forms)
	// still in it; ok is false when the catalogue has no such key.
	Message(ctx context.Context, key string) (text string, ok bool)
}

A Catalogue gives the text of a message key: an app's own wording (Messages), or its translations (Translations).

type Error

type Error struct {
	Path   string
	Rule   string
	Params map[string]string
	Key    string
}

Error is one thing wrong with a form: Path names the field ("email", "address.city"; "" for the form as a whole), Rule the rule it broke ("required", "max", "email", "number"; "" for a Clean method's error), Params the rule's values ({"n": "80"}), and Key the message to show — a key of the message catalogue, or the message itself when the catalogue has no such key.

func Errorf

func Errorf(format string, args ...any) Error

Errorf is an error of the form, or of the field whose Clean method returns it: its Key is format formatted with args. The key is looked up in the message catalogue (see Text); one the catalogue doesn't have is the message as written, so write it as a sentence a visitor can act on: "That email address is already registered", or as a key a catalogue gives the words of: "signup.email.taken".

func (Error) At

func (e Error) At(path string) Error

At is e at path: a Clean method's error for another field than the one it checks, or a form-level Clean's error for one field.

func (Error) Error

func (e Error) Error() string

type Errors

type Errors []Error

Errors is several errors, as a Clean method may return them.

func (Errors) Error

func (es Errors) Error() string

type LocaleCatalogue

type LocaleCatalogue interface {
	Catalogue
	// MessageIn is key's text and its language tag ("sw", "pt-BR").
	MessageIn(ctx context.Context, key string) (text, locale string, ok bool)
}

A LocaleCatalogue is a Catalogue that knows the language of the text it gives, which chooses the text's plural forms: Translations is one.

type Messages

type Messages map[string]string

Messages is a catalogue of texts by key, in one language:

forms.Messages{
	"validate.required": "{Label} can't be empty",
	"signup.agree":      "Accept the terms to continue",
	"Full name":         "Your name", // a label, by its text
}

A key is a message key — the default wording's ("validate.required", "form.problem", see DefaultMessages), or one an app names in a msg: tag, SetError, Fail or Errorf — or a label's, hint's or legend's text, which the catalogue's entry translates.

func DefaultMessages

func DefaultMessages() Messages

DefaultMessages is a copy of the default wording, by key: what an app's catalogue overrides, and a translation translates.

func (Messages) Message

func (m Messages) Message(_ context.Context, key string) (string, bool)

Message is the text of key.

type PluralRule

type PluralRule func(n float64, frac int) string

A PluralRule is a language's plural category of a number n, written with frac digits after its point: "one", "few", "many" or "other", as the Unicode CLDR names them ("zero" and "two" too, for the languages having them).

type Translations

type Translations map[string]Catalogue

Translations are catalogues by language tag ("en", "sw", "pt-BR"): a message is looked up in the catalogue of the request's first language that has it (WithLocale), a regional tag falling back to its language's ("pt-BR" to "pt"):

ui.Configure(ui.Config{Messages: forms.Translations{
	"sw": forms.Messages{"validate.required": "Weka {label}", "Full name": "Jina kamili"},
	"fr": forms.Messages{"validate.required": "Saisissez {label}"},
}})

A key no language of the request's has falls back to the default English wording.

func (Translations) Message

func (t Translations) Message(ctx context.Context, key string) (string, bool)

Message is the text of key in ctx's languages.

func (Translations) MessageIn

func (t Translations) MessageIn(ctx context.Context, key string) (string, string, bool)

MessageIn is the text of key in the first of ctx's languages that has it, and that language.

Jump to

Keyboard shortcuts

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