pwmsg

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package pwmsg reads message catalogs and generates the typed Go package a template's message reference resolves against.

It is a host-side tool: it runs during pw generate and never in a served process. See .knowledge decision:message-code-shape.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExportPO

func ExportPO(catalog *Catalog, locale string) []byte

ExportPO renders one locale as a gettext PO catalog.

PO rather than XLIFF because the mapping is exact and the format is line-based: msgctxt carries the message ID, msgid the source text, and msgstr the translation. That is the same asymmetry decision:message-source-of-truth records — the source is shown to the translator and is not theirs to edit — so nothing has to be invented to express it.

The source text comes from the declared source locale, so a translator sees what they are translating from rather than an identifier.

func ExtractMarks

func ExtractMarks(filename string, source []byte) ([]Mark, []MarkProblem, error)

ExtractMarks reports every marked string in one template.

A mark on an element whose children are not one run of text is declined rather than guessed at: that shape is rich text, whose holes name markup the template supplies, and a tool inventing hole names would produce translations no translator can check. It is reported so the author converts it by hand.

func Holes

func Holes(pieces []Piece) []string

Holes returns the hole names a parsed translation opens, sorted.

func Placeholders

func Placeholders(pieces []Piece) []string

Placeholders returns the placeholder names a parsed translation uses, sorted.

func ProposeID

func ProposeID(text string, element string, ordinal int) (id string, derived bool)

ProposeID derives a message ID from source text.

It is best effort by design: a slug from Latin text is mechanical, and a slug from kanji needs readings this build deliberately does not ship. A text it cannot slug produces a positional name the author renames, which costs what naming a variable costs. See .knowledge decision:message-id-assignment.

func RenameEntry

func RenameEntry(source []byte, from, to string) ([]byte, bool)

RenameEntry rewrites one scope file so an ID becomes another.

It edits the key line textually rather than re-marshalling, so the comments, the ordering, and the formatting a translator put in the file survive. A rename is one line changing; nothing else about the file should move.

func RenderEntry

func RenderEntry(id, text string, locales []string, sourceLocale string) string

RenderEntry renders one catalog entry as YAML, ready to append to a scope file.

It is appended as text rather than written by re-marshalling the file, because re-marshalling drops the comments and the ordering a translator put there and this tool has no business rewriting what it did not add.

func Rewrite

func Rewrite(source []byte, marks []Mark, ids []string) ([]byte, error)

Rewrite replaces each mark with a reference and removes the mark attribute.

Replacements are applied from the end of the file backwards, so an earlier range is not shifted by a later edit.

Types

type Catalog

type Catalog struct {
	Locales []string
	Default string
	Scopes  []Scope
	// Routing is the locale mode per path prefix, the display labels, and
	// whether the default locale carries a prefix. It is generated into the
	// message package because all of it is build configuration the served
	// process cannot otherwise know. See .knowledge decision:locale-url-modes.
	Routing Routing
}

Catalog is every message a project declares, read from the directory named by i18n.catalog.

One file is one scope, because decision:message-scope-declaration makes the scope a template writes the natural sharding key: two translators working on separate features do not contend on one file.

func Load

func Load(dir string, locales []string, defaultTag string) (*Catalog, error)

Load reads every catalog file under dir. locales and defaultTag come from data:i18n-config and are carried on the result because generation needs the declared order, not the order a file happens to list.

type Category

type Category string

Category is a CLDR plural category. The set is closed and ordered so a generated table row can be addressed by position.

const (
	Zero  Category = "zero"
	One   Category = "one"
	Two   Category = "two"
	Few   Category = "few"
	Many  Category = "many"
	Other Category = "other"
)

type Diagnostic

type Diagnostic struct {
	Severity Severity
	Path     string
	Line     int
	Message  string
}

Diagnostic is one finding against a catalog.

func Validate

func Validate(catalog *Catalog, missing Severity) []Diagnostic

Validate checks a catalog against the declared locale set.

missing is the severity of a locale that supplies no translation for a message, which data:i18n-config leaves to the project: a build that must ship complete fails, and a build still being translated reports and falls back. Every other finding here has a fixed severity, because a placeholder that does not match its declaration renders wrong text in a way no fallback repairs.

func (Diagnostic) String

func (d Diagnostic) String() string

type Entry

type Entry struct {
	// ID is the bare name within the scope. Qualified() joins it to the scope.
	ID     string
	Params []Param
	// Plural names the parameter driving category selection, empty for a
	// message with no plural variation.
	Plural string
	// Rich marks a message whose translations carry holes, which renders
	// through the segment path of policy:message-rich-text rather than as a
	// string.
	Rich bool
	// Snapshot is the source text recorded when the ID was assigned. It is
	// written by tooling and compared, never edited by hand: a source text
	// differing from it is what marks every other locale stale, per
	// decision:message-id-assignment.
	Snapshot string
	Texts    map[string]Text
	// Line is where the entry starts in its file, for diagnostics.
	Line int
}

Entry is one message.

func (Entry) Qualified

func (e Entry) Qualified(scope string) string

Qualified returns scope.id, which is what a template reference resolves to.

type Generated

type Generated struct {
	// Source is the formatted Go file.
	Source []byte
	// Symbols maps a resolved message ID to the symbol a reference calls.
	Symbols map[string]Symbol
}

Generated is the output of one generation run.

func Generate

func Generate(catalog *Catalog, packageName string) (*Generated, error)

Generate emits the message package for a catalog.

packageName is the Go package the file is written into. Every table is data and every function is code, so adding a locale grows the file by rows and not by functions. See .knowledge decision:message-code-shape.

