diff --git a/.gitignore b/.gitignore index fa0fdd7a..b816031e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.php_cs.cache node_modules/ yarn.lock +/var diff --git a/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php b/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php index f67cc30f..0da004ce 100644 --- a/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php +++ b/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php @@ -184,7 +184,10 @@ private function prependJMSTranslation(ContainerBuilder $container): void 'output_dir' => __DIR__ . '/../Resources/translations/', 'output_format' => 'xliff', 'excluded_dirs' => ['Behat', 'Tests', 'node_modules'], - 'extractors' => [], + 'extractors' => [ + 'ibexa.translation_extractor.field_type.ezrichtext.custom_tags', + 'ibexa.translation_extractor.field_type.ezrichtext.custom_tags.choices', + ], ], ], ]); diff --git a/src/bundle/Resources/config/translation.yaml b/src/bundle/Resources/config/translation.yaml index 60afbec6..42c800cf 100644 --- a/src/bundle/Resources/config/translation.yaml +++ b/src/bundle/Resources/config/translation.yaml @@ -5,7 +5,26 @@ services: public: false Ibexa\FieldTypeRichText\Translation\Extractor\OnlineEditorCustomAttributesExtractor: + deprecated: 'Since ibexa/fieldtype-richtext 4.6.7 the "%service_id%" service is deprecated, will be removed in 5.0.0' arguments: $siteAccessList: '%ibexa.site_access.list%' tags: - { name: jms_translation.extractor, alias: ez_online_editor_attributes } + + Ibexa\FieldTypeRichText\Translation\Extractor\CustomTagExtractor: + arguments: + $customTags: '%ibexa.field_type.richtext.custom_tags%' + $domain: '%ibexa.field_type.richtext.custom_tags.translation_domain%' + $allowlist: ['ezyoutube', 'eztwitter', 'ezfacebook'] + tags: + - name: jms_translation.extractor + alias: ibexa.translation_extractor.field_type.ezrichtext.custom_tags + + Ibexa\FieldTypeRichText\Translation\Extractor\ChoiceAttributeExtractor: + arguments: + $customTags: '%ibexa.field_type.richtext.custom_tags%' + $domain: '%ibexa.field_type.richtext.custom_tags.translation_domain%' + $allowlist: ['ezyoutube', 'eztwitter', 'ezfacebook'] + tags: + - name: jms_translation.extractor + alias: ibexa.translation_extractor.field_type.ezrichtext.custom_tags.choices diff --git a/src/bundle/Resources/translations/custom_tags.en.xliff b/src/bundle/Resources/translations/custom_tags.en.xliff new file mode 100644 index 00000000..59b3afc5 --- /dev/null +++ b/src/bundle/Resources/translations/custom_tags.en.xliff @@ -0,0 +1,131 @@ + + + +
+ + The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. +
+ + + Post URL + Post URL + key: ezrichtext.custom_tags.ezfacebook.attributes.post_url.label + + + Width + Width + key: ezrichtext.custom_tags.ezfacebook.attributes.width.label + + + Embed Facebook post + Embed Facebook post + key: ezrichtext.custom_tags.ezfacebook.description + + + Facebook + Facebook + key: ezrichtext.custom_tags.ezfacebook.label + + + Hidden + Hidden + key: ezrichtext.custom_tags.eztwitter.attributes.cards.choice.hidden.label + + + Cards + Cards + key: ezrichtext.custom_tags.eztwitter.attributes.cards.label + + + None + None + key: ezrichtext.custom_tags.eztwitter.attributes.conversation.choice.none.label + + + Conversation + Conversation + key: ezrichtext.custom_tags.eztwitter.attributes.conversation.label + + + Don't target + Don't target + key: ezrichtext.custom_tags.eztwitter.attributes.dnt.label + + + Language code + Language code + key: ezrichtext.custom_tags.eztwitter.attributes.lang.label + + + Link color + Link color + key: ezrichtext.custom_tags.eztwitter.attributes.link_color.label + + + Dark + Dark + key: ezrichtext.custom_tags.eztwitter.attributes.theme.choice.dark.label + + + Light + Light + key: ezrichtext.custom_tags.eztwitter.attributes.theme.choice.light.label + + + Theme + Theme + key: ezrichtext.custom_tags.eztwitter.attributes.theme.label + + + Tweet URL + Tweet URL + key: ezrichtext.custom_tags.eztwitter.attributes.tweet_url.label + + + Width + Width + key: ezrichtext.custom_tags.eztwitter.attributes.width.label + + + Embed X post + Embed X post + key: ezrichtext.custom_tags.eztwitter.description + + + X + X + key: ezrichtext.custom_tags.eztwitter.label + + + Autoplay + Autoplay + key: ezrichtext.custom_tags.ezyoutube.attributes.autoplay.label + + + Height + Height + key: ezrichtext.custom_tags.ezyoutube.attributes.height.label + + + Video URL + Video URL + key: ezrichtext.custom_tags.ezyoutube.attributes.video_url.label + + + Width + Width + key: ezrichtext.custom_tags.ezyoutube.attributes.width.label + + + Embed YouTube video + Embed YouTube video + key: ezrichtext.custom_tags.ezyoutube.description + + + YouTube + YouTube + key: ezrichtext.custom_tags.ezyoutube.label + + +
+
diff --git a/src/lib/Translation/Extractor/ChoiceAttributeExtractor.php b/src/lib/Translation/Extractor/ChoiceAttributeExtractor.php new file mode 100644 index 00000000..81d2f272 --- /dev/null +++ b/src/lib/Translation/Extractor/ChoiceAttributeExtractor.php @@ -0,0 +1,81 @@ + */ + private array $customTags; + + private string $domain; + + /** @var string[] */ + private array $allowlist; + + /** + * @param array $customTags Custom tags definitions + * @param string $domain Target translation domain + * @param string[] $allowlist Whitelist of custom tags to extract + */ + public function __construct(array $customTags, string $domain, array $allowlist = []) + { + $this->customTags = $customTags; + $this->domain = $domain; + $this->allowlist = $allowlist; + } + + public function extract(): MessageCatalogue + { + $catalogue = new MessageCatalogueBuilder($this->domain); + foreach ($this->customTags as $tagName => $customTag) { + if (!in_array($tagName, $this->allowlist, true)) { + continue; + } + + $attributes = $customTag['attributes'] ?? []; + foreach ($attributes as $attributeName => $attribute) { + $type = $attribute['type'] ?? null; + if ($type !== self::CHOICE_ATTRIBUTE_TYPE) { + continue; + } + + foreach ($attribute['choices'] as $choice) { + if (empty($choice)) { + continue; + } + + $this->addChoiceLabelMessage($catalogue, $tagName, $attributeName, $choice); + } + } + } + + return $catalogue->getCatalogue(); + } + + private function addChoiceLabelMessage( + MessageCatalogueBuilder $catalogue, + string $tagName, + string $attributeName, + string $choice + ): void { + $catalogue->addMessage( + sprintf(self::CHOICE_LABEL_KEY, $tagName, $attributeName, $choice), + $choice + ); + } +} diff --git a/src/lib/Translation/Extractor/CustomTagExtractor.php b/src/lib/Translation/Extractor/CustomTagExtractor.php new file mode 100644 index 00000000..599a4b2b --- /dev/null +++ b/src/lib/Translation/Extractor/CustomTagExtractor.php @@ -0,0 +1,84 @@ + */ + private array $customTags; + + private string $domain; + + /** @var string[] */ + private array $allowlist; + + /** + * @param array $customTags Custom tags definitions + * @param string $domain Target translation domain + * @param string[] $allowlist Whitelist of custom tags to extract + */ + public function __construct(array $customTags, string $domain, array $allowlist = []) + { + $this->customTags = $customTags; + $this->domain = $domain; + $this->allowlist = $allowlist; + } + + public function extract(): MessageCatalogue + { + $catalogue = new MessageCatalogueBuilder($this->domain); + foreach ($this->customTags as $tagName => $config) { + if (!in_array($tagName, $this->allowlist, true)) { + continue; + } + + $this->addCustomTagLabelMessage($catalogue, $tagName); + $this->addCustomTagDescriptionMessage($catalogue, $tagName); + + /** @var string[] $attributes */ + $attributes = array_keys($config['attributes'] ?? []); + foreach ($attributes as $attributeName) { + $this->addAttributeLabelMessage($catalogue, $tagName, $attributeName); + } + } + + return $catalogue->getCatalogue(); + } + + private function addCustomTagLabelMessage(MessageCatalogueBuilder $catalogue, string $tagName): void + { + $catalogue->addMessage(sprintf(self::CUSTOM_TAG_LABEL, $tagName), $tagName); + } + + private function addCustomTagDescriptionMessage(MessageCatalogueBuilder $catalogue, string $tagName): void + { + $catalogue->addMessage(sprintf(self::CUSTOM_TAG_DESCRIPTION, $tagName), $tagName); + } + + private function addAttributeLabelMessage( + MessageCatalogueBuilder $catalogue, + string $tagName, + string $attributeName + ): void { + $catalogue->addMessage( + sprintf(self::ATTRIBUTE_LABEL, $tagName, $attributeName), + $attributeName + ); + } +} diff --git a/src/lib/Translation/Extractor/MessageCatalogueBuilder.php b/src/lib/Translation/Extractor/MessageCatalogueBuilder.php new file mode 100644 index 00000000..8355afea --- /dev/null +++ b/src/lib/Translation/Extractor/MessageCatalogueBuilder.php @@ -0,0 +1,60 @@ +domain = $domain; + $this->catalogue = new MessageCatalogue(); + } + + public function getDomain(): string + { + return $this->domain; + } + + public function reset(): void + { + $this->catalogue = new MessageCatalogue(); + } + + public function getCatalogue(): MessageCatalogue + { + return $this->catalogue; + } + + public function addMessage(string $id, string $desc): void + { + $this->catalogue->add($this->createMessage($id, $desc)); + } + + private function createMessage(string $id, string $desc): XliffMessage + { + $message = new XliffMessage($id, $this->domain); + $message->setNew(false); + $message->setMeaning($desc); + $message->setDesc($desc); + $message->setLocaleString($desc); + $message->addNote('key: ' . $id); + + return $message; + } +} diff --git a/src/lib/Translation/Extractor/OnlineEditorCustomAttributesExtractor.php b/src/lib/Translation/Extractor/OnlineEditorCustomAttributesExtractor.php index 63f64479..fdca10a6 100644 --- a/src/lib/Translation/Extractor/OnlineEditorCustomAttributesExtractor.php +++ b/src/lib/Translation/Extractor/OnlineEditorCustomAttributesExtractor.php @@ -15,7 +15,7 @@ use JMS\TranslationBundle\Translation\ExtractorInterface; /** - * Generates translation strings for limitation types. + * @deprecated 4.6.7 The "OnlineEditorCustomAttributesExtractor" class is deprecated, will be removed in 5.0. */ final class OnlineEditorCustomAttributesExtractor implements ExtractorInterface { diff --git a/tests/integration/IbexaTestKernel.php b/tests/integration/IbexaTestKernel.php index 1c26428a..d41662d5 100644 --- a/tests/integration/IbexaTestKernel.php +++ b/tests/integration/IbexaTestKernel.php @@ -76,7 +76,7 @@ public function registerContainerConfiguration(LoaderInterface $loader): void { parent::registerContainerConfiguration($loader); - $loader->load(__DIR__ . '/../lib/_settings/common.yaml'); + $loader->load(__DIR__ . '/Resources/override.yaml'); $loader->load(__DIR__ . '/Resources/config.yaml'); $loader->load(static function (ContainerBuilder $container): void { $container->setDefinition( diff --git a/tests/integration/Resources/override.yaml b/tests/integration/Resources/override.yaml new file mode 100644 index 00000000..3a0c61ed --- /dev/null +++ b/tests/integration/Resources/override.yaml @@ -0,0 +1,5 @@ +parameters: + ibexa.field_type.richtext.resources: 'src/bundle/Resources/richtext' + ibexa.field_type.richtext.validator.docbook.resources: + - '%ibexa.field_type.richtext.resources%/schemas/docbook/ezpublish.rng' + - '%ibexa.field_type.richtext.resources%/schemas/docbook/docbook.iso.sch.xsl'