Core dna renders server-side sites with Smarty or with Twig, one engine per site, and treats the two as peers. This guide is the one place both are documented: what they share, each engine's language and the platform's additions and limits, the two side by side, and translation.
L1–L2
Unit 05 · Translation and internationalisation
Translating in Smarty
Translation and internationalisationPage 23 of 25Updated
The platform registers four ways to translate in Smarty: the |_ modifier, the same modifier with a domain, the {_} function and the {__} block. A worked example of each, which to reach for when, parameters, setting a default domain, escaping what goes in, and strings for JavaScript.
Who this is for. The developer translating a Smarty site's templates. It assumes the translation files and the lookup order on Interface strings. Afterwards you can translate any string in a Smarty template in the form that suits it, with parameters, and without the mistakes that print a key or open a hole.
The four forms
Form
Example
Reach for it when
The modifier with a domain
{'cart.button.add'|_:'site-shop'}
Almost always: short, and it names the domain
The modifier alone
{'cart.button.add'|_}
A template that has set its default domain once
The function
{_ key='cart.button.add' domain='site-shop'}
Attribute-style calls, such as a key built in a string
The block
{__ domain='site-shop'}cart.button.add{/__}
Rarely: its content is the key, not a passage of text
All four read the same files through the same lookup, and print the key itself when nothing is found.
The modifier
Template codeSmarty only
<button type="submit">{'cart.button.add'|_:'site-shop'}</button>
{* parameters: an array after the domain, keys without the percent signs *}
<p>{'cart.items'|_:'site-shop':['count' => $itemCount]}</p>
{* parameters alone: an array in place of the domain uses the default domain *}
<p>{'cart.items'|_:['count' => $itemCount]}</p>
{* into a variable, for an attribute or a later test *}
{$addLabel = 'cart.button.add'|_:'site-shop'}
<button aria-label="{$addLabel|escape}">+</button>
The first argument is the domain; the second, the parameters. When the first argument is an array, it is taken as the parameters and the default domain is used. In cart.items above, the target %count% artículos en tu carrito receives the value of $itemCount in place of %count%.
The function
Template codeSmarty only
{_ key='checkout.step.title' domain='site-shop' params=['step' => 2, 'total' => 4]}
{* a key built from a value *}
{_ key="product.badge.`$badge`" domain='site-shop'}
It needs key=: the short form {_ 'checkout.step.title'} does not compile, with too many shorthand attributes. A key built from a value, as in the second line, should take its value from a short fixed list, so every possible key exists in the file; otherwise a new value prints a raw key on the page.
The block's content is the key, looked up like any other; it is not a passage of English to be translated. It is there for templates that wrap a key in markup and want the key to read as content. Without domain= it always uses admin, even after the template has set another default domain, so give it a domain every time.
Setting a default domain
The translator is also available to templates as $__. A template that uses one domain throughout can set it once:
Template codeSmarty only
{$__->setDefaultDomain('site-shop')} {* prints nothing *}
<h2>{'cart.title'|_}</h2>
<p>{_ key='cart.empty'}</p>
The default lasts for the rest of the request, not only the template that set it: everything rendered afterwards, including components rendered with getHtml() and the platform's own partials, looks up in your domain unless it names one. The modifier without a domain and the function follow it; the block does not. Naming the domain in each call is the safer habit in shared partials.
Translations are printed as they are
Neither the translated text nor its parameters are escaped, which is what lets a target contain a link or emphasis. It also means a parameter holding anything a visitor typed must be escaped before it goes in:
|json_encode quotes and escapes each string for JavaScript, so a translation containing a quote cannot break the script. Pass only the strings the script needs: everything in the object is sent with every page.
Before you ship
Every lookup names its domain, or the template set one with setDefaultDomain().
Every key used exists in the site's file for each language the site supports, or falls back deliberately to English.
Every parameter that came from a visitor is escaped before it goes in.
The site's log shows no “Missing translation” warnings for its own domain after a full click-through.