Draft: Refactor entity translation into a shared orchestrator service
Summary
This MR starts the ai_translate refactoring needed to support a shared translation workflow across multiple entrypoints.
It introduces a shared EntityTranslationOrchestrator service and moves duplicated entity-translation logic out of the UI controller and Drush command into that service. The goal is to establish one reusable path for entity translation instead of maintaining separate orchestration in each entrypoint.
What changed
Added:
EntityTranslationOrchestratorInterfaceEntityTranslationOrchestratorEntityTranslationResult
Updated:
src/Controller/AiTranslateController.phpsrc/Drush/AiTranslateCommands.phpai_translate.services.yml
Also added test coverage:
tests/src/Unit/EntityTranslationOrchestratorTest.phptests/src/Kernel/EntityTranslationOrchestratorKernelTest.php
And added:
composer.jsonphpunit.bootstrap.phpphpunit.xml
The PHPUnit bootstrap/config is needed because the default Drupal test bootstrap can resolve Drupal\ai_translate\... to the bundled ai_translate copy inside drupal/ai instead of this project checkout. These files ensure tests run against the code under review.
Behavior / parity notes
This first draft keeps a few cleanup improvements instead of preserving legacy behavior 1:1.
Drush field failure handling
Previously, Drush could partially translate a field if one column failed partway through. Now, failed fields are skipped rather than partially translated.
Drush now respects translation_status
Previously, the draft/unpublished translation-status behavior was only applied in the UI flow. Now, Drush uses the same shared save logic and also respects that configuration.
Drush messaging changed
Previously, Drush emitted generic translation errors during field/column failures. Now, successful entity saves still report success, with warnings when some fields could not be translated.
Language edge cases
Language handling is now more explicit in the shared orchestrator, so invalid/unknown language edge cases may behave slightly differently than before. For this first review draft, I am intentionally keeping these cleanups unless reviewers prefer Phase 1 to preserve behavior 1:1.
Testing
Tested locally with:
ddev phpunit tests/src/Unit/EntityTranslationOrchestratorTest.phpddev phpunit tests/src/Kernel/EntityTranslationOrchestratorKernelTest.phpBoth pass.