Translates the user-facing text stored on quests, leaderboards and tournaments.
The database stores one string per field — the source text, in English.
It never stores a translation. Quests and leaderboards used to keep a
per-locale copy in metadata["titles"][locale], which drifted exactly like
the per-locale theme files did: editing the title left 29 stale copies and
nothing said which were stale. mix gamend.content.migrate_metadata lifts
any of those into the PO files.
Translations come from the content domain, whose msgids
mix gamend.content.extract reads back out of the database. That covers
both origins:
- defined in code (a plugin's
after_startup, a seed) — extraction picks the row up, and a translator fills it in like any other string. A plugin cannot usedgettext_noopeven if it wanted to: it is a separate Mix project with no:gettextdependency, so the macro is not in scope; - typed in the admin UI — extractable too, but until someone translates it
dgettextfalls back to the stored string, so it renders in whatever language it was written in.
The one thing never to do is translate on the way in:
create_quest(title: gettext("Welcome aboard")) # freezes one localeThat stores whichever locale the server booted in, for every user. Store the source, translate on the way out — which is what this module is for.
Applied where a player-facing LiveView assigns its records. Admin pages deliberately do not use it: an admin editing a quest has to see the string that is actually stored, or they will "fix" a translated title and overwrite the source.
Placeholders
A repeat quest's title carries %{n} (see Gamend.Quests.resolve_counter/2)
and that has to survive until here, because the msgid is the string with
the placeholder in it. So the number is passed to Gettext as a binding and
interpolated into the translation. Substituting it earlier looked right
in English and quietly broke every other locale: "Treasures x 3" matches no
msgid, so the lookup missed and the card fell back to English — while the
bare msgid reaching dgettext/3 without bindings logged a
missing Gettext bindings: [:n] error on every anonymous page view.
Summary
Functions
Translates one stored string into the caller's locale, falling back to the string itself.
Translates :title and :description on a record, a list of records, or a
map of them. Anything else is returned untouched, so this is safe to apply
to a whole assign.
Functions
Translates one stored string into the caller's locale, falling back to the string itself.
bindings are interpolated into the translation. Pass them for any stored
string that contains a %{placeholder}; Gettext logs an error and leaves the
placeholder raw on the page when one is missing.
Translates :title and :description on a record, a list of records, or a
map of them. Anything else is returned untouched, so this is safe to apply
to a whole assign.