pkgproxy design: localization
How errors, log messages and setup guidance are translated. Part of the pkgproxy design, whose index says which file holds each § number.
22. Localization: translatable errors and setup guidance
Every user-visible string goes through go-i18n’s l10n.D domain
(internal/l10n). The .po catalog is maintained with go tool i18n scan|merge|check -C internal/l10n. The scanner follows constants passed
to Tr/TrN/Errorf, so a shared message can live in a const.
It extracts a comment next to a call as a translator comment only when
the comment starts with TRANSLATORS:, so ordinary code and doc
comments stay out of the catalog.
- Errors are built with
l10n.D.Errorf, neverfmt.Errorforerrors.New. That includes sentinels (var errX = l10n.D.Errorf("...")): Errorf returns a distinct pointer, soerrors.Isidentity is preserved.Error()stays English, so logs and tests are locale-stable. go-cmdr’s error printer detectsi18n.LocalizedErrorand shows the translated text, wrapped chain included. - Wording: a failed action reads
cannot <verb> ..., never “failed to …”, “could not …” or a bare gerund wrapper (“creating X: %w”). Errors that describe a state (“malformed field line”) stay as they are. Package prefixes (apt:,lxdprovision:) are kept. Daemon log messages and HTTP error bodies use the same wording. HTTP error bodies are not translated, because HTTP clients carry no locale. That includes the payloads of a protocol: the message of an error result that snapd shows as it is (§11.7), the store-style error list a refused snapd request is answered with (§21) and the plain-text bodyoffline.PlainAnswergives other clients. They are English, each a whole sentence (or a format with the snap and revision in it) kept in one place, never a fragment glued to another or to a translated string, so that a message is the same whatever locale the daemon runs in and tests and people can match on it. The only error spliced into one is that of the assertion chain check, whoseError()is English by the rule above. - Log messages are translated by
l10n.NewLogHandler, which wraps the daemon’ssloghandler. It translates each record’s message and renders error-valued attributes throughD.LocalizedError, using the locale of the record’s context when it has one and the daemon’s otherwise. Attribute keys and other values stay as they are, so log lines keep the same machine-readable shape in every locale. Log messages are never passed toTrin source, so each call site wraps its message ini18n.MarkForTranslation(...)for the scanner to extract. A message that comes from a library at run time (the checksum database client’s) passes through untranslated. The snap’s daemon runs under systemd, normally in the C locale, so in that deployment logs stay English unless the service is given a locale. - Setup guidance (
pkgproxyctl setup *on a TTY, and the daemon’s startup banner) is assembled withguide(cmd/pkgproxy/guide.go): translated prose paragraphs, reflowed to 74 columns, interleaved with verbatim, computed code blocks. A msgid is one unwrapped paragraph of prose with%s/%qplaceholders for paths and names. Commands, config and YAML are never part of a msgid, so translators never see shell, and changing a command never invalidates a translation. Piped (| sh) output stays untranslated, except for the messages the scripts themselves print (shellFail), which are translated and shell-quoted (§14.2).