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
- Ensure the catalogue you add was created with the same locale as the target catalogue.
- Create a separate MessageCatalogue per locale and add each catalogue to its matching instance.
- Check the locale argument passed to the MessageCatalogue constructor; it is often hardcoded or swapped.
- 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
- Key catalogues by locale in a map and always merge into the matching entry.
- Pass the same locale variable to the catalogue constructor and its resources.
- Use addFallbackCatalogue for cross-locale relations, not addCatalogue.
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
- Circular reference detected when adding a fallback…
- The Translator does not support the following options
- The file dumper needs a path option.
- Unable to create directory
- No support implemented for dumping XLIFF version
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)