Magento 2 Knockout Translation Bug

Many bugs have been submitted to the Magento 2 Github page, and whilst lots have been fixed, there are many waiting to be fixed and included in upcoming versions. None more so than the Magento 2 knockout translation bug.

This bug is fairly easy to replicate in a blank install of Magento 2, and can be reproduced on the current version, 2.2.1.

The store’s locale in this example is set to en_GB and when using a custom theme, attempts to translate You have no items in your shopping cart. to You have no items in your shopping basket..

This is entered within the en_GB.csv file in the theme’s i18n directory.


"You have no items in your shopping cart.", "You have no items in your shopping basket."

After refreshing the cache and rebuilding static content, the text is successfully changed within the cart page’s template file, but not within the minicart.

Magento 2 Knockout Translation Bug

This is due to the js-translation.json file within your theme’s directory in the pub/static directory not rebuilding properly.

The bug can be traced back to the vendor/magento/module-translation/Model/Json/PreProcessor.php file, within the process() method.


public function process(Chain $chain)
{
    if ($this->isDictionaryPath($chain->getTargetAssetPath())) {
        $context = $chain->getAsset()->getContext();

        $themePath = '*/*';
        $areaCode = \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE;

        if ($context instanceof FallbackContext) {
            $themePath = $context->getThemePath();
            $areaCode = $context->getAreaCode();
            $this->translate->setLocale($context->getLocale());
        }

        $area = $this->areaList->getArea($areaCode);
        $area->load(\Magento\Framework\App\Area::PART_TRANSLATE);

        $chain->setContent(json_encode($this->dataProvider->getData($themePath)));
        $chain->setContentType('json');
    }
}

Magento does not load the design part when using area->load().

This can be fixed by replacing the following lines.

$area = $this->areaList->getArea($areaCode);
$area->load(\Magento\Framework\App\Area::PART_TRANSLATE);

With the below.

$area = $this->areaList->getArea($areaCode);
$area->load(\Magento\Framework\App\Area::PART_DESIGN);
$area->load(\Magento\Framework\App\Area::PART_TRANSLATE);

To achieve the above modifications without directly modifying the code code, you can create a custom module and write a before plugin on the process() method.

Assuming you have already set up a registration.php and module.xml, proceed by defining the plugin in di.xml.


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Translation\Model\Json\PreProcessor">
        <plugin name="plugin_translate_json_preprocessor" type="[Vendor]\[Module]\Plugin\Translate\Json\PreProcessor" sortOrder="10" />
    </type>
</config>

Add the plugin class that will include the $area->load(\Magento\Framework\App\Area::PART_DESIGN); line.


<?php
namespace [Vendor]\[Module]\Plugin\Translate\Json;

use Magento\Framework\App\AreaList;
use Magento\Framework\TranslateInterface;
use Magento\Framework\View\Asset\File\FallbackContext;
use Magento\Framework\View\Asset\PreProcessor\Chain;
use Magento\Translation\Model\Js\Config;
use Magento\Translation\Model\Js\DataProviderInterface;

class PreProcessor
{
    /**
     * Js translation configuration
     *
     * @var Config
     */
    protected $config;

    /**
     * Translation data provider
     *
     * @var DataProviderInterface
     */
    protected $dataProvider;

    /**
     * @var AreaList
     */
    protected $areaList;

    /**
     * @var TranslateInterface
     */
    protected $translate;

    /**
     * PreProcessor constructor.
     *
     * @param Config $config
     * @param DataProviderInterface $dataProvider
     * @param AreaList $areaList
     * @param TranslateInterface $translate
     */
    public function __construct(
        Config $config,
        DataProviderInterface $dataProvider,
        AreaList $areaList,
        TranslateInterface $translate
    ) {
        $this->config = $config;
        $this->dataProvider = $dataProvider;
        $this->areaList = $areaList;
        $this->translate = $translate;
    }

    /**
     * Transform content and/or content type for the specified preprocessing chain object
     *
     * @param Chain $chain
     * @return void
     */
    public function beforeProcess(\Magento\Translation\Model\Json\PreProcessor $processor, Chain $chain)
    {
        if ($this->isDictionaryPath($chain->getTargetAssetPath())) {
            $context = $chain->getAsset()->getContext();
            $areaCode = \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE;

            if ($context instanceof FallbackContext) {
                $areaCode = $context->getAreaCode();
                $this->translate->setLocale($context->getLocale());
            }

            $area = $this->areaList->getArea($areaCode);
            $area->load(\Magento\Framework\App\Area::PART_DESIGN);
        }
    }

    /**
     * Is provided path the path to translation dictionary
     *
     * @param string $path
     * @return bool
     */
    protected function isDictionaryPath($path)
    {
        return (strpos($path, $this->config->getDictionaryFileName()) !== false);
    }
}

Note that the isDictionaryPath() method is also included here, as it is a protected method used in process().

After adding the above, delete the existing js-translation.json file in the pub/static directory, refresh the translation cache and redeploying static content, the next time you load the cart page, the minicart text will successfully translate.

Magento 2 Knockout Translation Bug

You should also notice that the js-translation.json file has rebuilt containing the original and translated text added in the theme’s i18n/en_GB.csv file.

Note: This article is based on Magento Open Source version 2.2.