You are here

public static function LocalizeFields::fieldWidgetFormAlter in Localize Fields 7

Translates fields' labels, descriptions etc.

Used by hook_field_widget_form_alter() implementation.

Is executed right before hook_preprocess_field_multiple_value_form() and before hook_form_alter().

Parameters

array $element:

array $form_state:

array &$context:

1 call to LocalizeFields::fieldWidgetFormAlter()
localize_fields_field_widget_form_alter in ./localize_fields.module
Translates fields' labels, descriptions etc.

File

./LocalizeFields.inc, line 324
Drupal Localize Fields module

Class

LocalizeFields
@file Drupal Localize Fields module

Code

public static function fieldWidgetFormAlter(&$element, &$form_state, &$context) {
  if (($localize = self::$localize) == -1) {

    // Save a method call if possible.
    $localize = self::localize();
  }
  if (!$localize || empty($context['field']['type'])) {
    return;
  }
  $cntxtDelim = self::CONTEXT_DELIMITER;

  // Refer original label and description - to make other module's
  // hook_field_widget_form_alter() implementations 'see' translated labels
  // (unless 'tentative').
  $sourceLabel = $sourceDescription = '';
  if (!self::$tentative) {
    if (!empty($context['instance']['label'])) {
      $sourceLabel =& $context['instance']['label'];
    }
    if (!empty($context['instance']['description'])) {
      $sourceDescription =& $context['instance']['description'];
    }
  }
  else {
    if (!empty($context['instance']['label'])) {
      $sourceLabel = $context['instance']['label'];
    }
    if (!empty($context['instance']['description'])) {
      $sourceDescription = $context['instance']['description'];
    }
  }
  $field_name = $context['field']['field_name'];
  $field_type = $context['field']['type'];

  // Set these for preprocess_field_multiple_value_form as fallbacks.
  self::$lastKnownEntityType = $context['instance']['entity_type'];
  self::$lastKnownBundle = $bundle = $context['instance']['bundle'];
  $context_field = 'field' . $cntxtDelim . $field_name . $cntxtDelim;
  $context_instance = 'field_instance' . $cntxtDelim . $bundle . self::CONTEXT_DELIMITER_BUNDLE . $field_name . $cntxtDelim;

  // File fields are pretty non-standard.
  $file_cardinalitySingle = FALSE;

  // These might be used more than once.
  $filtered_translated_label = $filtered_translated_description = '';

  // Find the array listing labels.
  if (array_key_exists('value', $element) && array_key_exists('#title', $element['value'])) {

    /*
     * text and number_ fields have a #title in a value bucket.
     *
     * Their single row instances' ['value']['#title'] is non-empty (and that
     * one is used in widget rendering and validation),
     * whereas any/multiple rows instances' ['value']['#title'] is empty.
     *
     * Their single row instances also #title in in root; it's usually not
     * used but let's play it safe.
     *
     * Single row instance:
     * #title: (string) >>Native title<<
     * value: (array) {
     * . #title: (string) >>Native title<<
     * }
     *
     * Any/multiple row instance:
     * value: (array) {
     * . #title: (string) >><<
     * }
     *
     * NB: sometimes single row instances have the same structure as
     * any/multiple row instance.
     * Don't really know what affects/controls the structure.
     */
    $props =& $element['value'];

    // text and number_ fields single row instances got #title in root too;
    // it's usually not used but let's play it safe.
    if (array_key_exists('#title', $element)) {
      if ($sourceLabel) {
        $element['#title'] = $filtered_translated_label = check_plain($sourceLabel = self::translateInternal($sourceLabel, $context_instance . 'label', TRUE));
      }
      if ($sourceDescription && array_key_exists('#description', $element)) {
        $element['#description'] = $filtered_translated_description = field_filter_xss($sourceDescription = self::translateInternal($sourceDescription, $context_instance . 'description', FALSE));
      }
    }
  }
  elseif (array_key_exists('#title', $element)) {

    // list_ types.
    $props =& $element;
  }
  elseif (array_key_exists(0, $element) && array_key_exists('#title', $element[0])) {

    // Single instance file has no props in $element root.
    $file_cardinalitySingle = TRUE;
    $props =& $element[0];
  }
  else {

    // Unsupported element structure.
    return;
  }

  // For some field types we translate the label/description in a non-generic
  // manner.
  $genericTranslateLabel = $genericTranslateDescription = TRUE;

  // Most field types require that we prepend a custom validate function
  // to secure translation during validation.
  $preValidate = TRUE;
  switch ($field_type) {

    // These are all covered by the general behaviour, and may be tested via
    // the localize_fields_test Features module.
    // case 'text':
    // case 'text_long':
    // case 'text_with_summary':
    // case 'taxonomy_term_reference':
    // case 'date':
    // case 'datetime':
    // case 'datestamp':
    // case 'field_collection':
    //   break;
    case 'number_integer':
    case 'number_decimal':
    case 'number_float':

      // Prefix/suffixes are not encoded in form rendering.
      if (array_key_exists('#field_prefix', $props) && ($source = $props['#field_prefix']) !== '') {
        $props['#field_prefix'] = field_filter_xss(self::translateInternal($source, $context_instance . 'prefix', FALSE));
      }
      if (array_key_exists('#field_suffix', $props) && ($source = $props['#field_suffix']) !== '') {
        $props['#field_suffix'] = field_filter_xss(self::translateInternal($source, $context_instance . 'suffix', FALSE));
      }
      break;
    case 'list_boolean':
    case 'list_text':
    case 'list_integer':
    case 'list_decimal':
    case 'list_float':

      // @todo: Support custom list types by switch'ing over widget type and
      // filter off auto-complete fields by their widget type (on of the text
      // widgets) leaving only 'real' list types to this algo.
      if (!empty($context['field']['settings']['allowed_values'])) {

        // Context is field name only, because options are a field_base
        // property.
        $context_options = $context_field . 'allowed_values';

        // list_boolean that uses the 'on' value as field label
        // - except when nested in a field_collection (sic!).
        if ($props['#title'] && isset($context['instance']['widget']['settings']['display_label']) && !$context['instance']['widget']['settings']['display_label']) {
          $genericTranslateLabel = FALSE;
          $props['#title'] = check_plain($sourceLabel = self::translateInternal($props['#title'], $context_options, FALSE));
        }

        // Translate option labels - unless flagged 'off'.
        if (empty($context['field']['settings']['allowed_values_no_localization']) && array_key_exists('#options', $props) && !empty($props['#options'])) {
          foreach ($props['#options'] as &$label) {

            // Option labels could easily be integers. Decimals on the other
            // hand may have to be translated because of the decimal separator.
            if ($label && !ctype_digit('' . $label)) {
              $label = field_filter_xss(self::translateInternal($label, $context_options, FALSE));
            }
          }
          unset($label);

          // Iteration ref.
        }
      }
      break;
    case 'file':
    case 'image':

      // Doesn't need our pre validator.
      $preValidate = FALSE;

      // Multi instance file fields have title and description on instances as
      // well as the overall field.
      // And the title of such instances are used by core field validation.
      if (!$file_cardinalitySingle) {
        $limit = 100;
        for ($i = 0; $i < $limit; ++$i) {
          if (array_key_exists($i, $element)) {
            if ($sourceLabel && array_key_exists('#title', $element[$i])) {
              $element[$i]['#title'] = $filtered_translated_label ? $filtered_translated_label : ($filtered_translated_label = check_plain($sourceLabel = self::translateInternal($sourceLabel, $context_instance . 'label', TRUE)));
            }
            if ($sourceDescription && array_key_exists('#description', $element[$i])) {
              $element[$i]['#description'] = $filtered_translated_description ? $filtered_translated_description : ($filtered_translated_description = field_filter_xss($sourceDescription = self::translateInternal($sourceDescription, $context_instance . 'description', FALSE)));
            }
          }
          else {
            break;
          }
        }
      }
      elseif (array_key_exists('#description', $props) && $sourceDescription && ($source = $props['#description']) && strpos($source, '<br')) {
        $genericTranslateDescription = FALSE;
        $a = explode('<br', $source);
        $a[0] = field_filter_xss($sourceDescription = self::translateInternal($sourceDescription, $context_instance . 'description', FALSE));
        $props['#description'] = join('<br', $a);
      }
      break;
  }

  // All types has a label (at least here; because of the initial process
  // of finding the right element array).
  if ($genericTranslateLabel && $sourceLabel) {
    $props['#title'] = $filtered_translated_label ? $filtered_translated_label : ($filtered_translated_label = check_plain($sourceLabel = self::translateInternal($sourceLabel, $context_instance . 'label', TRUE)));
  }

  // Most types has a description.
  if ($genericTranslateDescription && $sourceDescription && array_key_exists('#description', $props)) {
    $props['#description'] = $filtered_translated_description ? $filtered_translated_description : ($filtered_translated_description = field_filter_xss($sourceDescription = self::translateInternal($sourceDescription, $context_instance . 'description', FALSE)));
  }

  // Most types need custom validator (which only translates).
  if ($preValidate && array_key_exists('#element_validate', $props) && !in_array('localize_fields_pre_element_validate', $props['#element_validate'])) {
    array_unshift($props['#element_validate'], 'localize_fields_pre_element_validate');
  }
}