How to get the form field label of a multi-value field In hook_form_user_form_alter I need to get the title of each field. When I dump a normal (single-value) field #title is an array value, like this:

array (size=13)
  '#title' =&gt; string 'Name' (length=13)
  '#title_display' =&gt; string 'before' (length=6)


but for fields that can contain more than one value, #title is an object that looks like this:

array (size=13)
  '#title' =&gt; 
    object(Drupal\Core\StringTranslation\TranslatableMarkup)[1544]
      protected 'translatedMarkup' =&gt; null
      protected 'options' =&gt; 
        array (size=0)
          empty
      protected 'stringTranslation' =&gt; 
        object(Drupal\Core\StringTranslation\TranslationManager)[285]
          protected 'translators' =&gt; 
            array (size=1)
              ...
          protected 'sortedTranslators' =&gt; 
            array (size=1)
              ...
          protected 'defaultLangcode' =&gt; string 'en' (length=2)
          public '_serviceId' =&gt; string 'string_translation' (length=18)
      protected 'string' =&gt; string '@title (value @number)' (length=22)
      protected 'arguments' =&gt; 
        array (size=2)
          '@title' =&gt; string 'Name' (length=19)
          '@number' =&gt; int 1
  '#title_display' =&gt; string 'invisible' (length=9)
  '#description' =&gt; string '' (length=0)
  '#field_parents' =&gt; 
    array (size=0)
      empty
  '#required' =&gt; boolean false
  '#delta' =&gt; int 0
  '#weight' =&gt; int 0
  '#type' =&gt; string 'textfield' (length=9)
  '#default_value' =&gt; string 'John Doe' (length=8)
  '#size' =&gt; int 60
  '#placeholder' =&gt; string '' (length=0)
  '#maxlength' =&gt; int 255
  '#attributes' =&gt; 
    array (size=1)
      'class' =&gt; 
        array (size=2)
          0 =&gt; string 'js-text-full' (length=12)
          1 =&gt; string 'text-full' (length=9)


If I just refer to #title, for the multi-value field I end up with:

Name (value 1)


What is the proper way of obtaining the title, and what causes the difference?
What is the proper way of obtaining the title, and what causes the
  difference?


The cause is this code in the field widget:

  // For multiple fields, title and description are handled by the wrapping
  // table.
  if ($is_multiple) {
    $element = [
      '#title' =&gt; $this-&gt;t('@title (value @number)', ['@title' =&gt; $title, '@number' =&gt; $delta + 1]),
      '#title_display' =&gt; 'invisible',
      '#description' =&gt; '',
    ];


This t($string, array $args = [], array $options = []) function returns a TranslatableMarkup object.

You can cast the translatable markup as string to obtain the title, but normally you don't have to. See both answers here  How to get the string value from the function CountryManager::getStandardList()?