The problem

My Magento 2 (v2.4.5-p5) admin is unable to display the entries in the “Layout” dropdown with their correct translations, despite the correct translations being present.

The layouts are defined in vendor/magento/module-theme/view/frontend/layouts.xml and additionally in vendor/magento/module-page-builder/view/frontend/layouts.xml, when you are using the PageBuilder module.

<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page_layouts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/View/PageLayout/etc/layouts.xsd">
    <layout id="1column">
        <label translate="true">1 column</label>
    </layout>
    <layout id="2columns-left">
        <label translate="true">2 columns with left bar</label>
    </layout>
    <layout id="2columns-right">
        <label translate="true">2 columns with right bar</label>
    </layout>
    <layout id="3columns">
        <label translate="true">3 columns</label>
    </layout>
</page_layouts>
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page_layouts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/PageLayout/etc/layouts.xsd">
    <layout id="cms-full-width">
        <label translate="true">Page -- Full Width</label>
    </layout>
    <layout id="category-full-width">
        <label translate="true">Category -- Full Width</label>
    </layout>
    <layout id="product-full-width">
        <label translate="true">Product -- Full Width</label>
    </layout>
</page_layouts>

After tracking down the values from KnockoutJS downwards using xdebug, I came across the culprit: The PageLayout Config.php file of the Magento framework. So as deep down in the core as it basically gets.

The method in question is called _extractData(). This method is supplied with the layout files XML DOM and returns the values (the names) of all the specified layouts. However, there is one problem with this logic: It completely ignores the translate="true" flag for each label tag.

/**
    * Extract configuration data from the DOM structure
    *
    * @param \DOMDocument $dom
    * @return array
    */
protected function _extractData(\DOMDocument $dom)
{
    $result = [];

    /** @var \DOMElement $layout */
    foreach ($dom->getElementsByTagName('layout') as $layout) {
        $result[$layout->getAttribute('id')] = $layout->nodeValue !== null ? trim($layout->nodeValue) : '';
    }
    return $result;
}

The solution

To fix this behaviour, you simply have to override the _extractData() method with a corrected version, which respects the translation.

Set up your own custom module with the following structure:

app/
└── code/
    └── <YOUR-VENDOR-NAME>/
        └── <YOUR-MODULE-NAME>/
            ├── registration.php
            ├── etc/
            │   ├── module.xml
            │   └── di.xml
            └── Overrides/
                └── PageLayoutConfig.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    "<YOUR-VENDOR-NAME>_<YOUR-MODULE-NAME>",
    __DIR__
);
<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="<YOUR-VENDOR-NAME>_<YOUR-MODULE-NAME>" setup_version="1.0.0">
    </module>
</config>
<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Framework\View\PageLayout\Config" type="<YOUR-VENDOR-NAME>\<YOUR-MODULE-NAME>\Overrides\PageLayoutConfig" />
</config>
<?php
namespace <YOUR-VENDOR-NAME>\<YOUR-MODULE-NAME>\Overrides;

class PageLayoutConfig extends \Magento\Framework\View\PageLayout\Config {
    protected function _extractData(\DOMDocument $dom) {
        $result = [];
        foreach ($dom->getElementsByTagName('layout') as $layout) {
            $label = trim($layout->nodeValue);
            if ('true' === (string)simplexml_import_dom($layout)->{'label'}['translate']) {
                $label = __($label);
            }
            $result[$layout->getAttribute('id')] = $label;
        }
        return $result;
    }
}

After you deployed your module to your Magento installation, make sure to activate it.

php bin/magento module:enable <YOUR-VENDOR-NAME>_<YOUR-MODULE-NAME>
php bin/magento setup:upgrade

Refresh your page and there you have it: The translations are working now.

You’re welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like