Translations
A Magento shop often runs in several languages. So strings in code are not printed directly: they are wrapped in the __() function, and the translation is kept in a separate file. Source strings are written in English by convention — they serve as keys.
Where translations live
A module's translations are in the i18n folder, one CSV file per language. The file name is a language and country code: de_DE.csv, fr_FR.csv. The file is created by hand.
app/code/Acme/Hello/
├── i18n/
│ └── de_DE.csv new file
└── view/frontend/templates/greeting.phtmlA translatable string
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<h2><?= $escaper->escapeHtml(__('Hello!')) ?></h2>The __() function looks up the translation of the string for the language of the current store view. If there is none, the source string is printed — "Hello!".
The translation file
"Hello!","Hallo!"
"Welcome to %1!","Willkommen bei %1!"
"Reviews","Bewertungen"This is the content of the app/code/Acme/Hello/i18n/de_DE.csv file. Every line has two quoted parts separated by a comma: the source phrase and the translation. The source phrase must match the __() argument to the last character.
Switching the language
The store view language is chosen in the admin panel: Stores → Configuration → General → General → Locale Options, the Locale field. The value can be set separately for each store view.
bin/magento cache:cleanAfter cleaning the cache the /hello page shows "Hallo!" on the German store view and "Hello!" on the English one.
Placeholders
The changing parts of a phrase are marked with %1, %2 and passed as the following arguments:
// app/code/Acme/Hello/Model/Greeter.php
public function greet(string $name): string
{
return (string) __('Hello, %1!', $name);
}In the translation file the placeholders can be reordered: "Hello, %1!","%1, hallo!". The function returns not a string but a phrase object — it turns into text when printed or cast to string.
Strings in XML
Labels from XML files are translated too — add them to the same CSV file:
| Where the string is | How it is marked |
|---|---|
system.xml | The translate="label" attribute on a section, a group and a field |
menu.xml, acl.xml | The title attribute is translated automatically |
| UI components | The translate="true" attribute on the label tag |
Strings in JavaScript
Scripts use the $t() function from the mage/translate module: $t('Hello!'). Magento gathers translations for it into a separate file together with styles and scripts, so static files must be rebuilt after a translation changes.
Collecting phrases
To avoid writing phrases out by hand, a command collects them:
bin/magento i18n:collect-phrases -o app/code/Acme/Hello/i18n/en_US.csv app/code/Acme/HelloIt scans the module code and creates a file where every phrase is repeated twice. Copy it under the name de_DE.csv and replace the second half of each line with the translation.
Where a translation comes from
| Source | Priority |
|---|---|
The i18n files of modules | Lowest |
| A language pack installed through Composer | Higher |
An i18n file in a theme: app/design/frontend/Acme/simple/i18n/de_DE.csv | Higher still |
| Translations saved in the database | Highest |
Ready-made core translations for many languages are installed as language packs.
Common mistake
A variable or a concatenated string is passed to __(): __('Hello, ' . $name) or __($message). The phrase collector does not find such a string, and a translation cannot be written for every name. Always write a complete constant string in __() and insert the changing parts with %1 placeholders.