type Mark

type Mark struct {
	// Text is the source text that becomes the message.
	Text string
	// Start and End are the byte range the reference replaces. The range is
	// source rather than content, so replacing it also replaces any escape the
	// text contained.
	Start, End int
	// AttributeRange is the range of the i18n attribute itself, removed by the
	// same rewrite.
	AttributeStart, AttributeEnd int
	// Element names what carried the mark, used when a slug cannot be derived
	// from the text.
	Element string
	// Attribute names the attribute whose value was marked, empty for element
	// text.
	Attribute string
	// Line is where the mark is, for reporting.
	Line int
}

Mark is one piece of source text an author flagged for extraction.

The flag is an ordinary HTML attribute, so it needs no grammar of its own and is removed by the rewrite. See .knowledge decision:message-id-assignment.

type MarkProblem

type MarkProblem struct {
	Line    int
	Element string
	Reason  string
}

MarkProblem is a mark this extractor declines to act on.

type POEntry

type POEntry struct {
	ID       string
	Simple   string
	Variants map[Category]string
}

POEntry is one translated unit read back from a PO file.

func ImportPO

func ImportPO(data []byte, locale string) ([]POEntry, error)

ImportPO reads translations back.

Only msgstr is read. The source text and the ID are the catalog's, and a PO file that disagrees about either is reporting an edit a translator should not have been able to make — so it is ignored rather than applied.

type Param

type Param struct {
	Name string
	Type string
}

Param is one declared argument.

type Piece

type Piece struct {
	// Hole is the hole this piece sits inside, empty at the top level.
	Hole string
	// Arg names the placeholder this piece renders, empty for a literal run.
	Arg string
	// Lit is the literal text, used when Arg is empty.
	Lit string
}

Piece is one parsed span of a translation.

A translation is plain text: the only markup it may carry is a hole name, and a hole stands for an element the template supplies. See .knowledge policy:message-rich-text.

func ParseText

func ParseText(text string, rich bool) ([]Piece, error)

ParseText splits a translation into pieces.

Placeholders are written {name}. A literal brace is written {{ or }}, matching the template language rather than inventing a second escape for the same character.

Holes are written <name>text</name> and are recognised only when rich is set, so a translation in an ordinary message may contain angle brackets as text. That matters because a message is escaped by the template for its position, so "a < b" is legitimate content.

type PluralRule

type PluralRule struct {
	// Categories are the ones this locale uses, in categoryOrder.
	Categories []Category
	// Selector is Go statements over the parameter n returning the position of
	// the matching category within Categories.
	//
	// It is statements rather than an expression because Go has no conditional
	// expression, and the expression forms that imitate one — a map literal
	// indexed by a bool, a slice indexed by a converted bool — allocate or
	// bounds-check on a path every rendered message crosses.
	//
	// Empty means the locale has one category, where generation writes a
	// constant instead.
	Selector string
}

PluralRule is the cardinal plural behaviour of one locale: which categories it distinguishes, and the Go expression selecting one for a value.

Only declared locales are generated, so a single-locale Japanese project emits a selector that is a constant. See .knowledge decision:message-code-shape.

func RuleFor

func RuleFor(tag string) (PluralRule, bool)

RuleFor returns the cardinal rule of a locale, matching the base language subtag so pt-BR follows pt.

A locale with no known rule reports the single-category rule and false, which is correct for a catalog that declares no variants and is what the caller turns into UnknownPluralLocale when it declares some.

type Route

type Route struct {
	Prefix string
	Mode   string
}

Route binds one path prefix to a locale mode.

type Routing

type Routing struct {
	// Routes are prefix and mode pairs, the mode being "path", "cookie", or
	// "header".
	Routes []Route
	// Labels is the display name of each locale, written in that locale.
	Labels map[string]string
	// PrefixDefault decides whether the default locale carries a path prefix.
	PrefixDefault bool
}

Routing is the build-time locale routing declaration.

type Scope

type Scope struct {
	Name    string
	Path    string
	Entries []Entry
}

Scope is one catalog file.

type Severity

type Severity int

Severity separates what stops a build from what a reader should see and decide about.

const (
	// Warning is reported and does not stop generation.
	Warning Severity = iota
	// Error stops generation.
	Error
)

func (Severity) String

func (s Severity) String() string

type Symbol

type Symbol struct {
	// Name is the exported Go function.
	Name string
	// Params are the argument names in declaration order, excluding the leading
	// locale the implicit binding supplies.
	Params []string
	// Rich marks a message whose function returns segments rather than a
	// string.
	Rich bool
}

Symbol is what one resolved message ID calls. It is the downstream half of the table system:tinybind is given, per decision:upstream-message-surface.

type Text

type Text struct {
	Simple   string
	Variants map[Category]string
}

Text is one locale's translation: either a single form, or one per plural category the locale distinguishes.

func (Text) Forms

func (t Text) Forms() ([]Category, bool)

Forms returns the translation's forms in categoryOrder, and whether it varies.

func (*Text) UnmarshalYAML

func (t *Text) UnmarshalYAML(data []byte) error

UnmarshalYAML accepts either a scalar or a mapping of category to scalar, so a locale with no plural variation reads as the string it is.

type UnknownPluralLocale

type UnknownPluralLocale struct {
	Tag string
}

UnknownPluralLocale is reported for a locale whose rule this build cannot state. It is an error only when the catalog declares plural variation for that locale: a locale using one form throughout needs no rule.

func (UnknownPluralLocale) Error

func (e UnknownPluralLocale) Error() string

Jump to

Keyboard shortcuts

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