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 05 · Translation and internationalisation

Interface strings

The words a template prints, as opposed to the content editors write, come from XLIFF files looked up by domain and key, in both engines. Where the files live and how they are named, how the visitor's interface language is chosen, the order a key is looked up in, shared domains, missing keys, and the spelling of keys.

Who this is for. The developer making a site's templates translatable, on either engine. Afterwards you can lay out a site's translation files, predict which language a visitor gets and which file a string comes from, and read a printed key as the diagnosis it is. The syntax for each engine is on Translating in Smarty and Translating in Twig.

Where strings live

Interface strings are kept in XLIFF files, one per domain and language, named <domain>.<language>.xlf with a two-letter language code. The platform has its own in its translations/ folder: admin, common, users, products and one per module, in English and in most cases German. A site keeps its own in a translations/ folder at the root of the site folder, beside templates/, not inside it:

Shell
translations/
  site-shop.en.xlf
  site-shop.es.xlf
  site-shop.de.xlf
templates/
  …

A domain is a group of keys. Give the site a domain of its own, such as site-shop, so its keys cannot collide with the platform's, and choose it by naming it in every lookup. Inside the file, each <source> is a key and each <target> the text for that language. The format is XLIFF 2.0, as in the platform's own files:

HTML translations/site-shop.es.xlf
<?xml version="1.0"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en" trgLang="es">
	<file id="site-shop" original="site-shop.txt">
		<unit id="cart">
			<segment>
				<source>cart.button.add</source>
				<target>Añadir al carrito</target>
			</segment>
			<segment>
				<source>cart.items</source>
				<target>%count% artículos en tu carrito</target>
			</segment>
		</unit>
	</file>
</xliff>
  • Keys say where a string is used, in dotted lower case: cart.button.add, not the English sentence. A key that is a sentence has to change whenever the wording does.
  • Parameters are written %name% in the text and passed without the percent signs.
  • Whitespace is collapsed when a string is printed: line breaks and tabs become spaces, runs of spaces become one, and the ends are trimmed.
  • A site file with a platform domain's name, such as translations/products.en.xlf, overrides the platform's text for the keys it contains. That is how a site relabels a platform screen, “Width” as “Width (cm)”, without overriding the template.

Which interface language a visitor gets

The interface language is decided once per request, in this order:

  1. The cdna_locale cookie, if it holds a two-letter code such as es. Only front-end page requests read it.
  2. The DXP language setting (Globals.admin.defaultLocale), the admin's language, which is also the site's default language for translations. The admin offers English, German, or “(Browser language)”, the default.
  3. The visitor's browser language, the first language it asks for, reduced to two letters.

The page being viewed plays no part. A Spanish page is Spanish because it is the Spanish record (Translated content and the language switcher); its buttons are Spanish only if the visitor's interface language is. The platform reads cdna_locale but never writes it, so a site that lets visitors choose sets the cookie itself, usually from its language switcher.

One consequence for caching: a page rendered for a cdna_locale that differs from the default is not stored in the shared page cache, because the cache does not vary on cookies. Visitors who choose a language other than the default therefore get uncached pages.

How a key is looked up

For a visitor whose interface language is Spanish, on a site whose DXP language is English, cart.button.add in site-shop is looked for in this order, and the first file that has the key wins:

StepFile
1The site's translations/site-shop.es.xlf
2The platform's translations/site-shop.es.xlf
3The site's and the platform's file for the DXP language, when it is set and differs: site-shop.en.xlf
4The site's and the platform's English file, the last resort
5None has it: the key itself is printed

A key printed on the page, cart.button.add where a label should be, therefore always means one of three things: the key is missing from every file in the chain, it is spelt differently in the template and the file, or the lookup named the wrong domain. Outside development environments the platform logs each missing key as a warning with its domain, so the site's log lists them.

A lookup that names no domain uses the current default domain, which is admin, the admin interface's, unless a template has changed it. Name your domain in every call, or set it once per template (in Smarty, in Twig).

Reusing the platform's shared domains

Some platform domains exist to be shared: admin holds generic interface labels and messages, common generic labels, validation messages and address fields, inputs form input labels, and mailer email salutations. Borrowing a key from one of them saves translating a generic word twice:

Template code
{'address.city'|_:'common'}           {* a generic label from a shared domain *}
{'cart.button.add'|_:'site-shop'}     {* everything specific to the site, from its own domain *}
{{ __.translate('address.city', {}, 'common') }}           {# a generic label from a shared domain #}
{{ __.translate('cart.button.add', {}, 'site-shop') }}     {# everything specific to the site #}

The rule that goes with it, which the platform applies to its own modules: borrow only from the shared domains. Do not take a key from another module's domain, such as products or carts, because that module may rename or remove it, and your label changes or breaks without anyone touching your templates. The platform's files exist in English and German, so a key borrowed for any other language needs that language in the site's own file of the same domain.

US English in keys and identifiers

The platform's keys, identifiers and source texts use US English, and so should yours, so that a key you guess matches one that exists: color, center, license, catalog, favorite, organization, customize, authorize, behavior, fulfillment, enrollment. The same goes for template and area names and other identifiers in code. The text in your own targets is a different matter: it follows the site's language and spelling, so a British site's English file says “colour” under the key product.color.

What is not translated this way

  • Content. Pages, posts and other records are translated as linked records, not through these files: Translated content and the language switcher.
  • Messages the platform returns from an API call, such as a cart error, are translated during that call, which does not read cdna_locale, so they arrive in the DXP or browser language. Show your own translated text for those cases rather than the platform's message.
Go deeper

Was this page helpful?

Your answer goes to the docs team.