diff --git a/code_samples/translations_management/config/services.yaml b/code_samples/translations_management/config/services.yaml new file mode 100644 index 00000000000..5be055d5c25 --- /dev/null +++ b/code_samples/translations_management/config/services.yaml @@ -0,0 +1,37 @@ +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\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_point2d', 'custom_value'] + 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' + App\TranslationsManagement\MyCustomLanguageNormalizer: + tags: + - name: 'ibexa.translations_management.auto_translate.provider.language_normalizer' + priority: 10 diff --git a/code_samples/translations_management/install/schema.mysql.sql b/code_samples/translations_management/install/schema.mysql.sql new file mode 100644 index 00000000000..e45471a1bbc --- /dev/null +++ b/code_samples/translations_management/install/schema.mysql.sql @@ -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; diff --git a/code_samples/translations_management/install/schema.postgresql.sql b/code_samples/translations_management/install/schema.postgresql.sql new file mode 100644 index 00000000000..c8af0e838cb --- /dev/null +++ b/code_samples/translations_management/install/schema.postgresql.sql @@ -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; diff --git a/code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php b/code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php new file mode 100644 index 00000000000..d11ec9b1305 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php @@ -0,0 +1,60 @@ +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 $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, + ]); + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyApiClient.php b/code_samples/translations_management/src/TranslationsManagement/MyApiClient.php new file mode 100644 index 00000000000..4091bb52859 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyApiClient.php @@ -0,0 +1,15 @@ +apiClient->translate( + $translationData->getText(), + $translationData->getSourceLanguage(), + $translationData->getTargetLanguage() + ); + } + + /** @return array */ + public function getSupportedLanguageCodes(): array + { + return ['eng-GB', 'ger-DE', 'fre-FR']; + } + + /** @return array */ + public function getConfiguration(): array + { + return [ + 'actionConfigurationIdentifier' => $this->actionConfigurationIdentifier, + ]; + } + + public function isConfigured(): bool + { + return $this->actionConfigurationIdentifier !== ''; + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php b/code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php new file mode 100644 index 00000000000..48a55e68e04 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php @@ -0,0 +1,16 @@ +getContentType()->identifier === 'my_excluded_type'; + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyCustomLanguageCodeNormalizer.php b/code_samples/translations_management/src/TranslationsManagement/MyCustomLanguageCodeNormalizer.php new file mode 100644 index 00000000000..0640f372c4a --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyCustomLanguageCodeNormalizer.php @@ -0,0 +1,38 @@ + 'en-GB', + 'ger-DE' => 'de', + 'fre-FR' => 'fr', + ]; + + public function supports(TranslationProviderInterface $provider): bool + { + return $provider->getIdentifier() === 'my_custom_ai_provider'; + } + + public function normalize( + TranslationProviderInterface $provider, + string $languageCode + ): string { + if (isset(self::LANGUAGE_MAP[$languageCode])) { + return self::LANGUAGE_MAP[$languageCode]; + } + + throw new UnsupportedLanguageException( + $languageCode, + $provider->getIdentifier(), + array_values(self::LANGUAGE_MAP) + ); + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php b/code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php new file mode 100644 index 00000000000..e726d3435fd --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php @@ -0,0 +1,50 @@ +apiClient->translate( + $translationData->getText(), + $translationData->getSourceLanguage(), + $translationData->getTargetLanguage() + ); + } + + /** @return array */ + public function getSupportedLanguageCodes(): array + { + return ['eng-GB', 'ger-DE', 'fre-FR']; + } +} diff --git a/composer.json b/composer.json index 0db07f05c8d..b6fa91384e4 100644 --- a/composer.json +++ b/composer.json @@ -94,6 +94,7 @@ "ibexa/connector-raptor": "~5.0.x-dev", "ibexa/image-editor": "~5.0.x-dev", "ibexa/integrated-help": "~5.0.x-dev", + "ibexa/translations-management": "~5.0.x-dev", "ibexa/site-context": "~5.0.x-dev", "ibexa/fieldtype-richtext-rte": "~5.0.x-dev", "ibexa/site-factory": "~5.0.x-dev", diff --git a/docs/administration/back_office/back_office_elements/custom_components.md b/docs/administration/back_office/back_office_elements/custom_components.md index cc18410c1cf..37fbab2eb3b 100644 --- a/docs/administration/back_office/back_office_elements/custom_components.md +++ b/docs/administration/back_office/back_office_elements/custom_components.md @@ -134,3 +134,10 @@ For more information, see [this example using few of those components](component |`admin-ui-discount-condition-code-usage-summary`| `vendor/ibexa/discounts/src/bundle/Resources/views/themes/admin/discounts/tab/details.html.twig` | |`admin-ui-discount-condition-code-summary`| `vendor/ibexa/discounts/src/bundle/Resources/views/themes/admin/discounts/tab/details.html.twig` | |`admin-ui-discount-condition-code-usage-limit-summary`| `vendor/ibexa/discounts/src/bundle/Resources/views/themes/admin/discounts/tab/details.html.twig` | + +## Translations management [[% include 'snippets/lts-update_badge.md' %]] + +| Group name | Template file | +|---|---| +|`admin-ui-content-translation-modal-footer`| `vendor/ibexa/translations-management/src/bundle/Resources/views/themes/admin/translations_management/component/side_by_side_modal_footer.html.twig` | +|`admin-ui-content-edit-translation-select-footer`| `vendor/ibexa/translations-management/src/bundle/Resources/views/themes/admin/translations_management/component/side_by_side_content_edit_footer.html.twig` | diff --git a/docs/api/event_reference/event_reference.md b/docs/api/event_reference/event_reference.md index 6cc5792d698..49396a4264f 100644 --- a/docs/api/event_reference/event_reference.md +++ b/docs/api/event_reference/event_reference.md @@ -37,6 +37,7 @@ For example, copying a content item is connected with two events: `BeforeCopyCon "api/event_reference/segmentation_events", "api/event_reference/site_events", "api/event_reference/taxonomy_events", + "api/event_reference/translations_management_events", "api/event_reference/trash_events", "api/event_reference/twig_component_events", "api/event_reference/url_events", diff --git a/docs/api/event_reference/translations_management_events.md b/docs/api/event_reference/translations_management_events.md new file mode 100644 index 00000000000..c910933617f --- /dev/null +++ b/docs/api/event_reference/translations_management_events.md @@ -0,0 +1,29 @@ +--- +description: Events that are triggered when working with translations management. +edition: lts-update +page_type: reference +--- + +# Translations management events + +The [Translations management](translations_management_guide.md) package dispatches events at two levels. + +## Translation events + +Translation events are dispatched for every field in each selected target language. +Use them for logging, analytics, and observability. +Both events are read-only, you can't use them to override the translation result. + +| Event | Dispatched by | Dispatched when | +|---|---|---| +| [`BeforeTranslateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Event-BeforeTranslateEvent.html) | `EventDispatchingProviderTranslator` | Before a translation request is sent to the provider | +| [`TranslateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Event-TranslateEvent.html) | `EventDispatchingProviderTranslator` | After a translation response is received | + +## Side-by-side creation events + +Side-by-side creation events are dispatched when preparing a new translation draft. + +| Event | Dispatched by | Dispatched when | +|---|---|---| +| [`OnContentSideBySideTranslationCreateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-SideBySide-Event-OnContentSideBySideTranslationCreateEvent.html) | `ContentTranslationCreateController` | When creating a draft side-by-side translation of a content item | +| [`OnProductSideBySideTranslationCreateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-SideBySide-Event-OnProductSideBySideTranslationCreateEvent.html) | `ProductTranslationViewController` | When creating a draft side-by-side translation of a product | diff --git a/docs/ibexa_products/editions.md b/docs/ibexa_products/editions.md index c9df1b14e5d..ef6e24f5ffc 100644 --- a/docs/ibexa_products/editions.md +++ b/docs/ibexa_products/editions.md @@ -71,3 +71,4 @@ The features brought by LTS Updates become standard parts of the next LTS releas | [Integrated help](integrated_help.md) | ✔ | ✔ | ✔ | | [MCP servers](mcp_guide.md) | ✔ | ✔ | ✔ | | [Shopping list](shopping_list_guide.md) | | | ✔ | +| [Translations management](translations_management_guide.md) | ✔ | ✔ | ✔ | diff --git a/docs/multisite/img/diagram_source/translations_management_flow.drawio b/docs/multisite/img/diagram_source/translations_management_flow.drawio new file mode 100644 index 00000000000..e5f8e09a4d8 --- /dev/null +++ b/docs/multisite/img/diagram_source/translations_management_flow.drawio @@ -0,0 +1 @@ +3Zpbd6M2EIB/jc9pH+wDyNwebWI7zmZ307rtJk89spGNWoyoEL7sr68EwgaLNOwGh03zkJjRhdE3o9FonB7wtocZhXHwkfgo7Bmaf+iBm55huIbDfwvBMRfoQ1NKNhT7UnYWLPBXJIWalKbYR0mlIyMkZDiuClckitCKVWSQUrKvdluTsPrWGG6QIlisYKhKv2CfBVKqa9q54RbhTSBf7ZiyYQuLzlKQBNAn+5IITHrAo4Sw/NP24KFQwCu45OOmz7SeFKMoYk0GjPIBOximcm0jf4sjoRliCf+TxgIuhVESQoaJaIkp2XH+NJFLYMeCCyVp5CMxtd4D432AGVrEcCVa99wTuCxg21A2r3EYeiQkNBsLfIic9YrLE0bJ36jUYq0ctFyLlgKWGK6uVC5+hyhDh5JIrnyGyBYxeuRdZKth5iOkG/b1wr/2JaM6UhaU7GlJGZR+tDlNfUbNP0ja9eTHCvmJjxlfs6FR9E+Kkgx/hPaX/NtkbiLHH9Yxd4wlsKxrMHe7hO4p0H9FCQl3KCn5dc+wQv7O8VJ82ohPP4Uw2qR8U/djiIWFxAv53khEZ75TQh5kuG1+btU46/XaWNVuCN9aWuY1jKMPtQvjqLY5ha6ybewWbDNVN8SBO/4q2wfFDoBLjpZHbIxCv9340wHui/jjvuFGmCmwFyjyM9Lk2YD/7nm73QG/rYk8K4TzyFPg5igFX4qjzbt3br0aSgygq6FEuxLsuQLbo4jjzVhDukGsX0R0kYZSuGbvHbfhdMj77r9ymR1G+yQ7My+PVRgJf98SH/N4ntkmQP+vbIffbCpWAcYbWuWDGuNhHm9gchW37wJw1euHNXx1s4av2QLfe5VvwNeSZK/nby+cv+WDswvKtvkyZvtKmD8qmB/SZYiTALV8AX17rsBq4L51+XYbXFFNsUCKEpLSVVEPyEX5sdkr36KQX6mSqKukSETxXbVo8iqVjUYqj38klUEjlT1V5WnbKmdDR5TCY6lDTHDEktLMD0JQvqNUt76jXaw+n/DM4qRZMzzDRnimKp5ZZxY1lYDU84zeyG60ktbVrjfbqRR5SgcvIkaOUo46L/5b3QM4VffQ3Yt64wt6XfR/vT9Zjaxwq/rTvDN/auY4c1Xlux8iQoCLm0eR47ZmUqcRnzuVz4fOTOp+r8r3nalcZNLfrvPHrnTGG9v36DiczqLHz7o32j+G6alkWcoKhWYL+UgoC8iGRDCcnKXjc96o8adzn3tCYpnu/YUYO8qvomDKSDWXRAfMHsXwgSmfnkotNwc5c/ZwLD08IIr5uhGVsmdtcO0D45RSFgeGYQ7c0o9tV2fMfUI5P1497zPn0nfEDffPCXn6dMfu4efRrZ8u7F++gr7Mxkpn92/NSp18bhwn6OVbBUzi/CvHNT4Ij1JrQ8iqrw1Npx4Y39RfM5Q7RY2TPH99u6h7nsr85WtGXRXiJHzNLq21w1Cxw8P977P5J4V7dWe+wL6KNCIRUvlbpqt5w2zPs9zq4MbV2gENgDnQ3Apr2xrUVOKGBhhYKm9gXAm3peBezG8m/fFTX/zlLX/MJ1+uz34MPGNsXIf90NIHGqhmIrY9cB3V1W19YOut0OeP5+/q8xh1/o8HMPkX \ No newline at end of file diff --git a/docs/multisite/img/managing_translations_sxs_view.png b/docs/multisite/img/managing_translations_sxs_view.png new file mode 100644 index 00000000000..466a8465bf7 Binary files /dev/null and b/docs/multisite/img/managing_translations_sxs_view.png differ diff --git a/docs/multisite/img/translations_management_flow.png b/docs/multisite/img/translations_management_flow.png new file mode 100644 index 00000000000..46a55427827 Binary files /dev/null and b/docs/multisite/img/translations_management_flow.png differ diff --git a/docs/multisite/img/translations_management_language_pairs.png b/docs/multisite/img/translations_management_language_pairs.png new file mode 100644 index 00000000000..2c925fa4720 Binary files /dev/null and b/docs/multisite/img/translations_management_language_pairs.png differ diff --git a/docs/multisite/languages/automated_translations.md b/docs/multisite/languages/automated_translations.md index ed78378e1ca..d5dc1ca5664 100644 --- a/docs/multisite/languages/automated_translations.md +++ b/docs/multisite/languages/automated_translations.md @@ -19,6 +19,10 @@ The following field types are supported out of the box: See [adding a custom field or block attribute encoder](#create-custom-field-or-block-attribute-encoder) for more information on how you can extend this list. +!!! note + + If you're currently using Automated translations, consider migrating to [Translations management](translations_management_guide.md). + ## Configure automated content translation ### Install package diff --git a/docs/multisite/translations_management/configure_translations_management.md b/docs/multisite/translations_management/configure_translations_management.md new file mode 100644 index 00000000000..e359b4987ac --- /dev/null +++ b/docs/multisite/translations_management/configure_translations_management.md @@ -0,0 +1,235 @@ +--- +description: Install translations management and configure translation providers, language pairs, and more. +edition: lts-update +month_change: true +--- + +# Configure translations management + +`ibexa/translations-management` extends [[= product_name =]]'s built-in language management tools that editors use for content item and product translation. +It introduces a plugin that handles automatic translations through the translation provider system by connecting to REST APIs and AI services. +By using the new [side-by-side editing interface](#side-by-side-translation-view), editors can compare source and target values, provide content item and product translations in a single view, and reject or approve translations. +There are multiple extension points that you can use to [customize different areas of the translation workflow](extend_translations_management.md). + +!!! note "Translation limitations" + + The following limitations apply to automatic translation: + + - Content types that contain the `ibexa_form` or `ibexa_landing_page` fields don't support the side-by-side translation view and open in the single-language editor instead. + - For `ibexa_landing_page` fields, translatable attributes of block content are sent to the translation provider, while layout, zones, and non-translatable block attributes are preserved. + - The value of `ibexa_form` field type is not translated. + + Also, [product attributes](products.md#product-attributes) remain non-translatable and are inactive in the side-by-side translation view. + +## Install package + +To install the Translations management [LTS Update](editions.md#lts-updates), run the following command: + +```bash +composer require ibexa/translations-management +``` + +If you're installing Translations management LTS Update as part of the installation process of a fresh [[= product_name =]] instance, this step copies the migration files into the project's migrations directory. +It also creates the database tables required for the review workflow, and adds the default action configurations in the database. +Otherwise follow the steps below. + +### Existing installations + +To add the Translations management LTS Update to an existing [[= product_name =]] instance, after installation, you must create database tables and action configurations yourself. + +#### Modify database schema + +Add the tables needed by the bundle: + +=== "MySQL" + + ```sql + [[= include_code('code_samples/translations_management/install/schema.mysql.sql', indent_level=1) =]] + ``` + +=== "PostgreSQL" + + ```sql + [[= include_code('code_samples/translations_management/install/schema.postgresql.sql', indent_level=1) =]] + ``` + +The script creates the required data structures, but doesn't add any data to the database. + +#### Add action configurations + +To complete the setup, import and run the AI Action Configuration migrations required by the [AI connectors](configure_ai_actions.md) that you use: + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/translations-management/src/bundle/Resources/migrations/2026_05_06_15_00_auto_translate_openai_action_configuration.yaml +php bin/console ibexa:migrations:import vendor/ibexa/translations-management/src/bundle/Resources/migrations/2026_05_11_10_00_auto_translate_gemini_action_configuration.yaml +php bin/console ibexa:migrations:import vendor/ibexa/translations-management/src/bundle/Resources/migrations/2026_05_12_08_30_auto_translate_anthropic_action_configuration.yaml +php bin/console ibexa:migrations:migrate +``` + +## Configure translation providers + +Translation providers are the services that perform the actual text translation. +If you fail to configure them, the automatic translation feature is disabled in the editor's UI, and a message is displayed that prompts the user to contact the administrator. + +The Translations management package comes with two types of translation services: + +- **REST API-based providers** - call a translation service such as Google Translate or DeepL directly by using an API key. +- **AI-based providers** - send translation requests through the [AI Actions](configure_ai_actions.md) framework, relying on the same model selection and policy controls as other AI features in [[= product_name =]]. + +!!! note "Prerequisites for the default translation providers" + + Before you can configure translation providers, you must meet the following prerequisites: + + - For the REST API-based translation providers, add API keys that you obtain from the machine translation services to the `.env` file in the root directory of your project. + - For the AI-based translation providers, [configure AI Actions and the corresponding connectors](configure_ai_actions.md). + +Out of the box, Translations management can support the following translation providers: + +| Provider | Type | +|---|---| +| Google Translate | REST API | +| DeepL | REST API | +| OpenAI | AI Actions | +| Anthropic (Claude) | AI Actions | +| Google Gemini | AI Actions | + +### Built-in AI providers + +If you meet the above prerequisites, and you install the Translations management package, the installation process automatically creates AI [Action Configurations](extend_ai_actions.md#action-configurations) for OpenAI (`auto_translate_openai`), Google Gemini (`auto_translate_gemini`), and Anthropic Claude (`auto_translate_anthropic`). + +You can use them directly in provider configuration: + +| Action Configuration identifier | Handler | Default model | +|---|---|---| +| `auto_translate_openai` | `openai-text-to-text` | `gpt-5` | +| `auto_translate_gemini` | `gemini-text-to-text` | `gemini-pro-latest` | +| `auto_translate_anthropic` | `anthropic-text-to-text` | `claude-sonnet-4-20250514` | + +You can then [customize these configurations in the UI]([[= user_doc =]]/ai_actions/work_with_ai_actions/#edit-existing-ai-actions). + +### Add YAML configuration + +In `config/packages`, create a `translations_management.yaml` file. +You configure the providers in the SiteAccess-aware `translations_management` namespace. + +``` yaml +ibexa: + system: + default: + translations_management: + auto_translate: + providers: + google: + apiKey: '%env(GOOGLE_TRANSLATE_API_KEY)%' + deepl: + apiKey: '%env(DEEPL_API_KEY)%' + openai: + actionConfigurationIdentifier: 'auto_translate_openai' + anthropic: + actionConfigurationIdentifier: 'auto_translate_anthropic' + gemini: + actionConfigurationIdentifier: 'auto_translate_gemini' +``` + +The `apiKey` values must reference API key values that you added to the `.env` file. +The `actionConfigurationIdentifier` values must reference existing Action Configurations. +If a value is missing or empty, the provider doesn't appear in the UI as a selectable option. + +#### Advanced translation provider options + +In addition to their required authentication keys, all providers support two optional ones: + +- `supportedLanguageCodes` - overrides the default list of language codes that this provider accepts +- `languageCodesMap` - maps language codes used by [[= product_name =]], for example, `eng-GB`, to the provider-specific codes the API expects + +REST API-based providers come with their own language code lists and mappings, therefore both settings are optional. +If configured, they replace the built-in defaults, so use them to restrict available languages or override mappings. + +!!! tip "Default values" + + To check the built-in defaults for the existing providers, run: + + ``` bash + php bin/console debug:container --parameters | grep ibexa.translations_management.auto_translate.provider + ``` + + The output lists the default `supported_language_codes` and `language_codes_map` values for each configured provider, which you can use as a reference. + +AI-based providers don't provide built-in language code lists or mappings. +If `supportedLanguageCodes` is not configured, all enabled languages are used, converted to POSIX format. +If `languageCodesMap` is not configured, the system automatically tries to match [[= product_name =]] language codes to the one supported by the provider by trying different format variants, for example, `eng-GB`, `en-GB`, or `en`. +If no match is found, an `UnsupportedLanguageException` is thrown at runtime. +Therefore, for AI-based providers, it's recommended that you explicitly configure both options. + +``` yaml +ibexa: + system: + default: + translations_management: + auto_translate: + providers: + # ... + openai: + actionConfigurationIdentifier: 'auto_translate_openai' + supportedLanguageCodes: + - 'eng-GB' + - 'ger-DE' + - 'fre-FR' + languageCodesMap: + eng-GB: 'en' + ger-DE: 'de' + fre-FR: 'fr' +``` + +The `supportedLanguageCodes` setting controls which languages are available when creating [language pairs](#define-language-pairs) for this provider. + +!!! note "Identifier normalization" + + Provider identifiers are normalized from hyphens to underscores during configuration processing. + Use one format consistently. + If you mix `my-provider` and `my_provider` for the same provider, it results in an exception. + +## Define language pairs + +Language pair definitions decide which provider handles each source-to-target language combination by default. +For example, you can decide that English to French translations should use DeepL. +When an editor [opens the translation modal]([[= user_doc =]]/content_management/translate_content/#add-new-translation) and selects a matching language combination, the provider that you chose is pre-selected in the dropdown. +The editor can override the pre-selection. + +The list of languages available when creating a language pair is determined by what each provider supports. +You can only select the languages that are present in a provider's [supported list](#advanced-translation-provider-options) for that provider's pairs. + +You [manage language pairs in the back office]([[= user_doc =]]/content_management/translate_content/#manage-translation-services-and-language-pairs). + +## Side-by-side translation view + +The [side-by-side translation view]([[= user_doc =]]/content_management/translate_content/#side-by-side-translation-view) is a two-column content editing interface where the source column is read-only and the target column is an editable form. + +Content types that contain the `ibexa_landing_page` or `ibexa_form` fields can't be opened in the side-by-side translation view. +Editors can open them in the standard single-language editor. + +You can exclude the support for additional content types if needed. +To do it, [define custom exclusion rules](extend_translations_management.md#define-custom-exclusion-rules). + +!!! note "Meta fields" + + Fields marked with [`meta: true`](content_tab_switcher.md#add-meta-tab) and fields that belong to groups listed in [`admin_ui_forms.content_edit.meta_field_groups_list`](content_tab_switcher.md#configure-field-groups-for-meta-tab) aren't rendered in the side-by-side translation view. + +For a description of the side-by-side view and its functions from the editor's perspective, see [User Documentation]([[= user_doc =]]/content_management/translate_content/#side-by-side-translation-view). + +### User settings + +The Translations management package adds preferences that editors can configure under their [user settings]([[= user_doc =]]/getting_started/get_started/#user-settings). +Each editor can configure them independently, and they don't affect other users. + +For example, editors can choose whether the target language column appears on the left or right in the side-by-side translation view. +By default, the target is on the right, and each editor can override this default. + +You can change the system-wide default in configuration: + +``` yaml +parameters: + ibexa.site_access.config.default.translations_management.default_side_by_side_column_order: source_right_target_left +``` + +The accepted values are `source_left_target_right` (default) and `source_right_target_left`. diff --git a/docs/multisite/translations_management/extend_translations_management.md b/docs/multisite/translations_management/extend_translations_management.md new file mode 100644 index 00000000000..1f1944e8e26 --- /dev/null +++ b/docs/multisite/translations_management/extend_translations_management.md @@ -0,0 +1,180 @@ +--- +description: Add custom classes, exclude custom content types and add support for custom fields. +edition: lts-update +month_change: true +--- + +# Extend translations management + +By extending [Translations management](translations_management_guide.md), you can adapt the package's behavior to your specific requirements. +The package is designed to be extended in multiple ways. +You can create custom [translation providers](configure_translations_management.md#configure-translation-providers), field type transformers, exclusion rules, and UI components. +In all cases, you follow the same pattern: implement an interface first, then register the service with a service tag. +The package discovers and registers tagged services automatically. + +## Add custom translation provider + +Before you build a custom translation provider, if your provider uses the AI Actions framework, make sure that the `ibexa/connector-ai` package is configured in your system. + +### REST API-based provider + +To connect a translation service that calls a REST API directly, implement [`TranslationProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-TranslationProviderInterface.html). +For providers that store API keys and other required settings, you can rely on [`ConfigurableProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-ConfigurableProviderInterface.html). +It extends `TranslationProviderInterface` and adds `getConfiguration()` and `isConfigured()` methods. + +The `translate()` method receives a [`TranslationDataInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-TranslationDataInterface.html) object that carries the text to translate along with the source and target [language codes](configure_translations_management.md#advanced-translation-provider-options): + +``` php hl_lines="36-49" +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php') =]] +``` + +Register the provider with the `ibexa.translations_management.auto_translate.provider` tag. +Both `identifier` and [`validation_profile`](#validation-profiles) attributes are required. + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 6) =]] +``` + +### AI-based provider + +To connect a translation service that uses the [AI Actions](ai_actions.md) framework, implement [`AiTranslationProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-AiTranslationProviderInterface.html). +This interface extends `ConfigurableProviderInterface` and serves as a type marker for AI-based providers. +The system uses the `getConfiguration()` and `isConfigured()` methods to determine whether the provider is available before displaying selectable options in the **Create a new translation** modal: + +``` php hl_lines="53 60" +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyCustomAiProvider.php') =]] +``` + +Register the provider with the `ibexa.translations_management.auto_translate.provider` tag, with `ai_generic` as the validation profile. +The `ai_generic` validation profile is meant to be used by default for AI providers, but you can [implement your own](#validation-profiles). + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 29, 33) =]] +``` + +If your custom provider integrates with the AI Actions framework, `isConfigured()` should check whether the `actionConfigurationIdentifier` resolves to an existing and enabled Action Configuration. + +The `validation_profile`, `supportedLanguageCodes`, and `languageCodesMap` options work the same way as for REST API-based providers. + +### Language code normalizer + +If your provider uses [language codes](configure_translations_management.md#advanced-translation-provider-options) that differ from the ones used by [[= product_name =]] and the `languageCodesMap` configuration is insufficient, implement a custom [`LanguageNormalizerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-LanguageNormalizer-LanguageNormalizerInterface.html) to handle the conversion: + +``` php +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyCustomLanguageCodeNormalizer.php') =]] +``` + +The `supports()` method is a way to bind the normalizer to a provider. +When a translation is triggered, the system checks the registered normalizers, and it uses the first one whose `supports()` method returns `true` for the current provider. + +Register the normalizer with the `ibexa.translations_management.auto_translate.provider.language_normalizer` tag: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 34, 37) =]] +``` + +If multiple normalizers are registered, use `priority` to control the order in which they're checked. + +### Validation profiles + +The `validation_profile` attribute links the provider to a validator that checks language codes and payload size before each translation request. +By default, three profiles are available: + +| Profile | Used by | +|---|---| +| `google` | Google Translate provider | +| `deepl` | DeepL provider | +| `ai_generic` | All built-in AI providers. Suitable for custom AI providers. | + +To define a custom validation profile, implement [`ProviderValidatorInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Validator-ProviderValidatorInterface.html) and register it: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 7, 10) =]] +``` + +You can reuse the [`DefaultProviderValidator`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Validator-DefaultProviderValidator.html) class if it meets your requirements or implement your own. +It exposes configurable maximum payload size and language code regex patterns. + +## Add support for custom field types + +The translation engine works by extracting translatable text from fields, sending it to the provider, and writing the translated text back. +Field value transformers handle this encode/decode cycle, one per field type. +The package includes transformers for `text`, `RichText`, and `ibexa_landing_page` fields. + +To add support for a custom or non-standard field type, implement [`FieldValueTransformerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Transformer-Field-FieldValueTransformerInterface.html): + +- `getFieldTypeIdentifier()` - returns the field type identifier that this transformer handles +- `encode(Field $field): EncodedFieldValue` - extracts the translatable string from the field and wraps it in an [`EncodedFieldValue`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Transformer-Field-EncodedFieldValue.html). +The constructor takes the extracted string as its first argument and an optional metadata array as the second. +- `decode(string $value, mixed $previousFieldValue, array $metadata): Value` - receives the translated string, the previous field value, and any metadata. Returns the updated field value. + +The following example adds support for automatically translating the alternative text of an image: + +``` php hl_lines="21 31 37 46-58" +[[= include_code('code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php') =]] +``` + +Register the new transformer with the `ibexa.translations_management.auto_translate.field_value_transformer` tag. +The `field_type_identifier` attribute is required. +It must match the value that `getFieldTypeIdentifier()` returns: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 11, 14) =]] +``` + +!!! note "Advanced metadata handling" + + When metadata is required for decoding or when you need to control what happens if metadata encoding fails, implement [`MetadataAwareFieldValueTransformerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Transformer-Field-MetadataAwareFieldValueTransformerInterface.html). + With this interface, you can fail the translation when metadata encoding fails and indicate that metadata is required for decoding. + Without it, the field is skipped instead. + +## Define custom exclusion rules + +Use exclusion rules to identify content that cannot use the side-by-side view. +The Translations management package ships with one rule that excludes content types that contain `ibexa_landing_page` or `ibexa_form` fields. + +### Exclude with custom class + +To exclude content from side-by-side view, for example, content types whose fields render incorrectly in the side-by-side layout, implement [`SideBySideExclusionRuleInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-SideBySide-Service-SideBySideExclusionRuleInterface.html). +The `isExcluded()` method receives a [`ContentInfo`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-ContentInfo.html) object, which gives you access to different criteria, including content type, section, owner, main language, publication status, visibility, and main location of the content item. +If the content item should be excluded, the method should return `true`. + +``` php +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php') =]] +``` + +Register the rule with the `ibexa.translations_management.side_by_side.exclusion_rule` tag. +This interface is not registered for [Symfony autoconfiguration]([[= symfony_doc =]]/service_container.html#the-autoconfigure-option), so the tag is required. + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 15, 18) =]] +``` + +## Use Twig component extension points + +Two [Twig component groups](custom_components.md#translations-management) allow you to inject custom UI elements into the translation interface without the need to override their templates. + +Such custom element could be, for example, a disclaimer or policy notice that the editor must acknowledge before a translation is created. + +The two groups behave differently: + +- `admin-ui-content-translation-modal-footer` — if any of the [components](components.md) renders output that is not empty, it entirely replaces the default footer buttons. +Your component template must therefore include its own action buttons. +- `admin-ui-content-edit-translation-select-footer` — component output is inserted between the existing **Edit** and **Discard** buttons of the content edit confirmation screen. + +Register a component with the `ibexa.twig.component` tag: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 24, 28) =]] +``` + +!!! note + + The `admin-ui-content-translation-modal-footer` group receives a `location` variable that may be `null` for an unpublished draft. + Always check for `null` before you access location properties in your component template. diff --git a/docs/multisite/translations_management/translate_with_cli.md b/docs/multisite/translations_management/translate_with_cli.md new file mode 100644 index 00000000000..0b7ad851856 --- /dev/null +++ b/docs/multisite/translations_management/translate_with_cli.md @@ -0,0 +1,37 @@ +--- +description: Use CLI command to translate content items. +edition: lts-update +month_change: true +--- + +# Translate content items with CLI + +For the purposes of batch processing, automation, and other scripted actions, the [Translations management](translations_management_guide.md) package exposes a command that automatically translates content items or products by using any of the configured providers: + +``` bash +php bin/console ibexa:translations:auto-translate-content \ + --content-id=42 \ + --provider=deepl \ + --from=eng-GB \ + --to=fre-FR +``` + +!!! tip "Command alias" + + You can use `ibexa:translations:translate-content` as an alias. + +The command uses the same provider configuration and field value transformers as the UI. +Therefore, depending on the specific command options used, the result can be the same as if an editor [triggered the automated translation manually]([[= user_doc =]]/content_management/translate_content/#add-new-translation). + +Without the `--draft-only` option, the translation generated with a CLI command is instantly published, while a manual one requires that a human publishes it. + +## CLI command options + +| Option | Required | Description | +|---|---|---| +| `--content-id` | Yes | ID of the content item or product to translate | +| `--provider` | Yes | Identifier of the translation provider to use | +| `--from` | Yes | Source language code | +| `--to` | Yes | Target language code | +| `--user-id` | No | Repository user ID to run the translation (default: `14`, which is the Administrator user) | +| `--draft-only` | No | Create a translated draft without publishing it | diff --git a/docs/multisite/translations_management/translations_management.md b/docs/multisite/translations_management/translations_management.md new file mode 100644 index 00000000000..3bbd07fd599 --- /dev/null +++ b/docs/multisite/translations_management/translations_management.md @@ -0,0 +1,20 @@ +--- +description: Translations management brings multiple features that help managers, developers and localization teams automate multilingual content delivery. +edition: lts-update +page_type: landing_page +month_change: true +--- + +# Translations management + +Translations management helps [[= product_name =]] developers and editors deliver automated content item and product translations. + +[[= cards([ + "multisite/translations_management/translations_management_guide", + "multisite/translations_management/configure_translations_management", + "multisite/translations_management/translate_with_cli", + "multisite/translations_management/extend_translations_management", + "api/event_reference/translations_management_events", + ("/api/php_api/php_api_reference/namespaces/ibexa-contracts-translationsmanagement.html", "PHP API Reference", "Ibexa\\Contracts\\TranslationsManagement"), + +], columns=3) =]] diff --git a/docs/multisite/translations_management/translations_management_guide.md b/docs/multisite/translations_management/translations_management_guide.md new file mode 100644 index 00000000000..85dd22b2c0e --- /dev/null +++ b/docs/multisite/translations_management/translations_management_guide.md @@ -0,0 +1,111 @@ +--- +description: Translations management helps managers, developers and localization teams with multilingual content delivery. +edition: lts-update +month_change: true +--- + +# Translations management product guide + +## What is Translations management + +Content managers, editors, translators, and proofreaders who work with multilingual content in [[= product_name =]] often face a common set of challenges: + +- context is lost when the source text isn't visible alongside the translation +- translating long and complex content items is time-consuming +- quality assurance is slow and error-prone without a direct comparison view +- switching between tools or tabs to cross-reference languages disrupts focus and slows down publishing + +The Translations management package addresses these pain points through a side-by-side view, machine translation and the ability to invite reviewers to collaborate on the translation of content items or products. + +The package integrates with the [AI Actions framework](ai_actions_guide.md) to support machine translation providers such as Google Translate and DeepL, and AI-powered translation services like OpenAI, Anthropic, and Google Gemini. + +Administrators can manage providers and configure default provider-to-language-pair mappings directly in [[= product_name =]]'s back office, while editors can trigger machine translation from the content editing interface. + +!!! note + + Translations management is a standalone set of features. + Although some views are similar to those delivered by the [Automated translations](automated_translations.md) opt-in package, Translations management does not require the `ibexa/automated-translation` package to run. + These two packages use different namespaces, service tags, and provider interfaces. + + If you're currently using Automated translations, consider migrating to Translations management. + +## Availability + +Translations management is an opt-in capability available as an [LTS Update](editions.md#lts-updates) for all [[= product_name =]] editions, starting with the v5.0.10 version. + +## How it works + +Before the translation flow can happen, an administrator sets up the translation providers and assigns language pairs to them. +Then, when an editor opens a content item or product and requests a new machine translation, the system resolves which provider to use. +If no language-pair rule matches, it falls back to the user's manual selection. +The system then extracts the translatable fields from the source language version of a content item and sends them to the configured provider's API. +The system writes the translated strings into a target-language draft of the content item or a target-language version of a product, and opens it in a side-by-side view for the editor to review and refine. +The editor can save the result of content item translation as a draft, share it with a reviewer or publish it. +Product translations are published when the editor closes the view without rejecting it. + +![Translations management flow for content item translation](translations_management_flow.png "Translations management flow for content item translation") + +## Capabilities + +### Translation provider management + +Administrators can manage translation providers and configure translation provider/language combination assignments ([language pairs](configure_translations_management.md#define-language-pairs)). +This allows administrators to define which provider handles which language combination. +Editors see the configured provider pre-selected when creating a new translation, but can override it if needed. + +![Creating a language pair](translations_management_language_pairs.png "Creating a language pair") + +The package provides integrations with several translation providers, including REST API-based services such as Google Translate and DeepL, and AI-powered services through the [AI Actions](ai_actions_guide.md). + +### Side-by-side translation view + +Translations management introduces a [side-by-side translation view]([[= user_doc =]]/content_management/translate_content/#side-by-side-translation-view) that displays the read-only source language content next to an editable target language form. +In this view, editors can provide and review translations in context, without having to leave the content editing interface. + +![Side-by-side translation view](managing_translations_sxs_view.png "Side-by-side translation view") + +Editors can: + +- access the side-by-side view when creating a new translation, reviewing an existing one, or editing a draft +- compare source and target content field by field while editing +- copy all content from the source column to the target column with a single action +- provide localized versions of media assets and their alternative text +- use the distraction-free mode for focused editing of individual fields, with AI actions available inline +- choose whether the source column appears on the left or right in user settings + +!!! note "Excluded content types" + + Content types that are editable in [Page builder](page_builder_guide.md) or [Form builder](form_builder_guide.md) are excluded from side-by-side editing. + + Products are editable in the side-by-side view, but [product attributes aren;t translatable](products.md#product-attributes). + +### Command-line translation + +The Translations management package exposes a [console command](translate_with_cli.md) for translating content items from the command line. +You can use it for batch processing or automated workflows. + +### Translation review + +When a draft translation of a content item or product is created by going through the automatic translation process in the back office, the system creates a review status record and marks the draft as "For review". +The console command bypasses this and drafts created with command-line translation aren't assigned a review status. +Editors can [accept or reject the translation]([[= user_doc =]]/content_management/translate_content/#review-automatic-translation) directly in the side-by-side view. +Accepted drafts are marked as "Translated". + +When the editor rejects the translation, the status doesn't change, but the system records that the draft translation required corrections for statistical purposes. +A draft translation in the "Translated" state can't be rejected anymore. + +The `ibexa_auto_translation_review` workflow is separate from the [editorial workflow](workflow.md). +Accepting or rejecting draft translations does not trigger editorial workflow transitions or notifications. + +!!! note "No review for human translations" + + Draft translations that were created by a human don't have a review status. + +### Extensibility + +Developers can [extend the translations management](extend_translations_management.md) package: + +- create custom translation providers +- add support for custom fields +- add custom content type exclusion rules +- tap into the translation lifecycle with [events](translations_management_events.md) diff --git a/docs/release_notes/ibexa_dxp_v5.0.md b/docs/release_notes/ibexa_dxp_v5.0.md index 3653de279ec..9ebda244759 100644 --- a/docs/release_notes/ibexa_dxp_v5.0.md +++ b/docs/release_notes/ibexa_dxp_v5.0.md @@ -102,24 +102,13 @@ The following additions were made to the PHP API: ['Headless', 'Experience', 'Commerce', 'LTS Update', 'New feature', 'First release'] ) =]] -MCP servers make it easier for AI agents to discover the available interactions with [[= product_name =]]. +MCP servers make it easier for AI agents to discover the available interactions with Ibexa DXP. With the MCP Servers feature, you can configure multiple MCP servers with their specific sets of tools. For more information, see [MCP Servers product guide](https://doc.ibexa.co/en/5.0/ai/mcp/mcp_guide/). [[= release_note_entry_end() =]] -[[= release_note_entry_begin( - product_name + ' ' + version, - date, - ['Headless', 'Experience', 'Commerce', 'New feature'] -) =]] - -### Security - -This release includes security fixes. -To learn more, see the [corresponding security advisory](https://developers.ibexa.co/security-advisories/ibexa-sa-2026-003-vulnerabilities-in-forms-submissions-rest-sessions-and-solr-logs). - ### Raptor connector #### New recommendation blocks [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] @@ -151,7 +140,7 @@ For more information, see [connector installation and configuration](https://doc ### Anonymous user segmentation in [[= product_name_cdp =]] [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] [[= product_name_cdp =]] can now build audiences for anonymous visitors. -Use them in [[= product_name =]] to deliver personalized experiences even before users log in. +Use them in Ibexa DXP to deliver personalized experiences even before users log in. For more information, see [Anonymous user segmentation](https://doc.ibexa.co/en/5.0/cdp/cdp_activation/cdp_configuration/#anonymous-user-segmentation). @@ -237,7 +226,7 @@ For more information, see how to [install and configure the Google Gemini connec ### Product tour -The product tour is a new Integrated help feature that helps back office contributors to discover [[= product_name =]]. +The product tour is a new Integrated help feature that helps back office contributors to discover Ibexa DXP. With product tours, you can create customized onboarding journeys. This accelerates user adoption, reduces training time, and helps users confidently navigate the platform. @@ -259,7 +248,7 @@ To learn more, see the [corresponding security advisory](https://developers.ibex ### Raptor connector -The Raptor connector provides a seamless integration between [[= product_name =]] and [Raptor Recommendation Engine](https://www.raptorservices.com/website-recommendations/). +The Raptor connector provides a seamless integration between Ibexa DXP and [Raptor Recommendation Engine](https://www.raptorservices.com/website-recommendations/). For more information, see [Raptor connector](https://doc.ibexa.co/en/5.0/recommendations/raptor_integration/raptor_connector/). @@ -280,10 +269,10 @@ For more information about Recommendation blocks in Page Builder, see the releva ### [[= pim_product_name =]] PIM -The [[= pim_product_name =]] integration add-on allows you to connect [[= product_name =]] with [[[= pim_product_name =]] Product Information Management (PIM)](https://www.quable.com/en), making [[= pim_product_name =]] the authoritative source of product information for every website powered by [[= product_name =]]. +The [[= pim_product_name =]] integration add-on allows you to connect Ibexa DXP with [[[= pim_product_name =]] Product Information Management (PIM)](https://www.quable.com/en), making [[= pim_product_name =]] the authoritative source of product information for every website powered by Ibexa DXP. [[= pim_product_name =]] can serve as the single source of truth for all product data, including attributes, classifications, variants, and translations. -[[= product_name =]] consumes this data and makes it available for use in content and digital experiences. +Ibexa DXP consumes this data and makes it available for use in content and digital experiences. For more information, see [Quable PIM Integration](https://doc.ibexa.co/en/5.0/product_catalog/quable/quable/). @@ -297,7 +286,7 @@ You can now use the [refining text AI Actions](https://doc.ibexa.co/en/5.0/ai/ai Symfony is upgraded from 7.3 to 7.4. It's the latest [LTS release](https://symfony.com/releases#long-term-support-release), maintained till November 2029. -See [what's new in Symfony 7.4](https://symfony.com/blog/category/living-on-the-edge/8.0-7.4) and [how to update Symfony within [[= product_name =]]](https://doc.ibexa.co/en/5.0/update_and_migration/from_5.0/update_from_5.0/#update-symfony-from-73-to-74). +See [what's new in Symfony 7.4](https://symfony.com/blog/category/living-on-the-edge/8.0-7.4) and [how to update Symfony within Ibexa DXP](https://doc.ibexa.co/en/5.0/update_and_migration/from_5.0/update_from_5.0/#update-symfony-from-73-to-74). #### Taxonomy search @@ -540,7 +529,7 @@ The [Collaborative editing](https://doc.ibexa.co/en/5.0/content_management/colla [[= release_note_entry_begin("Integrated help " + version, '2025-12-10', ['Headless', 'Experience', 'Commerce', 'LTS Update', 'New feature', 'First release']) =]] -Integrated help brings contextual documentation, guidance, and partner-specific resources right into the user interface of [[= product_name =]]. +Integrated help brings contextual documentation, guidance, and partner-specific resources right into the user interface of Ibexa DXP. It helps editors, store managers, and developers to quickly access relevant content, training and resources without leaving the UI, narrowing the gap between product and documentation. The default help menu can be modified to include links to internal editorial guidelines, custom tutorials, or support pages. @@ -573,7 +562,7 @@ Real-time editing is now part of the [Collaborative editing](https://doc.ibexa.c By using it, users can edit and review content in real time, making teamwork faster, more efficient, and streamlining the content review process. The system automatically tracks changes, allowing seamless collaboration within a single content item. -This extends the already existing capabilities allowing editors to work on the same content created in [[= product_name =]] simultaneously, streamlining the content creation and review process. +This extends the already existing capabilities allowing editors to work on the same content created in Ibexa DXP simultaneously, streamlining the content creation and review process. ![Participants list](img/participants_list.png) @@ -727,7 +716,7 @@ Additionally, shared drafts can be accessed and managed through new dashboard ta ### Discount indexing Discounts now allow scheduling a re-indexing of discounted product catalog prices at the most convenient time by using the Ibexa Messenger package. -Ibexa Messenger is a customization of the Symfony Messenger package, created to adjust it to [[= product_name =]]'s needs. +Ibexa Messenger is a customization of the Symfony Messenger package, created to adjust it to Ibexa DXP's needs. Once properly configured, it uses a background queue to trigger price re-indexing, ensuring efficient use of system resources without causing performance disruptions. @@ -858,7 +847,7 @@ It uses the [special characters plugin](https://ckeditor.com/docs/ckeditor5/late ### Support for Solr 9 -With this release, [[= product_name =]] starts supporting [Solr 9](https://doc.ibexa.co/en/5.0/getting_started/requirements/#search). +With this release, Ibexa DXP starts supporting [Solr 9](https://doc.ibexa.co/en/5.0/getting_started/requirements/#search). Solr 9 comes with support for [Dense Vector Search](https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html), paving the way for incoming improvements to the [AI Actions](https://doc.ibexa.co/en/5.0/ai/ai_actions/ai_actions/) feature. @@ -904,7 +893,7 @@ This version incorporates into the product numerous features brought by LTS Upda #### AI Actions -The AI Actions feature enhances the usability and flexibility of [[= product_name =]] by harnessing the potential of artificial intelligence to automate time-consuming editorial tasks. +The AI Actions feature enhances the usability and flexibility of Ibexa DXP by harnessing the potential of artificial intelligence to automate time-consuming editorial tasks. By default, the AI Actions feature can help users with their work in following scenarios: - Refining text: when editing a content item, users can request that a passage selected in online editor is modified, for example, by adjusting the length of the text, changing its tone, or correcting linguistic errors @@ -957,17 +946,17 @@ For a full list of updated system requirements, see [Requirements](https://doc.i #### Symfony 7.3 -With this release, [[= product_name =]] moves to Symfony 7.3 from the previously used versions of Symfony. +With this release, Ibexa DXP moves to Symfony 7.3 from the previously used versions of Symfony. For details, see [Symfony 7.3](https://symfony.com/blog/symfony-7-3-curated-new-features). #### Doctrine DBAL 3.9 -By moving to Doctrine DBAL 3.9, [[= product_name =]] brings developers better performance, cleaner code, and stronger foundation for a more modern and maintainable application. +By moving to Doctrine DBAL 3.9, Ibexa DXP brings developers better performance, cleaner code, and stronger foundation for a more modern and maintainable application. #### PHP 8.3 -With performance, coding safety and security in mind, with this version, [[= product_name =]] moves to [PHP 8.3](https://www.php.net/releases/8.3/en.php) and drops support for lower versions of the language. +With performance, coding safety and security in mind, with this version, Ibexa DXP moves to [PHP 8.3](https://www.php.net/releases/8.3/en.php) and drops support for lower versions of the language. #### OpenAPI support @@ -979,14 +968,14 @@ Support for serialization and deserialization of REST payloads with the [Symfony #### React 19 -[[= product_name =]]'s Back Office now uses [React 19](https://react.dev/blog/2024/12/05/react-19). +Ibexa DXP's Back Office now uses [React 19](https://react.dev/blog/2024/12/05/react-19). This upgrade enhances maintainability, unlocks new UI capabilities, and simplifies future feature development. ### Developer experience #### New packages -The following packages have been introduced in [[= product_name =]] v5.0.0: +The following packages have been introduced in Ibexa DXP v5.0.0: - ibexa/collaboration - ibexa/connector-ai @@ -999,7 +988,7 @@ The following packages have been introduced in [[= product_name =]] v5.0.0: #### REST APIs -[[= product_name =]] v5.0.0 adds REST API coverage for the following features: +Ibexa DXP v5.0.0 adds REST API coverage for the following features: - AI Actions: - Action Configurations diff --git a/mkdocs.yml b/mkdocs.yml index faa25a6006e..c3bfa0bd78f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -110,6 +110,7 @@ nav: - Discounts events: api/event_reference/discounts_events.md - Collaboration events: api/event_reference/collaboration_events.md - Integrated help events: api/event_reference/integrated_help_events.md + - Translations management events: api/event_reference/translations_management_events.md - Other events: api/event_reference/other_events.md - Notification channels: api/notification_channels.md - Administration: @@ -484,6 +485,12 @@ nav: - Language API: multisite/languages/language_api.md - Back office translations: multisite/languages/back_office_translations.md - Automated content translation: multisite/languages/automated_translations.md + - Translations management: + - Translations management: multisite/translations_management/translations_management.md + - Translations management guide: multisite/translations_management/translations_management_guide.md + - Configure translations management: multisite/translations_management/configure_translations_management.md + - Translate content items with CLI: multisite/translations_management/translate_with_cli.md + - Extend translations management: multisite/translations_management/extend_translations_management.md - Permissions: - Permissions: permissions/permissions.md - Permission overview: permissions/permission_overview.md