Fixed
Project:
Display Builder
Version:
1.0.x-dev
Component:
Main / Misc.
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
29 Oct 2025 at 21:48 UTC
Updated:
20 Sep 2026 at 11:16 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #3
pdureau commentedWork has started
Comment #4
pdureau commentedThis is a beta2 target, there was some hope of doing in for beta1, but we need to follow the flow of #3548884: SourceValueItem field synchronized translations so let's take some more time and target beta2 as initially planned.
Comment #5
pdureau commentedComment #7
lauriiiWhat are you planning to use for determining which props should be translated? Asking because it might be good for us to coordinate on this so that we could provide a good experience for people who are using both Canvas and Display Builder.
Comment #8
pdureau commentedHi Laurii,
Our logic (which is not really the current MR, we didn't push yet the current work which is very promising) is not based in props, it is based on our data source plugin type:
I believe it would be a mistake to think about "translatable props":
What do you think about that?
Comment #9
lauriiiI'm trying to understand how are you determining what becomes available for translators in In
ConfigTranslationAddFormandConfigTranslationEditForm. Are you for example going to allow translating all data usingTextfieldWidgetor is there a selection that happens at a certain point by someone to determine what gets translated? Or are you making everything available for translation and letting the translator decide which data should be translated?Comment #10
pdureau commentedInstead of a
ComponentTreelike Canvas, we have aSourceTree, we nest data sources and each source plugin has its own config schema. So, by nesting the source plugins, we have a big config schema tree and we can extract the translatable strings.This abstraction is the core of our architecture since the start of UI Patterns 2 development in June 2023 .
Such selection is not necessary.
Comment #11
christian.wiedemann commentedComment #12
pdureau commentedComment #13
pdureau commentedSince beta2, we have a fake, temporary, implementation of
ContentEntityInterface, so ofTranslatableInterface, inDrupal\display_builder\Entity\ContentEntityBaseWe are still hesitating:
\Drupal\Core\Entity\ContentEntityBaseas a base class and adapt the custom logic inInstanceandInstanceStorageonly ?Anyway, even if we are focusing on tarnslatabilitty of the "providers" (integration with Drupal API) for now, the addition of this intreface may interest you.
Comment #14
pdureau commentedMoved to beta4 because we will merge #3549266: Move DisplayBuildableInterface to a new plugin type first.
Comment #15
pdureau commented#3549266: Move DisplayBuildableInterface to a new plugin type has been merged!
Comment #16
pdureau commentedInstance entities are now using
\Drupal\Core\Entity\ContentEntityBaseand are currently adopting the Field API to store the data (see #3562989: Implements RevisionLogInterface for Instance entity).Once done, we will be able to easily use the work from UI Patterns (#3548884: SourceValueItem field synchronized translations).
Comment #17
pdureau commentedComment #18
pdureau commentedComment #23
pdureau commentedI will resquash and do a review.
However, the priority is the UI Patterns ticket for now: #3548884: SourceValueItem field synchronized translations
Comment #26
pdureau commentedSquashed, rebased & linted.
We are back to 3555110-symmetric-config-translation-cw and this MR: https://git.drupalcode.org/project/display_builder/-/merge_requests/273
383 additional PHP LOC (tests excluded), that's lean for such a valuable feature 👍
My understanding:
Instance entity
A new method:
InstanceInterface::ensureCurrentTranslation(): InstanceInterface, called from 2 places only:InstanceTranslationSubscriberlisteningKernelEvents::REQUEST.ProfileViewBuilderAs said in a note, I am surprised such mechanism is needed, i was expecting the Entity API to do all the work.
Buildable plugins
A new method:
DisplayBuildableInterface::isTranslationSynchronized(): boolFor
EntityViewOverride: a very simple implementation, because we use the same field type in Instance entity and in overrides.For
EntityView,PageLayoutandViewDisplay, the logic is more complex and implemented in the newConfigBuildablePluginBase. This is where most of the logic happen and will be the main part of the review.Island plugins
A new "Language" col in the logs panel
Do we also display the current language in the Controls panel?
Comment #27
pdureau commented(removing
third_party_settingsin all examples for clarity)Page Layout
1. Just translating without messing with the sources tree
Test:
/test-translationspage with:Results:
It may be an issue not related to the current work, which may need its dedicated ticket, so let's try again with
/user/*page condition instead of/test-translation.Results:
Technical analysis
Just a guess, but the error may come from us not following config translations expectations with the addition of a translations property which is too specific and (obviously) not present in the original language:
I guess we don't need to mess with the default config translation storage: just a skeleton tree with only the translated values. So:
Then, Drupal Core APIs can do the merge deep they usually do at config load to get the full translated display. The
ConfigBuildablePluginBaseis translating between the UI Patterns source field storage and the "normal" config translation storage, in both direction.Is it possible ? Will it simplify our logic ?
2. Moving stuff around in the translated language
Later
3. Changing a non translatable value from original language
Later
Entity View, Entity View Overrides, View Display
Later.
Comment #28
pdureau commentedComment #29
pdureau commentedDiscussed with Christian.
We will create a new branch to start a new proposal following the philosophy which was successful UI Patterns side: we don't mess with Drupal storage
That means config translation storage will be stored the normal way: symmetric and positional. And Display Builder will not intervene when the config is loaded by Drupal in read-only.
Like in UI Patterns with SourceItem and SourceItemList, we will run our logic when we load for display building tasks and when we publish from display builder. So, in the display buildable plugins, i guess :)
Comment #31
christian.wiedemann commentedComment #32
pdureau commentedComment #34
pdureau commentedOnly 186 PHP LOC added, so lean :)
Code review
Instance entity
A new method:
InstanceInterface::ensureCurrentTranslation(): InstanceInterface, called from 2 places only:InstanceTranslationSubscriberlisteningKernelEvents::REQUEST.ProfileViewBuilder⚠️ As said before, I am surprised such mechanism is needed, i was expecting the Entity API to do all the work. Can we move the logic to an override of the "normal" entity operations (load? save?) and remove the public method?
⚠️ Also,
Instance::getBuildablePlugin()is now public, but called only from tests. It would be better to keep it protected.Buildable plugins
A new abstract
ConfigBuildablePluginBaseclass with the shared translation logic and those additional public methods:⚠️ Here again, those methods are only called from tests, so no need to expand the API scope. It would be better to keep them protected.
ConfigBuildablePluginBaseis extended byEntityView,PageLayoutandViewDisplay.For
EntityViewOverride: a very simple implementation, because we use the same field type in Instance entity and in overrides.⚠️
DisplayBuildableInterface::isTranslationSynchronized()has a single implementation always returning TRUE and never called outside some tests. It can be removed.Island plugins
A new "Language" col in the logs panel.
Do we also display the current language in the Controls panel?
Page Layout
Tested with UI Patterns'
3548884-trans-make-sourcevalueitem-v2 branch(so before the modification of Mickael)1. Just translating without messing with the sources tree
Test:
/test-translationspage with:Results:
Let's try again with an existing page (
/user/*) instead:2. Moving stuff around in the translated language
❌ I go back to https://ui-patterns-2.ddev.site/fr/admin/structure/page-layout/test_tran... and I have:
So, I renamed
ComponentTreetoSourceTree.I go back to https://ui-patterns-2.ddev.site/fr/admin/structure/page-layout/test_tran... and I reorder tteh root level, movinf the second source at the first position.
3. Changing a non translatable value from original language
I change the button component variant in the English translation. Results:
Entity View, Entity View Overrides, View Display
Later. Let's address the first round of feedback first.
Comment #35
mogtofu33 commentedComment #36
mogtofu33 commentedComment #37
pdureau commentedUpdated review.
Code review
Instance entity
A new method:
InstanceInterface::ensureCurrentTranslation(): InstanceInterface, called from 2 places only:InstanceTranslationSubscriberlisteningKernelEvents::REQUEST.ProfileViewBuilder⚠️ As said before, I am surprised such mechanism is needed, i was expecting the Entity API to do all the work. Can we move the logic to an override of the "normal" entity operations (load? save?) and remove the public method?
Buildable plugins
A new abstract
ConfigBuildablePluginBaseclass with the shared translation logic, extended byEntityView,PageLayoutandViewDisplay. ForEntityViewOverride: a very simple implementation, because we use the same field type in Instance entity and in overrides.All this is OK.
Island plugins
A new "Language" col in the logs panel.
Do we also display the current language in the Control buttons island?
Page Layout
Tested with UI Patterns 2.0.19 this time.
1. Just translating without messing with the sources tree
Test:
/test-translationspage with:Results:
Let's try again with an existing page (
/user/*) instead:2. Moving stuff around in the translated language
I go back to https://ui-patterns-2.ddev.site/fr/admin/structure/page-layout/test_tran... and I reorder the root level, moving the second source at the first position.
3. Changing a non translatable value from original language
⚠️ The published state is not shown in LogsPanel. We may need to adapt the calculation of it. We may need #3595491: Move hash to permanent storage.
I change the button component variant in the English translation. Results:
Entity View, Entity View Overrides, View Display
Later. Let's address the first round of feedback first.
Comment #38
mogtofu33 commentedComment #39
pdureau commentedPrevious feedbacks
According to Christian, it is needed because it need to be executed in every route of the API, including the HTMX ones.
Managing the 404 pages is not in the scope of the translation system.
Fixed.
Fixed (it was the same issue)
It will be a follow-up.
Today feedback
We have automated both:
Without the need of manually adding the translation with Config Translation UI.
It is weird but absolutely needed for Entity View Displays config entity because they don't have their own Config Translation UI pages in Core: #2546212: Entity view/form mode formatter/widget settings have no translation UI
Because it was added for this buildable, it is available for all buidables.
Comment #40
pdureau commentedAlso,
DisplayBuildablePluginBase::getTranslationLangcode()must not be a public method.Comment #41
pdureau commentedWe have some playwright fails but, good news, it is on the test we have added:
multilingual.spec.tshttps://issue.pages.drupalcode.org/-/display_builder-3555110/-/jobs/1161...
Comment #42
pdureau commentedThe playwright fails have changed a bit: https://project.pages.drupalcode.org/-/display_builder/-/jobs/11658706/a...
Comment #43
pdureau commentedI will review again, add documentation and maybe add a little Language Switcher island plugin.
Comment #44
pdureau commentedUpdated review.
Tested with UI Patterns 2.0.20.
Config storage
They share most of the logic in
ConfigBuildablePluginBase, and they have a specificity: Both the creation of the Instance content entity translation (by just opening the Display Builder UI), and the creation of the targeted config entity translation (by just "publishing" the instance revision) are automated, without the need of manually adding the translation with Config Translation UI.It is weird but absolutely needed for Entity View Displays config entity because they don't have their own Config Translation UI pages in Core: #2546212: Entity view/form mode formatter/widget settings have no translation UI Because it was added for this buildable, it is available for all buidables.
Page Layout
Entity View
View Display
Content storage
Entity View Overrides
Conclusion
We are close to RTBC in m humble opinion. There is currently a phpunit fails but not related to the current work.
TODO before commiting:
/node/{nid]/translationsand that's a good thing here): returning a 404 from the display builder UI controller would be enough for nowCan be done in follow-ups:
InstanceInterface::ensureCurrentTranslation(): InstanceInterfaceand the "forcing" of the URL based language detection (see former comment)Comment #45
pdureau commentedI have hope adding Language Switcher island plugin and a few related methods in
DisplayBuildableInterfacecan help us:I will give a try.
Comment #46
pdureau commentedDONE:
/node/{nid]/translationsand that's a good thing here): returning a 404 from the display builder UI controller would be enough for nowTODO before commiting:
Comment #47
pdureau commentedFixed! The fix was very hard to guess but so easy to do: https://git.drupalcode.org/project/display_builder/-/merge_requests/311/...
Page Layout
Entity View
View Display
Entity View Overrides
Ready for review, IMHO.
Follow-ups proposals:
InstanceInterface::ensureCurrentTranslation(): InstanceInterfaceand the "forcing" of UR- based language detection isnetad of the configurable one from CoreComment #48
pdureau commentedI will rebase before sending to review again
Comment #49
pdureau commentedRebased. And documentation added.
Comment #51
mogtofu33 commentedCould you help with a rebase here please.
Comment #52
pdureau commentedSure. I did the rebase, it went well, but we reached the PHPMD class complexity limit for ViewDisplay & DisplayBuildablePluginBase. I will have a look.
Comment #53
pdureau commentedWill be addressed here: #3621008: Move DisplayBuildable form logic to a dedicated clas
Comment #54
mogtofu33 commentedComment #55
pdureau commentedRebased
Comment #57
mogtofu33 commentedComment #58
pdureau commentedReview of the new MR in progress.
Here is the diff for reference: https://git.drupalcode.org/issue/display_builder-3555110/-/compare/35551... with thosechanges:
::loadDefaultSources()removed in some buildable plugins, to address: "Publishing from a non-default language writes its config overrides into default config"Instance::getHash()not callingInstance::get('hash')anymore to address: "Saving a translation marks the other languages as unpublished"First feedback:
drupal:languagehas a dependency indisplay_builder.info.yml? Is the module still working without such dependency? Not all websites using Display Builder will want to activate the language module.Instance::getHash()is not callingInstance::get('hash')anymore but calculating "on the fly", so:I will do a little functional check soon.
Page Layout:
Entity View:
View Display:
Entity View Overrides
Comment #59
mogtofu33 commentedThanks for the review.
drupal:language dependency
Right, not needed, removed.
Instance hash
You were right on both points. Computing it on the fly was not negligible: the first read of
sourceson a loaded entity costs ~2ms, and the history panel reads one per revision (~30ms per builder render, on every site).So the field stays, but is now translatable and recomputed for every translation in
InstanceStorage::doPreSave(), after theui_patterns_sourcefield has cascaded structural edits between translations.getHash()reads the stored value again.Update path:
display_builder_update_11105()makes the field translatable,display_builder_post_update_9()resaves the instances having a translation to recompute their hashes.Comment #60
mogtofu33 commentedI keep it in review even if I will add a little fix and a Playwright test for the functional part.
Comment #61
mogtofu33 commentedSeems more work than expected, let not waste review time until ready.
Comment #62
mogtofu33 commentedRebased, new Playwright tests to cover multilingual, looks good to review.
Comment #63
pdureau commentedPage Layout:
Entity View:
View Display:
Entity View Overrides
The only issue I have found is related to UI, so easy to fix. The language switcher button is looking a bit out of place in the toolbar, with a border in darker color not used anywhere else:

Is there a specific reason for that? If not, the change could be as easy as:
We have a few little UI inconsistencies like that (just UI, not UX, so small stuff like: color, font, spacing, align...). I will create an issue to address them, but it would be nice to address this specific one here.
Once fixed, you can consider this ticket as RTBC
Follow-ups proposals:
InstanceInterface::ensureCurrentTranslation(): InstanceInterfaceand the "forcing" of UR- based language detection instead of the configurable one from CoreComment #64
mogtofu33 commentedThe language switcher is different because of the consequence of what it does. The main risk with this button is: I edited the French layout thinking I was in English. So I treated it differently.
This requires a rational approach and not personal opinions. I was wrong about the color, so let's challenge that based on UX guidelines we can find, even if our editing mode is a bit specific and most sources talk about front-end user language switchers. There is surely useful information to make a sound choice here.
Sources:
The first three agree on one high-emphasis button per view, or per container in a complex layout: emphasis should encode consequence, and the toolbar's only accent belongs to Publish. The last three cover the switcher itself: a quiet, persistent control, each language named in its own language.
Based on that, what has been done:
getNativeLanguages(), instead of uppercase codes. The trigger keeps the short code.Comment #66
mogtofu33 commentedLast changes are the button with back to neutral and then some changes on selection based on my previous comment.
I merge and fix this, could have a follow up for the button if required.