symfony/translation · error · LogicException

Cannot add a catalogue for locale

Error message

Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s".

What it means

MessageCatalogue::addCatalogue merges another catalogue into this one, but only if both catalogues belong to the same locale. Throwing a LogicException when the incoming catalogue's locale differs prevents silently mixing translations of different languages into one catalogue.

Solutions

  1. Ensure the catalogue you add was created with the same locale as the target catalogue.
  2. Create a separate MessageCatalogue per locale and add each catalogue to its matching instance.
  3. Check the locale argument passed to the MessageCatalogue constructor; it is often hardcoded or swapped.
  4. If you need cross-locale behavior, use addFallbackCatalogue to link locales instead of merging.

Example fix

// before
$fr = new MessageCatalogue('fr', $messages);
$enCatalogue->addCatalogue($fr);

// after
$en = new MessageCatalogue('en', $messages);
$enCatalogue->addCatalogue($en);
Defensive patterns

Strategy: type-guard

Validate before calling

if ($other->getLocale() !== $catalogue->getLocale()) {
    throw new \LogicException('Refusing to merge catalogues of different locales');
}

Type guard

function canMerge(MessageCatalogue $into, MessageCatalogue $from): bool { return $into->getLocale() === $from->getLocale(); }

Try / catch

try {
    $catalogue->addCatalogue($other);
} catch (\LogicException $e) {
    // route $other to a catalogue of its own locale instead
}

Prevention

When it happens

Trigger: Calling $catalogue->addCatalogue($other) where $other->getLocale() !== $this->locale, e.g. adding a 'fr' catalogue into a 'en' catalogue.

Common situations: Building catalogues in a loop keyed by the wrong variable, merging cached catalogues from different locales, or constructing a catalogue with one locale and then loading a resource file for another.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of symfony/translation@ae9e8a51bc (2026-09-15). Data as JSON: /api/errors/8eecaece1e557da3. Report an issue: GitHub.

Appendix: source

Thrown at MessageCatalogue.php:144

    }

    public function add(array $messages, string $domain = 'messages'): void
    {
        $altDomain = str_ends_with($domain, self::INTL_DOMAIN_SUFFIX) ? substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)) : $domain.self::INTL_DOMAIN_SUFFIX;
        foreach ($messages as $id => $message) {
            unset($this->messages[$altDomain][$id]);
            $this->messages[$domain][$id] = $message;
        }

        if ([] === ($this->messages[$altDomain] ?? null)) {
            unset($this->messages[$altDomain]);
        }
    }

    public function addCatalogue(MessageCatalogueInterface $catalogue): void
    {
        if ($catalogue->getLocale() !== $this->locale) {
            throw new LogicException(\sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s".', $catalogue->getLocale(), $this->locale));
        }

        foreach ($catalogue->all() as $domain => $messages) {
            if ($intlMessages = $catalogue->all($domain.self::INTL_DOMAIN_SUFFIX)) {
                $this->add($intlMessages, $domain.self::INTL_DOMAIN_SUFFIX);
                $messages = array_diff_key($messages, $intlMessages);
            }
            $this->add($messages, $domain);
        }

        foreach ($catalogue->getResources() as $resource) {
            $this->addResource($resource);
        }

        if ($catalogue instanceof MetadataAwareInterface) {
            $metadata = $catalogue->getMetadata('', '');
            $this->addMetadata($metadata);
        }

View on GitHub (pinned to ae9e8a51bc)