PKK]Z0mTargetOperation.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Target operation between two catalogues: * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target} * all = intersection ∪ (target ∖ intersection) = target * new = all ∖ source = {x: x ∈ target ∧ x ∉ source} * obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target} * Basically, the result contains messages from the target catalogue. * * @author Michael Lee */ class TargetOperation extends AbstractOperation { /** * @return void */ protected function processDomain(string $domain) { $this->messages[$domain] = [ 'all' => [], 'new' => [], 'obsolete' => [], ]; $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; foreach ($this->target->getCatalogueMetadata('', $domain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $domain)) { $this->result->setCatalogueMetadata($key, $value, $domain); } } foreach ($this->target->getCatalogueMetadata('', $intlDomain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $intlDomain)) { $this->result->setCatalogueMetadata($key, $value, $intlDomain); } } // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``, // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} // // For 'new' messages, the code can't be simplified as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));`` // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback} // // For 'obsolete' messages, the code can't be simplified as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))`` // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} foreach ($this->source->all($domain) as $id => $message) { if ($this->target->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } else { $this->messages[$domain]['obsolete'][$id] = $message; } } foreach ($this->target->all($domain) as $id => $message) { if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } } } } PKK]Z{AbstractOperation.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Base catalogues binary operation class. * * A catalogue binary operation performs operation on * source (the left argument) and target (the right argument) catalogues. * * @author Jean-François Simon */ abstract class AbstractOperation implements OperationInterface { public const OBSOLETE_BATCH = 'obsolete'; public const NEW_BATCH = 'new'; public const ALL_BATCH = 'all'; protected $source; protected $target; protected $result; /** * @var array|null The domains affected by this operation */ private $domains; /** * This array stores 'all', 'new' and 'obsolete' messages for all valid domains. * * The data structure of this array is as follows: * * [ * 'domain 1' => [ * 'all' => [...], * 'new' => [...], * 'obsolete' => [...] * ], * 'domain 2' => [ * 'all' => [...], * 'new' => [...], * 'obsolete' => [...] * ], * ... * ] * * @var array The array that stores 'all', 'new' and 'obsolete' messages */ protected $messages; /** * @throws LogicException */ public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target) { if ($source->getLocale() !== $target->getLocale()) { throw new LogicException('Operated catalogues must belong to the same locale.'); } $this->source = $source; $this->target = $target; $this->result = new MessageCatalogue($source->getLocale()); $this->messages = []; } public function getDomains(): array { if (null === $this->domains) { $domains = []; foreach ([$this->source, $this->target] as $catalogue) { foreach ($catalogue->getDomains() as $domain) { $domains[$domain] = $domain; if ($catalogue->all($domainIcu = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX)) { $domains[$domainIcu] = $domainIcu; } } } $this->domains = array_values($domains); } return $this->domains; } public function getMessages(string $domain): array { if (!\in_array($domain, $this->getDomains())) { throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } if (!isset($this->messages[$domain][self::ALL_BATCH])) { $this->processDomain($domain); } return $this->messages[$domain][self::ALL_BATCH]; } public function getNewMessages(string $domain): array { if (!\in_array($domain, $this->getDomains())) { throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } if (!isset($this->messages[$domain][self::NEW_BATCH])) { $this->processDomain($domain); } return $this->messages[$domain][self::NEW_BATCH]; } public function getObsoleteMessages(string $domain): array { if (!\in_array($domain, $this->getDomains())) { throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } if (!isset($this->messages[$domain][self::OBSOLETE_BATCH])) { $this->processDomain($domain); } return $this->messages[$domain][self::OBSOLETE_BATCH]; } public function getResult(): MessageCatalogueInterface { foreach ($this->getDomains() as $domain) { if (!isset($this->messages[$domain])) { $this->processDomain($domain); } } return $this->result; } /** * @param self::*_BATCH $batch */ public function moveMessagesToIntlDomainsIfPossible(string $batch = self::ALL_BATCH): void { // If MessageFormatter class does not exists, intl domains are not supported. if (!class_exists(\MessageFormatter::class)) { return; } foreach ($this->getDomains() as $domain) { $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; $messages = match ($batch) { self::OBSOLETE_BATCH => $this->getObsoleteMessages($domain), self::NEW_BATCH => $this->getNewMessages($domain), self::ALL_BATCH => $this->getMessages($domain), default => throw new \InvalidArgumentException(sprintf('$batch argument must be one of ["%s", "%s", "%s"].', self::ALL_BATCH, self::NEW_BATCH, self::OBSOLETE_BATCH)), }; if (!$messages || (!$this->source->all($intlDomain) && $this->source->all($domain))) { continue; } $result = $this->getResult(); $allIntlMessages = $result->all($intlDomain); $currentMessages = array_diff_key($messages, $result->all($domain)); $result->replace($currentMessages, $domain); $result->replace($allIntlMessages + $messages, $intlDomain); } } /** * Performs operation on source and target catalogues for the given domain and * stores the results. * * @param string $domain The domain which the operation will be performed for * * @return void */ abstract protected function processDomain(string $domain); } PKK]Zk* MergeOperation.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Merge operation between two catalogues as follows: * all = source ∪ target = {x: x ∈ source ∨ x ∈ target} * new = all ∖ source = {x: x ∈ target ∧ x ∉ source} * obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅ * Basically, the result contains messages from both catalogues. * * @author Jean-François Simon */ class MergeOperation extends AbstractOperation { /** * @return void */ protected function processDomain(string $domain) { $this->messages[$domain] = [ 'all' => [], 'new' => [], 'obsolete' => [], ]; $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; foreach ($this->target->getCatalogueMetadata('', $domain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $domain)) { $this->result->setCatalogueMetadata($key, $value, $domain); } } foreach ($this->target->getCatalogueMetadata('', $intlDomain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $intlDomain)) { $this->result->setCatalogueMetadata($key, $value, $intlDomain); } } foreach ($this->source->all($domain) as $id => $message) { $this->messages[$domain]['all'][$id] = $message; $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } foreach ($this->target->all($domain) as $id => $message) { if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } } } } PKK]ZЪAAOperationInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Represents an operation on catalogue(s). * * An instance of this interface performs an operation on one or more catalogues and * stores intermediate and final results of the operation. * * The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and * the following results are stored: * * Messages: also called 'all', are valid messages for the given domain after the operation is performed. * * New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}). * * Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}). * * Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'. * * @author Jean-François Simon */ interface OperationInterface { /** * Returns domains affected by operation. */ public function getDomains(): array; /** * Returns all valid messages ('all') after operation. */ public function getMessages(string $domain): array; /** * Returns new messages ('new') after operation. */ public function getNewMessages(string $domain): array; /** * Returns obsolete messages ('obsolete') after operation. */ public function getObsoleteMessages(string $domain): array; /** * Returns resulting catalogue ('result'). */ public function getResult(): MessageCatalogueInterface; } PKK]Z0mTargetOperation.phpnuW+APKK]Z{AbstractOperation.phpnuW+APKK]Zk* &MergeOperation.phpnuW+APKK]ZЪAA1OperationInterface.phpnuW+APKP$9