Translations
A shop can run in several languages, so interface strings are not written in code as is. They are wrapped in a translation function: it looks up the translation for the current language and, if there is none, returns the original string.
In PHP
In the module class strings are translated with the trans() method:
// modules/acmehello/acmehello.php
$title = $this->trans('Reviews', [], 'Modules.Acmehello.Shop');The first argument is the source string, the second is the replacements, the third is the translation domain. While there is no translation, the method returns the string "Reviews" itself.
The domain
A domain is the name of a group of strings. The engine uses it to tell which module and which part of the shop a string belongs to.
| Domain | What for |
|---|---|
Modules.Acmehello.Shop | Module strings in the front office |
Modules.Acmehello.Admin | Module strings in the back office |
Admin.Actions, Admin.Global | Ready core strings: "Save", "Yes", "No" |
Shop.Theme.Actions | Ready theme strings: "Add to cart", "Continue" |
A module domain is built like this: Modules, a dot, the module name with a capital first letter and no underscores, a dot, Shop or Admin. Strings from core domains already have translations in all languages.
In a template
In Smarty templates the l function does the same job:
{* modules/acmehello/views/templates/hook/product-reviews.tpl *}
<section class="acme-reviews">
<h3>{l s='Reviews' d='Modules.Acmehello.Shop'}</h3>
{if $reviews}
<ul>
{foreach from=$reviews item=review}
<li>{$review.author}: {$review.rating}</li>
{/foreach}
</ul>
{else}
<p>{l s='No reviews yet.' d='Modules.Acmehello.Shop'}</p>
{/if}
</section>The s attribute is the string, d is the domain.
Replacements
The variable part of a string is not concatenated but substituted: word order differs between languages.
$text = $this->trans('Reviews: %count%', ['%count%' => 12], 'Modules.Acmehello.Shop');{l s='Hello, %name%!' sprintf=['%name%' => $customer.firstname] d='Modules.Acmehello.Shop'}The translator sees the whole string with the %name% placeholder and puts it where it belongs.
Enabling it in the module
For the back office to show the module's strings on the translations page, add a method to the main class:
// modules/acmehello/acmehello.php
public function isUsingNewTranslationSystem(): bool
{
return true;
}Where strings are translated
- Open "International → Translations" in the back office.
- In the "Modify translations" block choose the type "Installed modules translations", the
acmehellomodule and a language. - Enter a translation next to each string and save.
Open the storefront in that language: the heading of the reviews block changes to the translation. Translations are saved in the shop database.
Which language to use for source strings
Modules distributed to different shops write source strings in English: English is the default language of the translation catalogue, and a shop without a translation shows the English text.
In controllers
Module controllers have the same method: $this->trans('Thank you for your review!', [], 'Modules.Acmehello.Shop'). Back office Twig templates use a filter: {{ 'Reviews'|trans({}, 'Modules.Acmehello.Admin') }}.
Common mistake
A variable is passed to trans(): $this->trans($message, [], 'Modules.Acmehello.Shop'). The translations page finds strings by reading the source code and cannot read a variable — the string will not be in the list. Always write the string itself in quotes as the first argument and pass variable parts as replacements.