Translating interface messages

Zotonic uses English source messages and gettext PO catalogs for interface translations. Language configuration and selection are provided by module#mod_translation; Erlang code looks up messages through the z_trans API.

Mark messages for translation

In templates, use {_ Save _} for a simple label, _"Save" for a translated argument, or tag#trans_ext for a message with named placeholders:

{% trans "Hello {name}" name=user_name %}
{% trans "{n} item" "{n} items" n=count %}

Keep source messages, message contexts, and both plural source strings literal so POT extraction can discover them. Preserve placeholders such as {name} and {n} exactly in every translation. Translate complete messages instead of concatenating words: surrounding text can change word order and grammatical case.

Choose a message context

Prefer clear English source text, such as “First name” instead of “First”. When identical English text needs different translations, use a stable semantic msgctxt, such as “action” versus “status” for “Open”. A message context is part of its gettext identity, not a language or an interpolation argument.

Templates can set a default with tag#msgctxt, override it inside tag#with_msgctxt, or pass an explicit msgctxt to tag#trans_ext. A module's -mod_msgctxt attribute supplies a default for its templates. See template message contexts for precedence and how includes and inheritance behave. Erlang calls supply their context explicitly.

Adding a context creates a new catalog entry. Template lookup tries contextual then contextless translations in each language before moving to the next fallback language. This keeps existing general translations usable during migration. The final fallback is the English source.

Contextual Erlang APIs are strict by default: they do not borrow a contextless translation, which might have a different meaning or grammatical case. The Erlang API reference describes how to opt into the template policy.

Translate plural messages

The two source strings describe the English singular and plural. They do not limit other languages to two forms: each PO catalog's Plural-Forms header specifies its own rule and number of forms. The count n must be a non-negative integer.

Within one message context, use the same singular/plural source pair consistently. POT extraction reports conflicting singular/plural definitions for the same msgid. Give messages with different meanings distinct source text or contexts. Use separate complete messages for “{n} hours ago” and “in {n} hours”.

Template compilation precompiles the available translated patterns. At rendering time the count selects a form using the selected catalog's rule. Empty forms and invalid rule results are skipped during fallback; each fallback catalog uses its own rule. A catalog without a usable plural header supplies no plural translations. Translation reloads invalidate compiled templates.

See the extended trans tag for template syntax and the Erlang translation API for resolving plural translations and substituting arguments in Erlang.