Skip to main content
Version: 2026.1

Custom Translation Domains

Pimcore ships with two built-in translation domains: messages for shared/website translations and studio for Pimcore Studio UI translations. Bundles and application modules can register additional domains to keep their translations separate from the core domains.

How Translation Domains Work

Each translation domain maps to its own database table following the naming convention translations_{domain}. For example, the default messages domain stores its entries in translations_messages, while the studio domain uses translations_studio.

Pimcore defines the default domains in bundles/CoreBundle/config/pimcore/default.yaml:

pimcore:
translations:
domains:
- messages
- studio

When you register a custom domain, Pimcore validates it at runtime and makes it available through the standard Symfony Translator component. The domain also appears as a filter option in the Pimcore Studio translations view.

Registering a Custom Domain

Registering a custom domain requires two steps: declaring the domain in your bundle configuration and creating the corresponding database table.

Step 1: Declare the Domain in Configuration

Add your domain to pimcore.translations.domains in your bundle's config file. For example, the Portal Engine bundle registers its frontend domain in src/Resources/config/pimcore/config.yml:

pimcore:
translations:
domains:
- portal_engine_frontend

This tells Pimcore to recognize portal_engine_frontend as a valid translation domain.

Step 2: Create the Database Table

Your bundle must create the translations_{domain} table during installation. Use a Doctrine migration or your bundle's installer to set up the required schema. The table needs the following structure:

use Doctrine\DBAL\Schema\Schema;

public function up(Schema $schema): void
{
$domain = 'your_domain';

if ($schema->hasTable('translations_' . $domain)) {
return;
}

$table = $schema->createTable('translations_' . $domain);

$table->addColumn('key', 'string', ['length' => 190, 'notnull' => true]);
$table->addColumn('type', 'string', ['length' => 10]);
$table->addColumn('language', 'string', ['notnull' => true, 'length' => 10]);
$table->addColumn('text', 'text');
$table->addColumn('creationDate', 'integer', ['unsigned' => true, 'length' => 11]);
$table->addColumn('modificationDate', 'integer', ['unsigned' => true, 'length' => 11]);
$table->addColumn('userOwner', 'integer', ['unsigned' => true, 'length' => 11]);
$table->addColumn('userModification', 'integer', ['unsigned' => true, 'length' => 11]);

$table->addIndex(['language'], 'language');
$table->setPrimaryKey(['key', 'language']);
}

This pattern mirrors the approach used by TableInstaller::addTranslationDomainTable() in the Portal Engine bundle (available in the Enterprise edition).

Using Custom Domain Translations

Once registered, you can reference your custom domain in Twig templates and PHP code just like the built-in domains.

In Twig:

{{ 'welcome_message'|trans({}, 'your_domain') }}

In PHP:

$translatedValue = $translator->trans('welcome_message', [], 'your_domain');

The translator resolves the key against the translations_your_domain table for the current locale, following the same fallback logic as shared translations.