Why I think you can’t overwrite translations in SW6

I’m building a Shopware 6 shop and we implement a custom theme for our customer. To change the HTML structure we change templates, which is fine, but from time to time we only want to replace a translation, so I tried it.

Use your own translation and “just do it”

Shopware 6 has a good explanation how to implement your own translation. So we implemented it but having a string which is already part of the default translation doesn’t change on the frontend.

So either by adding an autoloader json or your own translation via services.xml, loading PHP class and JSON file

    "checkout": {
        "addressHeader": "Personal Details"

But this didn’t work, so we started digging.

Priority of the service

If you not use auto loaded translations, but load them via a php class and services.xml you can set a priority on the tag:

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
        <service id="YourPlugin\Resources\snippet\de_DE\YourPlugin_de_DE">
            <tag name="shopware.snippet.file" priority="100"/>

But this doesn’t fix our problem. Why is that?

Thank to Benjamin Eberlei I learned to check the Debugging Container, created by Symfony to check what is going on behind the scene, so we do it:


And here we find:

return (new \Shopware\Core\System\Snippet\Files\SnippetFileCollectionFactory(new RewindableGenerator(function () {
    yield 0 => (new \Prems\Plugin\PremsOnePageCheckout6\Resources\snippet\en_GB\SnippetFile_en_GB());
    yield 1 => (new \Prems\Plugin\PremsOnePageCheckout6\Resources\snippet\de_DE\SnippetFile_de_DE());
    yield 2 => ($this->services['SwagI18nItalian\\Resources\\app\\storefront\\snippet\\SnippetFile_it_IT'] ?? ($this->services['SwagI18nItalian\\Resources\\app\\storefront\\snippet\\SnippetFile_it_IT'] = new \SwagI18nItalian\Resources\app\storefront\snippet\SnippetFile_it_IT()));
    yield 3 => ($this->services['SwagI18nItalian\\Resources\\app\\core\\snippet\\SnippetFile_it_IT'] ?? ($this->services['SwagI18nItalian\\Resources\\app\\core\\snippet\\SnippetFile_it_IT'] = new \SwagI18nItalian\Resources\app\core\snippet\SnippetFile_it_IT()));
    yield 4 => (new \PayonePayment\Resources\translations\de_DE\SnippetFile_de_DE());
    yield 5 => (new \PayonePayment\Resources\translations\en_GB\SnippetFile_en_GB());
    yield 6 => ($this->services['SwagI18nFrench\\Resources\\app\\storefront\\snippet\\SnippetFile_fr_FR'] ?? ($this->services['SwagI18nFrench\\Resources\\app\\storefront\\snippet\\SnippetFile_fr_FR'] = new \SwagI18nFrench\Resources\app\storefront\snippet\SnippetFile_fr_FR()));
    yield 7 => ($this->services['SwagI18nFrench\\Resources\\app\\core\\snippet\\SnippetFile_fr_FR'] ?? ($this->services['SwagI18nFrench\\Resources\\app\\core\\snippet\\SnippetFile_fr_FR'] = new \SwagI18nFrench\Resources\app\core\snippet\SnippetFile_fr_FR()));
    yield 8 => (new \YourPlugin\Resources\snippet\de_DE\YourPlugin_de_DE());
    yield 9 => (new \YourPlugin\Resources\snippet\en_GB\YourPlugin_en_GB());
}, 10), new \Shopware\Core\System\Snippet\Files\SnippetFileLoader(($this->services['kernel'] ?? $this->get('kernel', 1)), ($this->services['Doctrine\\DBAL\\Connection'] ?? $this->getConnectionService()), new \Shopware\Core\System\Snippet\Files\AppSnippetFileLoader(\dirname(__DIR__, 4)), ($this->privates['Shopware\\Core\\Framework\\App\\ActiveAppsLoader'] ?? $this->getActiveAppsLoaderService()))))->createSnippetFileCollection();

And when I saw that I was super happy, because the loading order is fine and I didn’t understand what is going on. But after debugging it even further I understood. It is hidden at the end of the last line.


While the translations are loaded, all translations, which have a php file to load are cached and therefore loaded first. After that Shopware 6 is autoloading all so called \Shopware\Core\System\Snippet\Files\GenericSnippetFile in \Shopware\Core\System\Snippet\Files\SnippetFileLoader::loadPluginSnippets and therefore the default translation inside the core (which is autoloader) is loaded AFTER all our translations.

Order of translation load

There is no definable order on loading translations, except:

  • Everything with a php file first
  • every generic translation after

How to fix?

Shyim had the brilliant idea to easily fix it (Thanks!). so I implemented a PR for that:

Kommentar verfassen