The Translate tab never appears on a status, workflow or tenant, because their edit forms declare no local task
Fixes #3624957.
Config translation was never broken for these entities. The schema types each label as translatable, config_translation builds a mapper for each type, the overview route exists, and saving a French override makes the entity answer in French:
language.fr.orchestra.status.under_review: label = "En cours de revue"
$status->label() (fr config override language) -> En cours de revueWhat was missing was the way in. Core derives one local task per mapper with the entity's edit form as its base_route (ConfigTranslationLocalTasks::getDerivativeDefinitions()), and that derivative is present — config_translation.local_tasks:entity.orchestra_status.config_translation_overview is in the definition list. But a tab bar only renders when the base route itself carries a local task, the one that becomes the default tab. Core entity types declare it; these did not, so the derived tab had nowhere to appear.
Before: getLocalTasksForRoute('entity.orchestra_status.edit_form') returned []. After: three tabs each, the Translate tab included — plus the devel and entity_clone tabs that were also being derived with nowhere to render.
Placement follows route ownership
| Entity | Route owner | Declaration |
|---|---|---|
orchestra_tenant |
the engine (orchestra.routing.yml) |
orchestra.links.task.yml |
orchestra_workflow |
the modeler | orchestra.links.task.yml, beside the four tabs already there and with the same "renders nowhere without a modeler" consequence |
orchestra_status |
orchestra_ui |
orchestra_ui.links.task.yml |
The status is headless in the engine on purpose — OrchestraUiHooks::entityTypeAlter() attaches its forms, list builder and link templates — so declaring its tab beside an entity that ships without a UI would point at a route that does not exist on a headless site.
orchestra_remote is deliberately excluded: its label is an operator's name for a server they configured, read only in the remote form, two status messages and the remote list.
Test
StatusAdminRouteTest is widened rather than joined by a new class: it already installs config_translation and asserts the translate route exists, so asserting the tab reaches it is the same rule one step further.
Proven to bite by mutation:
with the fix 1/1 classes passed
tab declaration removed 0/1 classes passed
restored 1/1 classes passed