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.