Moxie.Build supports translating any user-facing text into multiple languages from a single database table. Procedures, templates, and database field metadata all share the same translation store, so a string written once is reusable everywhere.
The Content.Lang table is the canonical home for application translations. Each record has at minimum:
| Field | Purpose |
|---|---|
Alias |
Unique key, usually written as Prefix.tag (e.g. Site.btnSignIn). |
English |
The source-language value. |
French |
The translation for that language. |
Adding more languages. Any number of languages can be supported. For each new language:
Content.Lang table named after the language (e.g. Spanish, German, Japanese).Setup table so the runtime knows which languages are enabled and how to switch between them.Once both are in place, the same retrieval syntax (below) automatically picks the column matching the active language.
The active language is determined per request. The simplest way to switch is the URL parameter ?Cnw-Lang=French (or whichever language field name you've added). English is the default when no language is specified.
Translation aliases are usually written as Prefix.tag. The prefix namespaces all the tags used by one procedure or template, which keeps tag suffixes short and lets you reuse common names (btnSave, title, etc.) across different pages without collision.
Use LangWith at the top of your procedure (or before any block that needs translations):
LangWith "Profile"
From that line onward, every translation lookup in the procedure resolves against the Profile.* namespace.
You can call LangWith again later to switch namespaces mid-procedure — useful when you need to pull a string from a different prefix (such as a shared Api.* set of toast messages) without changing the procedure's main prefix permanently. Just restore the original prefix when you're done.
Templates use the parallel form:
< ? $$ Profile ?>
Place this near the top of the template, and subsequent translation tags (below) will resolve against Profile.*.
Once a prefix is active, there are four ways to look up a value, each suited to a different context.
Lang$Lang$ is the Top Query function for retrieving a single translated value. It takes one parameter, the tag suffix:
[New] welcome = Lang$ "welcomeTitle"
With LangWith "Profile" active, this returns the value of Profile.welcomeTitle in the active language.
Use Lang$ when you need the string as a value (assigning to a field, concatenating, passing to another command).
LangLang is the Work Query Function for resolving translations across many records at once. It takes four parameters: FieldBase, Destination, Source, IfLenFld.
Lang "Profile.", "Label", "tagSuffix", ""
Use Lang when you're operating on a multi-record work query and want each record's source field to be resolved through the language table efficiently in one pass.
HtmlBlock — {$suffix$}Inside an HtmlBlock or HtmlBlocks the {$suffix$} syntax resolves a translation against the procedure's active LangWith prefix:
HtmlBlock
< h1>{$welcomeTitle$}< /h1>
< p>{$welcomeDesc$}< /p>
End HtmlBlock
A note on the examples below: a single space has been inserted after the literal lessthan sign or [ in some examples (e.g. < ? … ?>, [ Language …], or HTML wrapper tags like < h1> and < /p>) to prevent the Moxie.Build template engine from interpreting them as live syntax on this page. The actual syntax has no such space.
You can also include a fully-qualified alias (with one or more dots) to reach a record from a different prefix:
< p>{$Api.linkCopied$}< /p>
< ? $ tag ?>Templates have two forms:
< ? $$ Profile ?>
< h1>< ? $ welcomeTitle ?>< /h1>
< p>< ? $ Api.linkCopied ?>< /p>
< ? $ suffix ?> (no dot) resolves against the template's active prefix set by < ? $$ Prefix ?>.< ? $ Full.Alias ?> (one or more dots) is fully qualified and bypasses the active prefix.Field labels, notes, and attribute placeholders in your table definitions can also be translated. In the field's .tab definition, write {$$} as a shorthand placeholder in the Label, Note, or Attr column:
FieldName {$$} {$$} [Placeholder] {$$}
The shorthand resolves to an alias built from the table name and field name:
| Where | Resolves to (for table example.table, field FieldName) |
|---|---|
| Label column | example.table.FieldName |
| Note column | example.table.FieldName.Note |
Attr column (e.g. inside [Placeholder]) |
example.table.FieldName.Attr |
For more complex cases where one cell needs to embed multiple translation references, use the named form {$customSuffix$} which resolves to example.table.customSuffix:
Status {$$} {$$} {$statusHelp$}
For short embedded text that doesn't warrant a Content.Lang record — particularly inline within field attributes or [!] dropdown labels — you can use inline language tags:
[ Language English]Sign In[/ Language][ Language French]Connexion[/ Language]
Only the segment matching the active language renders. Use these sparingly — Content.Lang records are usually preferable because they're discoverable and translatable in one place.
When a tag is looked up:
Content.Lang table.BuiltIn language table.Profile.welcomeTitle) rather than raising an error.This makes development friction-free — you can reference a tag before the record exists, see the alias appear in the rendered page, and add the record when you're ready. There's no error to catch.
Different languages render at different lengths. French and Spanish typically run 20–30% longer than the equivalent English; German can be longer still. When you write the source-language string, picture the longest likely translation:
| Where | Set prefix | Resolve a tag |
|---|---|---|
| Procedure (Top Query) | LangWith "Prefix" |
Lang$ "suffix" |
| Procedure (Work Query) | LangWith "Prefix" |
Lang "Base.", "Dest", "Source", "" |
| HtmlBlock / HtmlBlocks | Procedure's LangWith |
{$suffix$} or {$Full.Alias$} |
| Template | < ? $$ Prefix ?> |
< ? $ suffix ?> or < ? $ Full.Alias ?> |
| Schema field | Filename-derived | {$$} shorthand or {$customSuffix$} |
| Inline attribute text | — | [ Language X]...[/ Language] |