Guide 02 · Developer docs

Template languages: Smarty and Twig

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 04 · Comparing and porting

Porting a template set

Moving a site from Smarty to Twig, or back, ports every template and ships in one deploy, because the engine is one setting. A procedure for either direction: inventory the constructs, keep file names, handle escaping, then translation, then scope, then compare rendered pages.

Who this is for. The technical lead estimating or running a change of engine on an existing site, and the developers doing it. Afterwards you have an order of work that avoids the bugs ports usually ship with, and a way to estimate the job from the site's own templates rather than from a guess.

Two constraints that shape the job

  • It ships all at once. The engine is one setting for the whole site, Globals.template.engine, so there is no running half the templates in each. Port on a branch, deployed to a development environment whose .settings.json already names the target engine, and release the port and the setting together.
  • Nothing outside templates/ changes. Layouts, areas, components, content, URLs and the front-end build are engine-independent (What is identical in both). Template code kept in the admin is the exception: it is rendered by the site's engine too, so it is part of the port.

The procedure

  1. Inventory the constructs. Count each construct in the equivalence table across the template set, and list the template code in jobs, discount conditions, shipping methods and the search facets template. The counts are the estimate (below).
  2. Keep every file name. Layouts name their templates as plain paths, so rename a file and every record on that layout breaks. Change the contents, never the path. Templates with a -twig suffix are the platform's own and are not a pattern to copy.
  3. Escaping first. Carry every escape across exactly: |escape becomes |e and the reverse, with the same strategy. Add none and drop none because of the target engine: neither engine escapes on Core dna (Escaping). Remove |raw when porting to Smarty, which has no equivalent and needs none.
  4. Translation second. The translation files stay as they are. To Twig, every {'key'|_:'domain'}, {_ key=…} and {__}…{/__} becomes a call to one macro over __.translate(); to Smarty, the macro calls become the |_ modifier (Translating in Twig, Translating in Smarty).
  5. Scope third. Find every scope='parent', scope='root' and scope='global' and replace it with _templateContext, or the reverse. Then look at includes: a Smarty partial that relied on seeing its caller's variables needs them passed explicitly before you add only in Twig, and a Twig partial that relied on only may collide with its caller's variables in Smarty.
  6. The platform helpers. |format becomes |date with the site's format from _config; {json} becomes a map and |json_encode; {error} has no Twig equivalent; {break} and {continue} become a filtered list; foreach properties become loop.*; {strip} becomes whitespace modifiers. In the other direction, Smarty needs its ternary conditions in parentheses and has no ??.
  7. The platform's own screens. Moving to Twig, override every platform template the site uses that exists only in Smarty, such as users/login.html; otherwise it prints its Smarty source as text (Overriding a platform template). Moving to Smarty, a site's -twig overrides stop being used and need Smarty versions under the plain names.
  8. Verify by comparing rendered pages. Before the port, save the rendered HTML of a list of URLs covering every layout; after it, render the same URLs on the development environment and compare the text of each. A port that looks right can still have lost a heading level, doubled an escape or emptied a block. In Twig, an empty block where content was is the sign of a misspelt method.
Shell Counting the constructs in a Smarty template set
# templates by tag: includes, inheritance, captures, functions, strip, json
grep -rhoE '\{(include|extends|block|capture|function|strip|json|literal|foreach|section)\b' templates | sort | uniq -c

# assignments that cross templates
grep -rn "scope=" templates | wc -l

# translation calls, in the modifier form
grep -rhoE "\|_(:'[a-z-]+')?" templates | wc -l

# the platform's date modifier
grep -rho '|format' templates | wc -l

Estimating

A template set is a mix of constructs that port mechanically and constructs that need a decision. Count both:

  • Mechanical: output, modifiers and filters, conditions, loops, comments, inheritance tags. These change syntax and nothing else.
  • A decision each: every scope= assignment, every include that relies on shared scope, every {json} block, every {error}, every translation call, and every piece of template code in the admin. These take the longest per occurrence.

Port two or three representative templates first, including the base template and the busiest component, and time them. Multiply by the counts, add the verification pass over every layout, and add the admin's template code separately. That estimate comes from the site itself; a figure that does not is not worth quoting.

One template at a time is covered step by step in Port one template from Smarty to Twig.

Go deeper

Was this page helpful?

Your answer goes to the docs team.