Here's what I've found, not sure if it will answer all of your questions though.
So the translation is something that's happening in the \Magento\Framework\Phrase
class (an object of this class is being returned in the __
function).
If you check \Magento\Framework\Phrase\README.md
, you can get interesting information regarding this class:
Class \Magento\Framework\Phrase
calls renderer to make the
translation of the text. Phrase
provides RendererInterface
and a
few renderers to support different kinds of needs of translation of
the text. Here are list of renderers in this library:
- Placeholder render - it replaces placeholders with parameters for
substitution. It is the default render if none is set for the Phrase.
- Translate render - it is a base renderer that implements text translations.
- Inline render - it adds inline translate part to text
translation and returns the strings by a template.
- Composite render - it can have several renderers, calls each renderer for processing the text. Array of renderer class names pass into composite render
constructor as a parameter.
So the translation is initiated via the _initTranslate
method of \Magento\Framework\App\Area.php
, here a renderer is set on the Phrase
class:
protected function _initTranslate()
{
$this->_translator->loadData(null, false);
\Magento\Framework\Phrase::setRenderer(
$this->_objectManager->get('Magento\Framework\Phrase\RendererInterface')
);
return $this;
}
Looking at \Magento\Translation\etc\di.xml
we can see the following preference is set:
<preference for="Magento\Framework\Phrase\RendererInterface" type="Magento\Framework\Phrase\Renderer\Composite" />
And later in the same file we find the declaration:
<type name="Magento\Framework\Phrase\Renderer\Composite">
<arguments>
<argument name="renderers" xsi:type="array">
<item name="translation" xsi:type="object">Magento\Framework\Phrase\Renderer\Translate</item>
<item name="placeholder" xsi:type="object">Magento\Framework\Phrase\Renderer\Placeholder</item>
<item name="inline" xsi:type="object">Magento\Framework\Phrase\Renderer\Inline</item>
</argument>
</arguments>
</type>
So looking at the render
function of this Composite
class we can see that it renders the source using each argument items render
function:
public function render(array $source, array $arguments = [])
{
$result = $source;
foreach ($this->_renderers as $render) {
$result[] = $render->render($result, $arguments);
}
return end($result);
}
When we check \Magento\Framework\Phrase\Renderer\Translate.php
(the first argument of the Composite
class) we can see that the render
function uses the TranslateInterface
class (corresponds to $this->_translator
in the code below) to get the corresponding translation:
public function render(array $source, array $arguments)
{
$text = end($source);
try {
$data = $this->translator->getData();
} catch (\Exception $e) {
$this->logger->critical($e->getMessage());
throw $e;
}
return array_key_exists($text, $data) ? $data[$text] : $text;
}
Remember the _initTranslate
method mentionned above ? Right before setting the \Magento\Framework\Phrase
renderer we have the following code:
$this->_translator->loadData(null, false);
Where $this->_translator
is a \Magento\Framework\TranslateInterface
Under app/etc/di.xml
we can find the following preference:
<preference for="Magento\Framework\TranslateInterface" type="Magento\Framework\Translate" />
Finally, we can find how each module translation is loaded in the loadData
method of \Magento\Framework\Translate
class:
public function loadData($area = null, $forceReload = false)
{
$this->setConfig(
['area' => isset($area) ? $area : $this->_appState->getAreaCode()]
);
if (!$forceReload) {
$this->_data = $this->_loadCache();
if ($this->_data !== false) {
return $this;
}
}
$this->_data = [];
$this->_loadModuleTranslation();
$this->_loadThemeTranslation();
$this->_loadPackTranslation();
$this->_loadDbTranslation();
$this->_saveCache();
return $this;
}
Then in _loadModuleTranslation
we get the current module and get the corresponding translations:
protected function _loadModuleTranslation()
{
$currentModule = $this->getControllerModuleName();
$allModulesExceptCurrent = array_diff($this->_moduleList->getNames(), [$currentModule]);
$this->loadModuleTranslationByModulesList($allModulesExceptCurrent);
$this->loadModuleTranslationByModulesList([$currentModule]);
return $this;
}
There's probably more to say about it but I reckon that summarizes (even if my post is super long) the translation process in Magento 2.