{"result":{"depiction_url":null,"edges":{"refers":{"objects":[{"created":"2026-11-02T11:36:17Z","object_id":{"id":1381,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_trans_ext","title":"trans_ext","uri":"https:\/\/zotonic.com\/id\/1381"},"seq":1000000},{"created":"2026-11-02T11:36:17Z","object_id":{"id":1410,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_translation","title":"mod_translation","uri":"https:\/\/zotonic.com\/id\/1410"},"seq":1000000},{"created":"2026-11-02T11:36:17Z","object_id":{"id":3129,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_with_msgctxt","title":"with_msgctxt","uri":"https:\/\/zotonic.com\/id\/3129"},"seq":1000000},{"created":"2026-11-02T11:36:17Z","object_id":{"id":3130,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_msgctxt","title":"msgctxt","uri":"https:\/\/zotonic.com\/id\/3130"},"seq":1000000},{"created":"2026-11-02T11:44:28Z","object_id":{"id":3132,"is_a":["text","documentation","reference"],"name":"doc_reference_z_trans","title":"Erlang translation API: z_trans","uri":"https:\/\/zotonic.com\/id\/3132"},"seq":1000000}],"predicate":{"id":2409,"is_a":["meta","predicate"],"name":"refers","title":{"_type":"trans","tr":{"en":"Refers"}},"uri":"https:\/\/zotonic.com\/id\/refers"}},"subject":{"objects":[{"created":"2026-11-02T11:36:17Z","object_id":{"id":2556,"is_a":["categorization","keyword","keyword_information_type"],"name":"zotonic_topic_explanation","title":"Explanation","uri":"https:\/\/zotonic.com\/id\/2556"},"seq":1},{"created":"2026-11-02T11:36:17Z","object_id":{"id":2563,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_frontend_developer","title":"Frontend developer","uri":"https:\/\/zotonic.com\/id\/2563"},"seq":2},{"created":"2026-11-02T11:36:17Z","object_id":{"id":2564,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_backend_developer","title":"Backend developer","uri":"https:\/\/zotonic.com\/id\/2564"},"seq":3},{"created":"2026-11-02T11:36:17Z","object_id":{"id":2588,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_localization_and_translation","title":"Localization and translation","uri":"https:\/\/zotonic.com\/id\/2588"},"seq":4},{"created":"2026-11-02T11:36:17Z","object_id":{"id":2669,"is_a":["categorization","keyword","keyword_data_type"],"name":"zotonic_topic_translated_text","title":"Translated text","uri":"https:\/\/zotonic.com\/id\/2669"},"seq":5}],"predicate":{"id":308,"is_a":["meta","predicate"],"name":"subject","title":{"_type":"trans","tr":{"en":"Keyword"}},"uri":"http:\/\/purl.org\/dc\/elements\/1.1\/subject"}}},"export_revision":46,"id":3131,"is_a":["text","documentation","developerguide"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/3131"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_guide_translations","page_url":{"en":"https:\/\/zotonic.com\/docs\/3131\/translating-interface-messages","x-default":"https:\/\/zotonic.com\/docs\/3131\/translating-interface-messages"},"preview_url":null,"resource":{"version":46,"pivot_location_lat":null,"title":"Translating interface messages","is_authoritative":true,"body":"<h1>Translating interface messages<\/h1>\n<p>Zotonic uses English source messages and gettext PO catalogs for interface\ntranslations. Language configuration and selection are provided by\n<a href=\"\/id\/doc_module_mod_translation\" class=\"doc-reference doc-reference-module\"><code>module#mod_translation<\/code><\/a>; Erlang code looks up messages through\n<a href=\"\/id\/doc_reference_z_trans\">the z_trans API<\/a>.<\/p>\n<h2>Mark messages for translation<\/h2>\n<p>In templates, use <code>{_ Save _}<\/code> for a simple label, <code>_&quot;Save&quot;<\/code> for a translated\nargument, or <a href=\"\/id\/doc_template_tag_tag_trans_ext\" class=\"doc-reference doc-reference-tag\"><code>tag#trans_ext<\/code><\/a> for a message with named placeholders:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-django\">{% trans &quot;Hello {name}&quot; name=user_name %}\n{% trans &quot;{n} item&quot; &quot;{n} items&quot; n=count %}\n<\/code><\/pre>\n<p>Keep source messages, message contexts, and both plural source strings literal\nso POT extraction can discover them. Preserve placeholders such as <code>{name}<\/code> and\n<code>{n}<\/code> exactly in every translation. Translate complete messages instead of\nconcatenating words: surrounding text can change word order and grammatical case.<\/p>\n<h2>Choose a message context<\/h2>\n<p>Prefer clear English source text, such as “First name” instead of “First”. When\nidentical English text needs different translations, use a stable semantic\n<code>msgctxt<\/code>, such as “action” versus “status” for “Open”. A message context is part\nof its gettext identity, not a language or an interpolation argument.<\/p>\n<p>Templates can set a default with <a href=\"\/id\/doc_template_tag_tag_msgctxt\" class=\"doc-reference doc-reference-tag\"><code>tag#msgctxt<\/code><\/a>, override it inside\n<a href=\"\/id\/doc_template_tag_tag_with_msgctxt\" class=\"doc-reference doc-reference-tag\"><code>tag#with_msgctxt<\/code><\/a>, or pass an explicit <code>msgctxt<\/code> to <a href=\"\/id\/doc_template_tag_tag_trans_ext\" class=\"doc-reference doc-reference-tag\"><code>tag#trans_ext<\/code><\/a>.\nA module&#39;s <code>-mod_msgctxt<\/code> attribute supplies a default for its templates.\nSee <a href=\"\/id\/doc_template_tag_tag_msgctxt\">template message contexts<\/a> for precedence and\nhow includes and inheritance behave. Erlang calls supply their context explicitly.<\/p>\n<p>Adding a context creates a new catalog entry. Template lookup tries contextual\nthen contextless translations in each language before moving to the next fallback\nlanguage. This keeps existing general translations usable during migration.\nThe final fallback is the English source.<\/p>\n<p>Contextual Erlang APIs are strict by default: they do not borrow a contextless\ntranslation, which might have a different meaning or grammatical case. The\n<a href=\"\/id\/doc_reference_z_trans#contextless-fallback\">Erlang API reference<\/a> describes\nhow to opt into the template policy.<\/p>\n<h2>Translate plural messages<\/h2>\n<p>The two source strings describe the English singular and plural. They do not\nlimit other languages to two forms: each PO catalog&#39;s <code>Plural-Forms<\/code> header\nspecifies its own rule and number of forms. The count <code>n<\/code> must be a non-negative\ninteger.<\/p>\n<p>Within one message context, use the same singular\/plural source pair consistently.\nPOT extraction reports conflicting singular\/plural definitions for the same\n<code>msgid<\/code>. Give messages with different meanings distinct source text or contexts.\nUse separate complete messages for “{n} hours ago” and “in {n} hours”.<\/p>\n<p>Template compilation precompiles the available translated patterns. At rendering\ntime the count selects a form using the selected catalog&#39;s rule. Empty forms and\ninvalid rule results are skipped during fallback; each fallback catalog uses its\nown rule. A catalog without a usable plural header supplies no plural translations.\nTranslation reloads invalidate compiled templates.<\/p>\n<p>See <a href=\"\/id\/doc_template_tag_tag_trans_ext\">the extended trans tag<\/a> for template syntax\nand <a href=\"\/id\/doc_reference_z_trans\">the Erlang translation API<\/a> for resolving plural\ntranslations and substituting arguments in Erlang.<\/p>","slug":"translating-interface-messages","is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"7cf37f59b86fc57be8ea76282109054e2b672b9bf1f3b40baee96b007b5ef4c5","is_featured":false,"content_group_id":{"id":2551,"is_a":["meta","content_group"],"name":"content_group_imported_docs","title":"Imported documentation","uri":"https:\/\/zotonic.com\/id\/content_group_imported_docs"},"category_id":{"id":317,"is_a":["meta","category"],"name":"developerguide","title":"Developer guide","uri":"https:\/\/zotonic.com\/id\/developerguide"},"doc_source_path":"doc\/guides\/translations.md","publication_start":"2026-11-02T11:36:17Z","github_url":"https:\/\/github.com\/zotonic\/zotonic\/blob\/master\/doc\/guides\/translations.md","pivot_location_lng":null,"doc_source_kind":"guide","name":"doc_guide_translations","is_unfindable":false,"is_published":true,"pivot_geocode":null,"created":"2026-11-02T11:36:17Z","uri":null,"doc_status":"current","is_dependent":false,"publication_end":"9999-06-01T00:00:00Z","modifier_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"privacy":0,"doc_source_commit":"f3c6a9a2dde17cb5b121dd3e3018d9ef4b1b3943\n","creator_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"modified":"2026-11-09T21:40:03Z","title_slug":"translating-interface-messages"},"uri":"https:\/\/zotonic.com\/id\/3131","uri_template":"https:\/\/zotonic.com\/id\/:id","version":46,"websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/3131"}},"status":"ok"}