diff --git a/src/Service/MessageCatalogueFilterer.php b/src/Service/MessageCatalogueFilterer.php new file mode 100644 index 0000000..f499487 --- /dev/null +++ b/src/Service/MessageCatalogueFilterer.php @@ -0,0 +1,37 @@ + + * + * For the full importright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Service; + +use Symfony\Component\Translation\MessageCatalogue; + +final class MessageCatalogueFilterer +{ + public function filterByDomains(MessageCatalogue $messageCatalogue, array $whitelistedDomains, array $blacklistedDomains): MessageCatalogue + { + $filteredMessageCatalogue = new MessageCatalogue($messageCatalogue->getLocale()); + + $translations = $messageCatalogue->all(); + foreach ($messageCatalogue->getDomains() as $domain) { + if (!empty($blacklistedDomains) && \in_array($domain, $blacklistedDomains, true)) { + continue; + } + + if (!empty($whitelistedDomains) && !\in_array($domain, $whitelistedDomains, true)) { + continue; + } + + $filteredMessageCatalogue->add($translations[$domain], $domain); + } + + return $filteredMessageCatalogue; + } +} diff --git a/src/Service/StorageImporter.php b/src/Service/StorageImporter.php new file mode 100644 index 0000000..8a20941 --- /dev/null +++ b/src/Service/StorageImporter.php @@ -0,0 +1,36 @@ + + * + * For the full importright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Service; + +use Symfony\Component\Translation\MessageCatalogue; +use Translation\Common\Storage\StorageInterface; + +final class StorageImporter +{ + /** + * This method import all messages from the source storage to the + * destination storage for given locales. + * + * * The source storage is not updated. + * * Translations already present in destination storage are overwritten. + * * Translations present in destination storage only are not deleted. + */ + public function import(StorageInterface $sourceStorage, StorageInterface $destinationStorage, array $locales): void + { + foreach ($locales as $locale) { + $messageCatalogue = new MessageCatalogue($locale); + + $sourceStorage->export($messageCatalogue, []); + $destinationStorage->import($messageCatalogue, []); + } + } +} diff --git a/src/Service/StorageMessageCatalogueExtractor.php b/src/Service/StorageMessageCatalogueExtractor.php new file mode 100644 index 0000000..8b205ca --- /dev/null +++ b/src/Service/StorageMessageCatalogueExtractor.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Service; + +use Symfony\Component\Translation\MessageCatalogue; +use Translation\Common\Storage\StorageInterface; + +final class StorageMessageCatalogueExtractor +{ + public function extract(StorageInterface $storage, array $locales): array + { + $catalogues = []; + + foreach ($locales as $locale) { + $catalogue = new MessageCatalogue($locale); + + $storage->export($catalogue, []); + + $catalogues[] = $catalogue; + } + + return $catalogues; + } +} diff --git a/src/Storage/ArrayStorage.php b/src/Storage/ArrayStorage.php new file mode 100644 index 0000000..9056966 --- /dev/null +++ b/src/Storage/ArrayStorage.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Storage; + +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; +use Translation\Common\Model\Message; +use Translation\Common\Model\MessageInterface; + +/** + * An in-memory storage. + */ +final class ArrayStorage implements StorageInterface +{ + /** + * @var MessageCatalogue[] + */ + private $catalogues; + + /** + * {@inheritdoc} + */ + public function get(string $locale, string $domain, string $key): ?MessageInterface + { + $translation = $this->getCatalogue($locale)->get($key, $domain); + + return new Message($key, $domain, $locale, $translation); + } + + /** + * {@inheritdoc} + */ + public function create(MessageInterface $message): void + { + $catalogue = $this->getCatalogue($message->getLocale()); + if (!$catalogue->defines($message->getKey(), $message->getDomain())) { + $catalogue->set($message->getKey(), $message->getTranslation(), $message->getDomain()); + } + } + + /** + * {@inheritdoc} + */ + public function update(MessageInterface $message): void + { + $catalogue = $this->getCatalogue($message->getLocale()); + $catalogue->set($message->getKey(), $message->getTranslation(), $message->getDomain()); + } + + /** + * {@inheritdoc} + */ + public function delete(string $locale, string $domain, string $key): void + { + $catalogue = $this->getCatalogue($locale); + $messages = $catalogue->all($domain); + unset($messages[$key]); + + $catalogue->replace($messages, $domain); + } + + /** + * {@inheritdoc} + */ + public function export(MessageCatalogueInterface $catalogue, array $options = []): void + { + $catalogue->addCatalogue($this->getCatalogue($catalogue->getLocale())); + } + + /** + * {@inheritdoc} + */ + public function import(MessageCatalogueInterface $catalogue, array $options = []): void + { + $this->getCatalogue($catalogue->getLocale())->addCatalogue($catalogue); + } + + private function getCatalogue(string $locale): MessageCatalogue + { + if (empty($this->catalogues[$locale])) { + $this->catalogues[$locale] = new MessageCatalogue($locale); + } + + return $this->catalogues[$locale]; + } +} diff --git a/src/Storage/ChainStorage.php b/src/Storage/ChainStorage.php index 16c754c..47efde2 100644 --- a/src/Storage/ChainStorage.php +++ b/src/Storage/ChainStorage.php @@ -19,6 +19,8 @@ */ class ChainStorage implements StorageInterface { + const DIRECTION_DOWN = 'down'; + private $storages = []; /** @@ -83,7 +85,12 @@ public function delete(string $locale, string $domain, string $key): void */ public function export(MessageCatalogueInterface $catalogue, array $options = []): void { - foreach ($this->storages as $storage) { + $storages = $this->storages; + if (isset($options['direction']) && self::DIRECTION_DOWN === $options['direction']) { + $storages = array_reverse($storages); + } + + foreach ($storages as $storage) { $storage->export($catalogue, $options); } } diff --git a/src/Storage/StorageInterface.php b/src/Storage/StorageInterface.php index f8b05e7..c25cadf 100644 --- a/src/Storage/StorageInterface.php +++ b/src/Storage/StorageInterface.php @@ -48,6 +48,9 @@ public function delete(string $locale, string $domain, string $key): void; /** * Get messages from the storage into the $catalogue. * + * This action should be considered as a "force merge". Existing messages + * in the storage will be overwritten but no message will be removed. + * * @var array a list of arbitrary options that could be used. The array SHOULD * use a format of array. * Example: ['foo' => ['bar', 'baz]] @@ -55,9 +58,10 @@ public function delete(string $locale, string $domain, string $key): void; public function export(MessageCatalogueInterface $catalogue, array $options = []): void; /** - * Populate the storage with all the messages in $catalogue. This action - * should be considered as a "force merge". Existing messages in the storage - * will be overwritten but no message will be removed. + * Populate the storage with all the messages in $catalogue. + * + * This action should be considered as a "force merge". Existing messages + * in the storage will be overwritten but no message will be removed. * * @var array a list of arbitrary options that could be used. The array SHOULD * use a format of array. diff --git a/tests/Unit/Service/MessageCatalogueFiltererTest.php b/tests/Unit/Service/MessageCatalogueFiltererTest.php new file mode 100644 index 0000000..462b626 --- /dev/null +++ b/tests/Unit/Service/MessageCatalogueFiltererTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Tests\Unit\Service; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\MessageCatalogue; +use Translation\Common\Service\MessageCatalogueFilterer; + +class MessageCatalogueFiltererTest extends TestCase +{ + public function testFilterWithoutWhitelistNorBlacklist() + { + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('foo', 'foo', 'domain_1'); + $messageCatalogue->set('bar', 'bar', 'domain_2'); + + $service = new MessageCatalogueFilterer(); + $filteredMessageCatalogue = $service->filterByDomains($messageCatalogue, [], []); + + $this->assertEquals($messageCatalogue, $filteredMessageCatalogue); + } + + public function testFilterWithWhitelistOnly() + { + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('foo', 'foo', 'domain_1'); + $messageCatalogue->set('bar', 'bar', 'domain_2'); + + $expectedMessageCatalogue = new MessageCatalogue('en'); + $expectedMessageCatalogue->set('foo', 'foo', 'domain_1'); + + $service = new MessageCatalogueFilterer(); + $filteredMessageCatalogue = $service->filterByDomains($messageCatalogue, ['domain_1'], []); + + $this->assertEquals($expectedMessageCatalogue, $filteredMessageCatalogue); + } + + public function testFilterWithBlacklistOnly() + { + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('foo', 'foo', 'domain_1'); + $messageCatalogue->set('bar', 'bar', 'domain_2'); + + $expectedMessageCatalogue = new MessageCatalogue('en'); + $expectedMessageCatalogue->set('foo', 'foo', 'domain_1'); + + $service = new MessageCatalogueFilterer(); + $filteredMessageCatalogue = $service->filterByDomains($messageCatalogue, [], ['domain_2']); + + $this->assertEquals($expectedMessageCatalogue, $filteredMessageCatalogue); + } +} diff --git a/tests/Unit/Service/StorageImporterTest.php b/tests/Unit/Service/StorageImporterTest.php new file mode 100644 index 0000000..b20fcfd --- /dev/null +++ b/tests/Unit/Service/StorageImporterTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Tests\Unit\Service; + +use PHPUnit\Framework\TestCase; +use Translation\Common\Model\Message; +use Translation\Common\Service\StorageImporter; +use Translation\Common\Storage\ArrayStorage; + +class StorageImporterTest extends TestCase +{ + public function testImport() + { + $sourceStorage = new ArrayStorage(); + $sourceStorage->create(new Message('common', 'messages', 'en', 'Translation from source storage')); + $sourceStorage->create(new Message('foo', 'messages', 'en', 'Only in source storage')); + $sourceStorage->create(new Message('baz', 'domain_1', 'en', 'Baz in domain 1')); + $destinationStorage = new ArrayStorage(); + $destinationStorage->create(new Message('common', 'messages', 'en', 'Translation from destination storage')); + $destinationStorage->create(new Message('bar', 'messages', 'en', 'Only in destination storage')); + $destinationStorage->create(new Message('baz', 'domain_2', 'en', 'Baz in domain 2')); + + $initialSourceStorage = clone $sourceStorage; + $expectedDestinationStorage = clone $destinationStorage; + $expectedDestinationStorage->update(new Message('common', 'messages', 'en', 'Translation from source storage')); + $expectedDestinationStorage->create(new Message('foo', 'messages', 'en', 'Only in source storage')); + $expectedDestinationStorage->create(new Message('baz', 'domain_1', 'en', 'Baz in domain 1')); + + $importer = new StorageImporter(); + $importer->import($sourceStorage, $destinationStorage, ['en']); + + $this->assertEquals($initialSourceStorage, $sourceStorage); + $this->assertEquals($expectedDestinationStorage, $destinationStorage); + } +} diff --git a/tests/Unit/Service/StorageMessageCatalogueExtractorTest.php b/tests/Unit/Service/StorageMessageCatalogueExtractorTest.php new file mode 100644 index 0000000..ac0c603 --- /dev/null +++ b/tests/Unit/Service/StorageMessageCatalogueExtractorTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\Common\Tests\Unit\Service; + +use PHPUnit\Framework\TestCase; +use Translation\Common\Model\Message; +use Translation\Common\Service\StorageMessageCatalogueExtractor; +use Translation\Common\Storage\ArrayStorage; + +class StorageMessageCatalogueExtractorTest extends TestCase +{ + public function testImport() + { + $storage = new ArrayStorage(); + $storage->create(new Message('foo', 'domain_1', 'en', 'Foo')); + $storage->create(new Message('bar', 'domain_2', 'en', 'Bar')); + $storage->create(new Message('baz', 'domain_3', 'en', 'Baz')); + $storage->create(new Message('foo', 'domain_1', 'fr', 'Foo')); + $storage->create(new Message('foo', 'domain_1', 'nl', 'Foo')); + + $service = new StorageMessageCatalogueExtractor(); + $catalogues = $service->extract($storage, ['en', 'fr']); + + $this->assertCount(2, $catalogues); + + $this->assertSame([ + 'domain_1' => ['foo' => 'Foo'], + 'domain_2' => ['bar' => 'Bar'], + 'domain_3' => ['baz' => 'Baz'], + ], $catalogues[0]->all()); + + $this->assertSame([ + 'domain_1' => ['foo' => 'Foo'], + ], $catalogues[1]->all()); + } +} diff --git a/tests/Unit/Storage/ArrayStorageTest.php b/tests/Unit/Storage/ArrayStorageTest.php new file mode 100644 index 0000000..091875d --- /dev/null +++ b/tests/Unit/Storage/ArrayStorageTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Translation\common\tests\Unit\Storage; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\MessageCatalogue; +use Translation\Common\Model\Message; +use Translation\Common\Storage\ArrayStorage; + +class ArrayStorageTest extends TestCase +{ + private $messages; + private $storage; + + public function setUp(): void + { + $this->messages = [ + 'messages_en_foo' => new Message('foo', 'messages', 'en', 'I am the "foo" translation for English in the "messages" domain.'), + 'messages_fr_foo' => new Message('foo', 'messages', 'fr', 'Je suis la traduction de la clé "foo" en français dans le domain "messages".'), + 'messages_en_bar' => new Message('bar', 'messages', 'en', 'I am the "bar" translation for English in the "messages" domain.'), + 'messages_fr_bar' => new Message('bar', 'messages', 'fr', 'Je suis la traduction de la clé "bar" en français dans le domain "messages".'), + 'validators_en_foo' => new Message('foo', 'validators', 'en', 'I am the "foo" translation for English in the "validators" domain.'), + 'validators_fr_foo' => new Message('foo', 'validators', 'fr', 'Je suis la traduction de la clé "foo" en français dans le domain "validators".'), + 'validators_en_bar' => new Message('bar', 'validators', 'en', 'I am the "bar" translation for English in the "validators" domain.'), + 'validators_fr_bar' => new Message('bar', 'validators', 'fr', 'Je suis la traduction de la clé "bar" en français dans le domain "validators".'), + ]; + + $this->storage = new ArrayStorage(); + foreach ($this->messages as $message) { + $this->storage->create($message); + } + } + + public function testDelete() + { + $this->assertEquals($this->messages['messages_en_foo'], $this->storage->get('en', 'messages', 'foo')); + $this->storage->delete('en', 'messages', 'foo'); + $this->assertEquals(new Message('foo', 'messages', 'en', 'foo'), $this->storage->get('en', 'messages', 'foo')); + } + + public function testUpdate() + { + $this->assertEquals($this->messages['messages_en_foo'], $this->storage->get('en', 'messages', 'foo')); + $updatedMessage = new Message('foo', 'messages', 'en', 'Updated translation'); + $this->assertEquals($this->messages['messages_en_foo'], $this->storage->get('en', 'messages', 'foo')); + } + + public function testExport() + { + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('foo', 'I will be overrided.', 'messages'); + + $this->storage->export($messageCatalogue); + + $this->assertEquals($this->messages['messages_en_foo'], $this->storage->get('en', 'messages', 'foo')); + } + + public function testImport() + { + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('foo', 'I will override the existing key.', 'messages'); + + $this->storage->import($messageCatalogue); + + $this->assertEquals(new Message('foo', 'messages', 'en', 'I will override the existing key.'), $this->storage->get('en', 'messages', 'foo')); + } +} diff --git a/tests/Unit/Storage/ChainStorageTest.php b/tests/Unit/Storage/ChainStorageTest.php index d7cc35c..24be646 100644 --- a/tests/Unit/Storage/ChainStorageTest.php +++ b/tests/Unit/Storage/ChainStorageTest.php @@ -12,8 +12,9 @@ namespace Translation\common\tests\Unit\Storage; use PHPUnit\Framework\TestCase; -use Symfony\Component\Translation\MessageCatalogueInterface; +use Symfony\Component\Translation\MessageCatalogue; use Translation\Common\Model\Message; +use Translation\Common\Storage\ArrayStorage; use Translation\Common\Storage\ChainStorage; use Translation\Common\Storage\StorageInterface; @@ -37,7 +38,7 @@ public function testGetWithMessageInFirstStorage() { $expectedMessage = new Message('PHP Translation IS awesome!'); - $this->childStorage1->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1)->willReturn($expectedMessage); + $this->childStorage1->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1)->willReturn($expectedMessage); $this->childStorage2->get('en', 'messages', 'php_translation_is_awesome')->shouldNotBeCalled(); $message = $this->storage->get('en', 'messages', 'php_translation_is_awesome'); @@ -48,8 +49,8 @@ public function testGetWithMessageInSecondStorage() { $expectedMessage = new Message('PHP Translation IS awesome!'); - $this->childStorage1->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1)->willReturn(null); - $this->childStorage2->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1)->willReturn($expectedMessage); + $this->childStorage1->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1)->willReturn(null); + $this->childStorage2->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1)->willReturn($expectedMessage); $message = $this->storage->get('en', 'messages', 'php_translation_is_awesome'); $this->assertSame($expectedMessage, $message); @@ -57,8 +58,8 @@ public function testGetWithMessageInSecondStorage() public function testGetWithMessageNotFound() { - $this->childStorage1->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1)->willReturn(null); - $this->childStorage2->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1)->willReturn(null); + $this->childStorage1->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1)->willReturn(null); + $this->childStorage2->get('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1)->willReturn(null); $message = $this->storage->get('en', 'messages', 'php_translation_is_awesome'); $this->assertNull($message); @@ -68,8 +69,8 @@ public function testCreateCallAllStorages() { $message = new Message('PHP Translation IS awesome!'); - $this->childStorage1->create($message)->shouldBeCalledtimes(1); - $this->childStorage2->create($message)->shouldBeCalledtimes(1); + $this->childStorage1->create($message)->shouldBeCalledTimes(1); + $this->childStorage2->create($message)->shouldBeCalledTimes(1); $this->storage->create($message); } @@ -78,35 +79,144 @@ public function testUpdateCallAllStorages() { $message = new Message('PHP Translation IS awesome!'); - $this->childStorage1->update($message)->shouldBeCalledtimes(1); - $this->childStorage2->update($message)->shouldBeCalledtimes(1); + $this->childStorage1->update($message)->shouldBeCalledTimes(1); + $this->childStorage2->update($message)->shouldBeCalledTimes(1); $this->storage->update($message); } public function testDeleteCallAllStorages() { - $this->childStorage1->delete('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1); - $this->childStorage2->delete('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledtimes(1); + $this->childStorage1->delete('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1); + $this->childStorage2->delete('en', 'messages', 'php_translation_is_awesome')->shouldBeCalledTimes(1); $this->storage->delete('en', 'messages', 'php_translation_is_awesome'); } - public function testExportCallOnlyTransferrableStorage() + public function testExport() { - $messageCatalogue = $this->prophesize(MessageCatalogueInterface::class)->reveal(); - - $this->childStorage2->export($messageCatalogue, [])->shouldBeCalledtimes(1); - - $this->storage->export($messageCatalogue, []); + $firstStorage = new ArrayStorage(); + $firstStorage->create(new Message('common', 'messages', 'en', 'Translation from first storage')); + $firstStorage->create(new Message('foo', 'messages', 'en', 'Only in first storage')); + $firstStorage->create(new Message('baz', 'domain_1', 'en', 'Baz in domain 1')); + $secondStorage = new ArrayStorage(); + $secondStorage->create(new Message('common', 'messages', 'en', 'Translation from second storage')); + $secondStorage->create(new Message('bar', 'messages', 'en', 'Only in second storage')); + $secondStorage->create(new Message('baz', 'domain_2', 'en', 'Baz in domain 2')); + + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('common', 'Translation from the existing catalogue.', 'messages'); + $messageCatalogue->set('baz', 'Baz in domain 3', 'domain_3'); + + $storage = new ChainStorage([$firstStorage, $secondStorage]); + $storage->export($messageCatalogue, []); + + $expectedMessages = [ + 'messages' => [ + 'common' => 'Translation from second storage', + 'foo' => 'Only in first storage', + 'bar' => 'Only in second storage', + ], + 'domain_3' => [ + 'baz' => 'Baz in domain 3', + ], + 'domain_1' => [ + 'baz' => 'Baz in domain 1', + ], + 'domain_2' => [ + 'baz' => 'Baz in domain 2', + ], + ]; + + $this->assertSame($expectedMessages, $messageCatalogue->all()); } - public function testImportCallOnlyTransferrableStorage() + public function testExportDown() { - $messageCatalogue = $this->prophesize(MessageCatalogueInterface::class)->reveal(); - - $this->childStorage2->import($messageCatalogue, [])->shouldBeCalledtimes(1); + $firstStorage = new ArrayStorage(); + $firstStorage->create(new Message('common', 'messages', 'en', 'Translation from first storage')); + $firstStorage->create(new Message('foo', 'messages', 'en', 'Only in first storage')); + $firstStorage->create(new Message('baz', 'domain_1', 'en', 'Baz in domain 1')); + $secondStorage = new ArrayStorage(); + $secondStorage->create(new Message('common', 'messages', 'en', 'Translation from second storage')); + $secondStorage->create(new Message('bar', 'messages', 'en', 'Only in second storage')); + $secondStorage->create(new Message('baz', 'domain_2', 'en', 'Baz in domain 2')); + + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('common', 'Translation from the existing catalogue.', 'messages'); + $messageCatalogue->set('baz', 'Baz in domain 3', 'domain_3'); + + $storage = new ChainStorage([$firstStorage, $secondStorage]); + $storage->export($messageCatalogue, ['direction' => ChainStorage::DIRECTION_DOWN]); + + $expectedMessages = [ + 'messages' => [ + 'common' => 'Translation from first storage', + 'bar' => 'Only in second storage', + 'foo' => 'Only in first storage', + ], + 'domain_3' => [ + 'baz' => 'Baz in domain 3', + ], + 'domain_2' => [ + 'baz' => 'Baz in domain 2', + ], + 'domain_1' => [ + 'baz' => 'Baz in domain 1', + ], + ]; + + $this->assertSame($expectedMessages, $messageCatalogue->all()); + } - $this->storage->import($messageCatalogue, []); + public function testImport() + { + $firstStorage = new ArrayStorage(); + $firstStorage->create(new Message('common', 'messages', 'en', 'Translation from first storage')); + $firstStorage->create(new Message('foo', 'messages', 'en', 'Only in first storage')); + $firstStorage->create(new Message('baz', 'domain_1', 'en', 'Baz in domain 1')); + $secondStorage = new ArrayStorage(); + $secondStorage->create(new Message('common', 'messages', 'en', 'Translation from second storage')); + $secondStorage->create(new Message('bar', 'messages', 'en', 'Only in second storage')); + $secondStorage->create(new Message('baz', 'domain_2', 'en', 'Baz in domain 2')); + + $messageCatalogue = new MessageCatalogue('en'); + $messageCatalogue->set('common', 'Translation from the existing catalogue.', 'messages'); + $messageCatalogue->set('baz', 'Baz in domain 3', 'domain_3'); + + $storage = new ChainStorage([$firstStorage, $secondStorage]); + $storage->import($messageCatalogue, []); + + $expectedMessagesInFirstStorage = [ + 'messages' => [ + 'common' => 'Translation from the existing catalogue.', + 'foo' => 'Only in first storage', + ], + 'domain_1' => [ + 'baz' => 'Baz in domain 1', + ], + 'domain_3' => [ + 'baz' => 'Baz in domain 3', + ], + ]; + + $firstStorage->export($messagesInFirstStorage = new MessageCatalogue('en'), []); + $this->assertSame($expectedMessagesInFirstStorage, $messagesInFirstStorage->all()); + + $expectedMessagesInSecondStorage = [ + 'messages' => [ + 'common' => 'Translation from the existing catalogue.', + 'bar' => 'Only in second storage', + ], + 'domain_2' => [ + 'baz' => 'Baz in domain 2', + ], + 'domain_3' => [ + 'baz' => 'Baz in domain 3', + ], + ]; + + $secondStorage->export($messagesInSecondStorage = new MessageCatalogue('en'), []); + $this->assertSame($expectedMessagesInSecondStorage, $messagesInSecondStorage->all()); } }