-
Notifications
You must be signed in to change notification settings - Fork 81
IBX-10684: Document translation management #3249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dabrt
wants to merge
23
commits into
5.0
Choose a base branch
from
translations-management
base: 5.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,107
−1
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
f0df726
IBX-10684: Document translation management
dabrt 2aec539
Add events and extensions
dabrt bffca77
PHP & JS CS Fixes
dabrt 6f128e0
Fix formatting in events
dabrt 07583df
Add "Configure..." stem to enable building
dabrt 9a00463
Implement reviewer comments
dabrt 1196977
PHP & JS CS Fixes
dabrt 97c540b
Add the "Configura" article
dabrt 510b6bb
Clarify configuration article
dabrt 9f3f304
Fix issues
dabrt 5739e69
Remove duplicate blanks
dabrt 45be4c6
Add a release notes entry
dabrt 866d19c
Fix code issues
dabrt 00ebddc
PHP & JS CS Fixes
dabrt 45ce125
Add TranslationAddType to deptrack baseline
dabrt f51b281
Apply suggestions from code review
dabrt 53570f8
Add translation review description.
dabrt bedfd31
Implement reviewer comments
dabrt b9f08ca
Minor fix
dabrt 55a11e7
Implement reviewer comments, improve installation procedure
dabrt 2133328
Apply suggestion from @ciastekk
dabrt 74b684a
Apply suggestions from @ciastekk
dabrt c37003b
Apply suggestions from @ciastekk
dabrt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| services: | ||
| App\TranslationsManagement\MyCustomProvider: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.provider' | ||
| identifier: 'my_custom_provider' | ||
| validation_profile: 'my_custom_profile' | ||
| App\TranslationsManagement\MyProviderValidator: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.provider.validator' | ||
| profile: 'my_custom_profile' | ||
| App\TranslationsManagement\MyTranslationAddExtension: | ||
| tags: | ||
| - { name: form.type_extension } | ||
| App\TranslationsManagement\ImageAltTextTransformer: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.field_value_transformer' | ||
| field_type_identifier: 'ibexa_image' | ||
| App\TranslationsManagement\MyCustomExclusionRule: | ||
| tags: | ||
| - { name: 'ibexa.translations_management.side_by_side.exclusion_rule' } | ||
| app.translations_management.exclusion_rule.custom_field_types: | ||
| class: Ibexa\TranslationsManagement\SideBySide\Service\UnsupportedFieldTypeExclusionRule | ||
| arguments: | ||
| $excludedFieldTypeIdentifiers: ['custom_blog_post', 'custom_landing_page'] | ||
| tags: | ||
| - { name: 'ibexa.translations_management.side_by_side.exclusion_rule' } | ||
| App\TranslationsManagement\TwigComponent\MyTranslationModalFooter: | ||
| tags: | ||
| - name: ibexa.twig.component | ||
| group: 'admin-ui-content-translation-modal-footer' | ||
| priority: 10 | ||
| App\TranslationsManagement\MyCustomAiProvider: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.provider' | ||
| identifier: 'my_custom_ai_provider' | ||
| validation_profile: 'ai_generic' | ||
35 changes: 35 additions & 0 deletions
35
code_samples/translations_management/install/schema.mysql.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| CREATE TABLE IF NOT EXISTS ibexa_auto_translation ( | ||
| id INT AUTO_INCREMENT NOT NULL, | ||
| provider_identifier VARCHAR(190) NOT NULL, | ||
| content_id INT NOT NULL, | ||
| version_no INT NOT NULL, | ||
| source_language_id BIGINT NOT NULL, | ||
| target_language_id BIGINT NOT NULL, | ||
| review_status VARCHAR(64) NOT NULL, | ||
| created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', | ||
| updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', | ||
| INDEX ibexa_auto_translation_content_version_idx (content_id, version_no), | ||
| INDEX ibexa_auto_translation_target_language_idx (target_language_id), | ||
| INDEX ibexa_auto_translation_review_status_idx (review_status), | ||
| UNIQUE INDEX ibexa_auto_translation_context_uidx (content_id, version_no, source_language_id, target_language_id), | ||
| PRIMARY KEY(id) | ||
| ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_520_ci` ENGINE = InnoDB; | ||
|
|
||
| CREATE TABLE IF NOT EXISTS ibexa_auto_translation_review_log ( | ||
| id INT AUTO_INCREMENT NOT NULL, | ||
| auto_translation_id INT DEFAULT NULL, | ||
| user_id INT NOT NULL, | ||
| status VARCHAR(64) NOT NULL, | ||
| operation VARCHAR(64) NOT NULL, | ||
| created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', | ||
| INDEX IDX_325A3B737CE350E8 (auto_translation_id), | ||
| INDEX ibexa_auto_translation_review_log_auto_translation_created_idx (auto_translation_id, created_at, id), | ||
| INDEX ibexa_auto_translation_review_log_status_created_idx (status, created_at), | ||
| INDEX ibexa_auto_translation_review_log_user_idx (user_id), | ||
| PRIMARY KEY(id) | ||
| ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_520_ci` ENGINE = InnoDB; | ||
|
|
||
| ALTER TABLE ibexa_auto_translation_review_log ADD CONSTRAINT ibexa_auto_translation_review_log_auto_translation_fk | ||
| FOREIGN KEY (auto_translation_id) REFERENCES ibexa_auto_translation (id) ON UPDATE CASCADE ON DELETE SET NULL; | ||
| ALTER TABLE ibexa_auto_translation_review_log ADD CONSTRAINT ibexa_auto_translation_review_log_user_fk | ||
| FOREIGN KEY (user_id) REFERENCES ibexa_user (contentobject_id) ON UPDATE CASCADE ON DELETE RESTRICT; |
40 changes: 40 additions & 0 deletions
40
code_samples/translations_management/install/schema.postgresql.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| CREATE TABLE IF NOT EXISTS ibexa_auto_translation ( | ||
| id SERIAL NOT NULL, | ||
| provider_identifier VARCHAR(190) NOT NULL, | ||
| content_id INT NOT NULL, | ||
| version_no INT NOT NULL, | ||
| source_language_id BIGINT NOT NULL, | ||
| target_language_id BIGINT NOT NULL, | ||
| review_status VARCHAR(64) NOT NULL, | ||
| created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, | ||
| updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, | ||
| PRIMARY KEY(id) | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS ibexa_auto_translation_content_version_idx ON ibexa_auto_translation (content_id, version_no); | ||
| CREATE INDEX IF NOT EXISTS ibexa_auto_translation_target_language_idx ON ibexa_auto_translation (target_language_id); | ||
| CREATE INDEX IF NOT EXISTS ibexa_auto_translation_review_status_idx ON ibexa_auto_translation (review_status); | ||
| CREATE UNIQUE INDEX IF NOT EXISTS ibexa_auto_translation_context_uidx ON ibexa_auto_translation (content_id, version_no, source_language_id, target_language_id); | ||
| COMMENT ON COLUMN ibexa_auto_translation.created_at IS '(DC2Type:datetime_immutable)'; | ||
| COMMENT ON COLUMN ibexa_auto_translation.updated_at IS '(DC2Type:datetime_immutable)'; | ||
|
|
||
| CREATE TABLE IF NOT EXISTS ibexa_auto_translation_review_log ( | ||
| id SERIAL NOT NULL, | ||
| auto_translation_id INT DEFAULT NULL, | ||
| user_id INT NOT NULL, | ||
| status VARCHAR(64) NOT NULL, | ||
| operation VARCHAR(64) NOT NULL, | ||
| created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, | ||
| PRIMARY KEY(id) | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS IDX_325A3B737CE350E8 ON ibexa_auto_translation_review_log (auto_translation_id); | ||
| CREATE INDEX IF NOT EXISTS ibexa_auto_translation_review_log_auto_translation_created_idx ON ibexa_auto_translation_review_log (auto_translation_id, created_at, id); | ||
| CREATE INDEX IF NOT EXISTS ibexa_auto_translation_review_log_status_created_idx ON ibexa_auto_translation_review_log (status, created_at); | ||
| CREATE INDEX IF NOT EXISTS ibexa_auto_translation_review_log_user_idx ON ibexa_auto_translation_review_log (user_id); | ||
| COMMENT ON COLUMN ibexa_auto_translation_review_log.created_at IS '(DC2Type:datetime_immutable)'; | ||
|
|
||
| ALTER TABLE ibexa_auto_translation_review_log ADD CONSTRAINT ibexa_auto_translation_review_log_auto_translation_fk | ||
| FOREIGN KEY (auto_translation_id) REFERENCES ibexa_auto_translation (id) ON UPDATE CASCADE ON DELETE SET NULL; | ||
| ALTER TABLE ibexa_auto_translation_review_log ADD CONSTRAINT ibexa_auto_translation_review_log_user_fk | ||
| FOREIGN KEY (user_id) REFERENCES ibexa_user (contentobject_id) ON UPDATE CASCADE ON DELETE RESTRICT; |
41 changes: 41 additions & 0 deletions
41
...es/translations_management/src/TranslationsManagement/ContentProxyTranslateSubscriber.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement\EventSubscriber; | ||
|
|
||
| use Ibexa\Contracts\AdminUi\Event\ContentProxyTranslateEvent; | ||
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
| use Symfony\Component\HttpFoundation\RedirectResponse; | ||
| use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
|
|
||
| final readonly class ContentProxyTranslateSubscriber implements EventSubscriberInterface | ||
| { | ||
| public function __construct( | ||
| private UrlGeneratorInterface $urlGenerator, | ||
| ) { | ||
| } | ||
|
|
||
| public static function getSubscribedEvents(): array | ||
| { | ||
| return [ | ||
| ContentProxyTranslateEvent::class => ['onProxyTranslate', 200], | ||
| ]; | ||
| } | ||
|
|
||
| public function onProxyTranslate(ContentProxyTranslateEvent $event): void | ||
| { | ||
| // Read the translation context: | ||
| $event->getContentId(); | ||
| $event->getFromLanguageCode(); // ?string — null when no source language exists | ||
|
dabrt marked this conversation as resolved.
|
||
| $event->getToLanguageCode(); | ||
| $event->getLocationId(); // ?int — null when no location context is available | ||
|
dabrt marked this conversation as resolved.
|
||
|
|
||
| $url = $this->urlGenerator->generate('your_custom_route', [ | ||
| 'contentId' => $event->getContentId(), | ||
| ]); | ||
|
|
||
| $event->setResponse(new RedirectResponse($url)); | ||
| $event->stopPropagation(); | ||
| } | ||
| } | ||
60 changes: 60 additions & 0 deletions
60
code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\Core\Repository\Values\Content\Field; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\EncodedFieldValue; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\FieldValueTransformerInterface; | ||
| use Ibexa\Core\Base\Exceptions\InvalidArgumentException; | ||
| use Ibexa\Core\FieldType\Image\Value as ImageValue; | ||
| use Ibexa\Core\FieldType\Value; | ||
|
|
||
| final class ImageAltTextTransformer implements FieldValueTransformerInterface | ||
| { | ||
| public function getFieldTypeIdentifier(): string | ||
| { | ||
| return 'ibexa_image'; | ||
| } | ||
|
|
||
| public function encode(Field $field): EncodedFieldValue | ||
| { | ||
| $value = $field->getValue(); | ||
| if (!$value instanceof ImageValue) { | ||
| throw new InvalidArgumentException( | ||
| '$field', | ||
| sprintf('Expected %s, got %s.', ImageValue::class, get_debug_type($value)) | ||
| ); | ||
| } | ||
|
|
||
| return new EncodedFieldValue($value->alternativeText ?? ''); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<string, mixed> $metadata | ||
| */ | ||
| public function decode(string $value, mixed $previousFieldValue, array $metadata): Value | ||
| { | ||
| if (!$previousFieldValue instanceof ImageValue) { | ||
| throw new InvalidArgumentException( | ||
| '$previousFieldValue', | ||
| sprintf('Expected %s, got %s.', ImageValue::class, get_debug_type($previousFieldValue)) | ||
| ); | ||
| } | ||
|
|
||
| return new ImageValue([ | ||
| 'id' => $previousFieldValue->id, | ||
| 'fileName' => $previousFieldValue->fileName, | ||
| 'fileSize' => $previousFieldValue->fileSize, | ||
| 'uri' => $previousFieldValue->uri, | ||
| 'imageId' => $previousFieldValue->imageId, | ||
| 'inputUri' => $previousFieldValue->inputUri, | ||
| 'width' => $previousFieldValue->width, | ||
| 'height' => $previousFieldValue->height, | ||
| 'alternativeText' => $value, | ||
| 'additionalData' => $previousFieldValue->additionalData, | ||
| 'mime' => $previousFieldValue->mime, | ||
| ]); | ||
| } | ||
| } |
15 changes: 15 additions & 0 deletions
15
code_samples/translations_management/src/TranslationsManagement/MyApiClient.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| /** Placeholder for your own HTTP client or third-party SDK wrapper. */ | ||
| final class MyApiClient | ||
| { | ||
| public function translate(string $text, string $sourceLanguage, string $targetLanguage): string | ||
| { | ||
| // Your implementation here. | ||
| return ''; | ||
| } | ||
| } |
64 changes: 64 additions & 0 deletions
64
code_samples/translations_management/src/TranslationsManagement/MyCustomAiProvider.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Provider\AiTranslationProviderInterface; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\TranslationDataInterface; | ||
|
|
||
| final readonly class MyCustomAiProvider implements AiTranslationProviderInterface | ||
| { | ||
| /** | ||
| * Replace MyAiClient with your HTTP client, SDK wrapper, or any service | ||
| * that communicates with the external AI translation API. | ||
| */ | ||
| public function __construct( | ||
| private MyAiClient $apiClient, | ||
| private string $actionConfigurationIdentifier, | ||
| ) { | ||
| } | ||
|
|
||
| public function getIdentifier(): string | ||
| { | ||
| return 'my_custom_ai_provider'; | ||
| } | ||
|
|
||
| public function getName(): string | ||
| { | ||
| return 'My AI Translation Service'; | ||
| } | ||
|
|
||
| public function getVendorName(): string | ||
| { | ||
| return 'My Company Ltd'; | ||
| } | ||
|
|
||
| public function translate(TranslationDataInterface $translationData): string | ||
| { | ||
| return $this->apiClient->translate( | ||
| $translationData->getText(), | ||
| $translationData->getSourceLanguage(), | ||
| $translationData->getTargetLanguage() | ||
| ); | ||
| } | ||
|
|
||
| /** @return array<string> */ | ||
| public function getSupportedLanguageCodes(): array | ||
| { | ||
| return ['eng-GB', 'ger-DE', 'fre-FR']; | ||
| } | ||
|
|
||
| /** @return array<string, mixed> */ | ||
| public function getConfiguration(): array | ||
| { | ||
| return [ | ||
| 'actionConfigurationIdentifier' => $this->actionConfigurationIdentifier, | ||
| ]; | ||
| } | ||
|
|
||
| public function isConfigured(): bool | ||
| { | ||
| return $this->actionConfigurationIdentifier !== ''; | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; | ||
| use Ibexa\Contracts\TranslationsManagement\SideBySide\Service\SideBySideExclusionRuleInterface; | ||
|
|
||
| final class MyCustomExclusionRule implements SideBySideExclusionRuleInterface | ||
| { | ||
| public function isExcluded(ContentInfo $contentInfo): bool | ||
| { | ||
| return $contentInfo->getContentType()->identifier === 'my_excluded_type'; | ||
| } | ||
| } |
50 changes: 50 additions & 0 deletions
50
code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Provider\TranslationProviderInterface; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\TranslationDataInterface; | ||
|
|
||
| final readonly class MyCustomProvider implements TranslationProviderInterface | ||
| { | ||
| /** | ||
| * Replace MyApiClient with your HTTP client, SDK wrapper, or any service | ||
| * that communicates with the external translation API. | ||
| */ | ||
| public function __construct( | ||
| private MyApiClient $apiClient, | ||
| ) { | ||
| } | ||
|
|
||
| public function getIdentifier(): string | ||
| { | ||
| return 'my_custom_provider'; | ||
| } | ||
|
|
||
| public function getName(): string | ||
| { | ||
| return 'My Translation Service'; | ||
| } | ||
|
|
||
| public function getVendorName(): string | ||
| { | ||
| return 'My Company Ltd'; | ||
| } | ||
|
|
||
| public function translate(TranslationDataInterface $translationData): string | ||
| { | ||
| return $this->apiClient->translate( | ||
| $translationData->getText(), | ||
| $translationData->getSourceLanguage(), | ||
| $translationData->getTargetLanguage() | ||
| ); | ||
| } | ||
|
|
||
| /** @return array<string> */ | ||
| public function getSupportedLanguageCodes(): array | ||
| { | ||
| return ['eng-GB', 'ger-DE', 'fre-FR']; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.