Problem/Motivation
Drupal\views_bulk_operations\Service\ViewsBulkOperationsActionManager extends core's ActionManager but registers its own plugin manager service (plugin.manager.views_bulk_operations_action) with its own discovery cache bin (views_bulk_operations_action_info). Neither the bin name nor the parent constructor call carries a language key.
Action plugin derivatives whose labels resolve from translatable entity data ? e.g. Drupal\ai_automators\Plugin\Action\RunAutomatorActionDeriver from drupal/ai which builds t('AI: @label', ['@label' => $automator->label()]) ? therefore bake whichever language warmed the cache first. Editors on a German interface continue to see initial cached version instead of per-language override in the VBO bulk-actions dropdown.
This is the same anti-pattern that d.o 3589007 (core ActionManager) and d.o 3038717 (BlockManager) address. Fixing core's plugin.manager.action alone does not propagate to VBO because VBO has a separate service instance and explicitly overrides the cache bin in its constructor.
Steps to reproduce
- Multilingual site (e.g. core
language+content_translation+ a non-default UI language). - Define a VBO action with a label resolved from a translatable entity. Easiest reproduction: drupal/ai 1.3.x+ with an
ai_automatorwhoseworker_typeisbulkand that has a config-language override on itslabel. - Place a VBO views_bulk_operations bulk-form field on
/admin/content. - Hit
/admin/contentin English to warm the cache, then switch to/de/admin/content. - Observed: VBO dropdown shows the entity-derived suffix in English. Expected: the suffix resolves in the current interface language.
Proposed resolution
Apply the same pattern d.o 3589007 applies to core ActionManager, directly on ViewsBulkOperationsActionManager so the fix lands independent of core. The patch is self-contained ? it does not require core 3589007 to be merged first:
- Inject
@language_manageras an additional autowired constructor argument. - Append
:langcodeto the cache key passed tosetCacheBackend(). - Override
clearCachedDefinitions()to walk every installed language and clear all per-language buckets together (model:LocalActionManager::clearCachedDefinitions()).
When core 3589007 lands, this contribution can be simplified later (forward @language_manager to parent::__construct(), drop the local property), but no change here is blocked on core.
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #6 | 3589765-vbo-actionmanager-language-aware-4.4.x.patch | 3.63 KB | petar_basic |
| #4 | 3589765-vbo-actionmanager-language-aware-4.4.x.patch | 2.84 KB | petar_basic |
Issue fork views_bulk_operations-3589765
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
petar_basic commentedComment #4
petar_basic commentedApproach is self-contained, doesn't depend on core d.o 3589007 (MR !15745):
- Adds @language_manager as an autowired constructor arg (defaulted to NULL for BC).
- Appends :langcode to the discovery cache bin so each interface language gets its own bucket.
- Overrides clearCachedDefinitions() to walk all installed languages and clear every per-language bucket together (mirrors LocalActionManager).
Adding a patch for use with composer patches.
Comment #5
graber commentedAdded a comment on the MR.
Comment #6
petar_basic commentedAddressed review:
to the parent
ActionManagerfor the standard core BC/deprecation cycle. Child's type must be invariant withparent's.
::class— doneTwo phpstan-baseline entries added with explanatory links:
$this->definitions = NULL(same pattern core itself uses inDefaultPluginManager::clearCachedDefinitions()) tripsassign.propertyTypebecause the inherited
DiscoveryCachedTrait::$definitions@varsaysarray. Pending d.o3590805 (docblock fix).
parent::__construct(..., $languageManager)is a 4-arg call against stock core's 3-argActionManager::__construct; the 4th arg is silently dropped at runtime on stock core and consumed by the new constructor signature once d.o 3589007 lands. Pending that MR.Comment #7
fago> The related core MR !15745 / d.o 3589007 adds protected ?LanguageManagerInterface $languageManager = NULL to the parent ActionManager for the standard core BC/deprecation cycle.
I'd not see why are we doing that, the service is always available. Let's discuss and clarify there and take over the solution here then.