Problem/Motivation

UI Patterns is currently building a symmetric translation system for content storage only: #3548884: [trans] Make SourceValueItem field type translatable with synchronized and asynchron translations where:

  • translations are flat list of translatable sources with their tree node IDs
  • we rebuild the translated tree by overriding tree nodes values with translated values

On Display Builder side, we will leverage this feature and we need to do the same for our config entities: Page Layout, Views & Entity View Display.

Proposed resolution

  1. Make source property translatable
  2. Add a way of retrieving all nodes with translatable source plugins
  3. In ConfigTranslationAddForm and ConfigTranslationEditForm, expose those translatable source with the UI Patterns Form Builder (the final goal is to translate from Display Builder UI, but this will be a post-beta 1 target)
  4. Make it store a flat list of nodes and build the full translated field when retrieving the value
Command icon 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

pdureau created an issue. See original summary.

pdureau’s picture

Status: Active » Needs work

Work has started

pdureau’s picture

This is a beta2 target, there was some hope of doing in for beta1, but we need to follow the flow of #3548884: [trans] Make SourceValueItem field type translatable with synchronized and asynchron translations so let's take some more time and target beta2 as initially planned.

pdureau’s picture

christian.wiedemann made their first commit to this issue’s fork.

lauriii’s picture

What 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.

pdureau’s picture

Hi Laurii,

What 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.

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:

  • Prop types (what you call "Prop shapes" in Canvas) are guessed from props schema
  • Data source plugins are targeting prop types
  • Data source plugins have a different data model than the prop shape they are targeting. Sometimes it happens there is a one-to-one mapping between the two models, but it is not the rule.
  • Data source plugins data model is using config schema API so can have translatable strings here. This is where translation is managed.

I believe it would be a mistake to think about "translatable props":

  • this is not part of JSON schema AFAIK
  • this is not part of UI modeling and it is not up to the component authors to care about that
  • this is a mechanism related to the CMS application state, so out of SDC

What do you think about that?

lauriii’s picture

I'm trying to understand how are you determining what becomes available for translators in In ConfigTranslationAddForm and ConfigTranslationEditForm. Are you for example going to allow translating all data using TextfieldWidget or 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?

pdureau’s picture

Instead of a ComponentTree like Canvas, we have a SourceTree, 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 .

Is there a selection that happens at a certain point by someone to determine what gets translated?

Such selection is not necessary.

christian.wiedemann’s picture

pdureau’s picture

pdureau’s picture

Since beta2, we have a fake, temporary, implementation of ContentEntityInterface, so of TranslatableInterface, in Drupal\display_builder\Entity\ContentEntityBase

We are still hesitating:

  • do we keep this file and fill it with our own custom implementations of the expected interfaces (see also #3562989: Implements RevisionLogInterface for Instance entity) ?
  • do we adopt the real \Drupal\Core\Entity\ContentEntityBase as a base class and adapt the custom logic in Instance and InstanceStorage only ?

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.

pdureau’s picture

pdureau’s picture

pdureau’s picture

Instance entities are now using \Drupal\Core\Entity\ContentEntityBase and 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: [trans] Make SourceValueItem field type translatable with synchronized and asynchron translations).

pdureau’s picture

Title: Symmetric config translation » Symmetric translation
pdureau’s picture

pdureau changed the visibility of the branch 3555110-symmetric-config-translation to hidden.

pdureau changed the visibility of the branch 3555110-symmetric-config-translation-cw to hidden.

pdureau’s picture

Assigned: christian.wiedemann » pdureau
Status: Needs work » Needs review

I will resquash and do a review.

However, the priority is the UI Patterns ticket for now: #3548884: [trans] Make SourceValueItem field type translatable with synchronized and asynchron translations

pdureau changed the visibility of the branch 3555110-symmetric-config-translation-cw to active.

pdureau changed the visibility of the branch 3555110-symmetric-config-translation-cw-squash to hidden.

pdureau’s picture

Squashed, 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:

  • the new InstanceTranslationSubscriber listening KernelEvents::REQUEST.
  • ProfileViewBuilder

As 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(): bool

For EntityViewOverride: a very simple implementation, because we use the same field type in Instance entity and in overrides.

For EntityView, PageLayout and ViewDisplay, the logic is more complex and implemented in the new ConfigBuildablePluginBase. 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?

pdureau’s picture

(removing third_party_settings in all examples for clarity)

Page Layout

1. Just translating without messing with the sources tree

Test:

  1. I have 2 languages: English (default) and French.
  2. Create a new page layout in English for /test-translations page with:
    - source_id: component
      source:
        component:
          component_id: ui_suite_daisyui:button
          slots:
            label:
              sources:
              - source_id: textfield
                source:
                  value: "🏴󠁧󠁢󠁥󠁮󠁧󠁿🍺🍲 1"
                node_id: d96b1621a2e47636
      node_id: a4ee7afd36d20134
    
    - source_id: textfield
      source:
        value: "🏴󠁧󠁢󠁥󠁮󠁧󠁿🍺🍲 2"
      node_id: 21af59dc27fa5844
    
  3. Add a translation: /admin/structure/page-layout/test/translate (by the way, the /admin/structure/page-layout/test/translate/fr/edit modal doesn't show source tree data as a translatable, i guess this is normal)
  4. Back to Display Builder, I need to manually add the language code in the URL (⚠️ is it normal?): /fr/admin/structure/page-layout/test/builder
  5. I publish the translation with "🇫🇷🥐🍷 1" instead of "🏴󠁧󠁢󠁥󠁮󠁧󠁿🍺🍲 1" and "🇫🇷🥐🍷 2" instead of "🏴󠁧󠁢󠁥󠁮󠁧󠁿🍺🍲 2", without moving anything around for now

Results:

  • I see English language in /test-translation: ✅ OK, with 404, because the page doesn't exist
  • I see French language in /test-translation: ❌ KO, 404 not found again, but without the expected display

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:

  • I see English language in /test-translation: ✅ OK
  • I see French language in /test-translation: ❌ KO, I see the English translation

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:

Default language (English) Translation (French)
langcode: en
status: true
id: test
label: 'Test'
conditions: {}
profile: default
sources:
  - source_id: component
    source:
      component:
        component_id: 'ui_suite_daisyui:button'
        slots:
          label:
            sources:
              -
                source_id: textfield
                source:
                  value: '🏴󠁧󠁢󠁥󠁮󠁧󠁿🍺🍲 1'
                node_id: d96b1621a2e47636
    node_id: a4ee7afd36d20134
  - source_id: textfield
    source:
      value: '🏴󠁧󠁢󠁥󠁮󠁧󠁿🍺🍲 2'
    node_id: 21af59dc27fa5844
label: 'Test FR'
translations:
  - key: 'd96b1621a2e47636:source.value'
    value: '🇫🇷🥐🍷 1'
  - key: '21af59dc27fa5844:source.value'
    value: '🇫🇷🥐🍷 2'

I guess we don't need to mess with the default config translation storage: just a skeleton tree with only the translated values. So:

label: 'Test FR'
sources:
  - source:
      component:
        slots:
          label:
            sources:
              - source:
                  value: '🇫🇷🥐🍷 1'
  - source:
      value: '🇫🇷🥐🍷 2'

Then, Drupal Core APIs can do the merge deep they usually do at config load to get the full translated display. The ConfigBuildablePluginBase is 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.

pdureau’s picture

Assigned: pdureau » Unassigned
Status: Needs review » Needs work