ylliX - Online Advertising Network Fix Missing Mail Template Translations in Shopware 6

Fixing Missing Mail Template Translations in Shopware Migrations

When creating custom mail templates in Shopware via database migrations, you may notice that the template appears in the admin panel but without translations. This issue is usually caused by UUID handling and Doctrine DBAL insert types. In this tutorial, we’ll walk through the problem, show you the root cause, and provide the exact fix.

Introduction

In Shopware 6, adding custom mail templates is a common task when building plugins. Usually, you create a migration that inserts rows into the mail_template_type, mail_template, and their translation tables.

But what if the template appears in the admin panel with an empty name and missing translations? That’s exactly what happened to me, and the solution turned out to be less obvious than expected.

The Symptom

After running the migration, a new mail template type showed up in the Settings → Email Templates list.
However, instead of the expected names, it looked like this:

  • Template type exists

  • No translation text shown

  • Admin only displays empty labels

When I dumped the values being inserted, the language_id values already looked like proper binary UUIDs:

dump($langEnId);
// Output: b"\x01™ŠÃp..."

So the issue wasn’t about missing UUID conversion.

The Root Cause

Shopware stores all IDs (including language_id, mail_template_type_id, and mail_template_id) as BINARY(16).

When you call Doctrine DBAL’s $connection->insert(), the library automatically tries to convert parameters. If you pass a raw binary UUID without specifying the parameter type, DBAL often treats it as a string.

That means your mail_template_type_id in the translation table does not match byte-for-byte the ID in mail_template_type. From the database’s perspective, these are completely different values, so Shopware cannot join the records.

The Fix: Specify Binary Parameter Types

The fix is to explicitly tell DBAL which fields are binary. You can do that using the third parameter of insert().

Example for inserting the mail template type:

$connection->insert(
    MailTemplateTypeDefinition::ENTITY_NAME,
    [
        'id' => $typeId,
        'technical_name' => 'test-plugin.ticket.created',
        'available_entities' => json_encode(['salesChannel' => 'sales_channel'], JSON_THROW_ON_ERROR),
        'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
    ],
    [
        'id' => \Doctrine\DBAL\ParameterType::BINARY,
    ]
);

And when inserting translations:

$connection->insert(
    MailTemplateTypeTranslationDefinition::ENTITY_NAME,
    [
        'mail_template_type_id' => $typeId,
        'language_id' => $langEnId,
        'name' => 'Ticket created!',
        'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
    ],
    [
        'mail_template_type_id' => \Doctrine\DBAL\ParameterType::BINARY,
        'language_id' => \Doctrine\DBAL\ParameterType::BINARY,
    ]
);

Do the same for mail_template and mail_template_translation.

Why This Works

By explicitly setting ParameterType::BINARY, you ensure that DBAL inserts the values as raw 16-byte binary instead of misinterpreted strings. That way, all references between template types, templates, and translations line up correctly.

Once you rerun the migration, you’ll see your template and all translations properly displayed in the Shopware admin panel.

Conclusion

If your custom mail templates appear in the Shopware admin but without translations, the culprit is most likely improper handling of binary UUIDs.

Always remember:

  • Shopware IDs are stored as BINARY(16)

  • Doctrine DBAL needs explicit type hints when inserting binary values

  • Use ParameterType::BINARY for IDs and foreign keys

This little detail can save you hours of debugging when working with migrations in Shopware.

Leave a Reply