Problem/Motivation

In core/includes/theme.inc, function template_preprocess_field_multiple_value_form():

    $header = array(
      array(
        'data' => array(
          '#prefix' => '<h4' . $header_attributes . '>',
          'title' => array(
            '#markup' => t($element['#title']),
          ),
          '#suffix' => '</h4>',
         ...

But $element['#title'] is already translated either as TranslatableMarkup when the field is a base field, either as translated string when the field is a config field. In the first case the page crashes with InvalidArgumentException because t() expects a string. In the second case it only translates an already translated string.

Proposed resolution

Remove the superfluous t(). Remove also the un-needed nesting of #markup.

Remaining tasks

None.

User interface changes

None.

API changes

None.

Data model changes

None.

Original report, by @kevin.dutra

Not sure exactly where to slot this one, component-wise, but putting it under 'field system' for now.

Steps to reproduce

  1. Create an entity that has a base field that is multi-valued (e.g. CARDINALITY_UNLIMITED), like
    $fields['multi_field'] = BaseFieldDefinition::create('entity_reference')
          ->setLabel(t('Multi field label'))
          ->setDescription(t('Some description text.'))
          ->setSetting('target_type', 'user')
          ->setSetting('handler', 'default')
          ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
          ->setDisplayOptions('form', array(
            'type' => 'entity_reference_autocomplete',
            'weight' => 0,
            'settings' => array(
              'match_operator' => 'CONTAINS',
              'size' => '60',
              'placeholder' => '',
            ),
          ))
          ->setDisplayOptions('view', array(
            'label' => 'above',
            'type' => 'entity_reference_label',
            'weight' => 0,
          ));
    
  2. Visit the add page
  3. Cry

More info

It looks like the underlying problem is that a call is made to t() for the title in template_preprocess_field_multiple_value_form(), but the title is a TranslatableMarkup object, not a string, so it barfs with the error:

InvalidArgumentException: $string ("Multi field label") must be a string. in Drupal\Core\StringTranslation\TranslatableMarkup->__construct() (line 83 of core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php).

Drupal\Core\StringTranslation\TranslationManager->translate(Object, Array, Array)
t(Object)
template_preprocess_field_multiple_value_form(Array, 'field_multiple_value_form', Array)
Drupal\Core\Theme\ThemeManager->render('field_multiple_value_form', Array)
Drupal\Core\Render\Renderer->doRender(Array)
Drupal\Core\Render\Renderer->doRender(Array)
Drupal\Core\Render\Renderer->doRender(Array, )
Drupal\Core\Render\Renderer->render(Array, )
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}()
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.view', Object)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1)
Stack\StackedHttpKernel->handle(Object, 1, 1)
Drupal\Core\DrupalKernel->handle(Object)

It's probably worth noting that this doesn't happen for configured fields, just base fields.

You can sidestep the error by forcibly casting the label to a string when you're creating the base field definition, but that's a bit hokey since that translated string is then going to be translated again when it hits the preprocessor.

Also attaching a simple sample module to demonstrate. The listing page is at /admin/test.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

kevin.dutra created an issue. See original summary.

kevin.dutra’s picture

Issue summary: View changes
dawehner’s picture

So

          'title' => array(
            '#markup' => t($element['#title']),
          ),

is certainly wrong ... can someone look which patch added it?

claudiu.cristea’s picture

Component: field system » theme system
Priority: Normal » Major
Status: Active » Needs review
FileSize
549 bytes

@dawehner, I tried with $ git log --grep against that file but I cannot find a point where this has been added.

The bug affects both base and config fields but is crashing only on base fields because in that case the wrong placed t() take an object as argument, not a string

The fix is trivial. Should we add some tests?

claudiu.cristea’s picture

Priority: Major » Critical
Issue summary: View changes
FileSize
3.95 KB
3.41 KB

Here's a "test only" patch to prove the crash and the full patch.

I belive that this is Critical while breaks the entity add/edit form for multiple cardinality base fields.

claudiu.cristea’s picture

Issue summary: View changes

The last submitted patch, 5: 2585483-5.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 5: 2585483-test-only.patch, failed testing.

star-szr’s picture

Hold on I think we have some duplication, let's combine forces: #2584837: Double translation in template_preprocess_field_multiple_value_form()

claudiu.cristea’s picture

Priority: Critical » Normal
Status: Needs work » Closed (duplicate)