diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml index 3edb8057..7b658e0 100644 --- a/core/config/schema/core.data_types.schema.yml +++ b/core/config/schema/core.data_types.schema.yml @@ -650,12 +650,9 @@ field.field_settings.integer: max: type: integer label: 'Maximum' - prefix: + format_plural_string: type: label - label: 'Prefix' - suffix: - type: label - label: 'Suffix' + label: 'Plural formatted prefix/suffix' field.value.integer: type: mapping @@ -688,12 +685,9 @@ field.field_settings.decimal: max: type: float label: 'Maximum' - prefix: - type: label - label: 'Prefix' - suffix: + format_plural_string: type: label - label: 'Suffix' + label: 'Plural formatted prefix/suffix' field.value.decimal: type: mapping @@ -719,12 +713,9 @@ field.field_settings.float: max: type: float label: 'Maximum' - prefix: - type: label - label: 'Prefix' - suffix: + format_plural_string: type: label - label: 'Suffix' + label: 'Plural formatted prefix/suffix' field.value.float: type: mapping diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index 71b7ccd..984056f 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -202,6 +202,9 @@ field.widget.settings.number: placeholder: type: label label: 'Placeholder' + format_plural: + type: boolean + label: 'Whether or not to display prefix/suffix' field.widget.settings.checkbox: type: mapping @@ -292,9 +295,12 @@ field.formatter.settings.number_decimal: scale: type: integer label: 'Scale' - prefix_suffix: - type: boolean - label: 'Display prefix and suffix.' + format_plural: + type: string + label: 'Which prefix/suffix to display, if any' + format_plural_string: + type: label + label: 'Override for prefix/suffix' field.formatter.settings.number_integer: type: mapping @@ -303,9 +309,12 @@ field.formatter.settings.number_integer: thousand_separator: type: string label: 'Thousand marker' - prefix_suffix: - type: boolean - label: 'Display prefix and suffix.' + format_plural: + type: string + label: 'Which prefix/suffix to display, if any' + format_plural_string: + type: label + label: 'Override for prefix/suffix' field.formatter.settings.number_unformatted: type: mapping diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php index bee0144..9760f29 100644 --- a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php +++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php @@ -141,6 +141,11 @@ public function getRenderer($field_name) { // Instantiate the widget object from the stored display properties. if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) { + // Pass the language of the display configuration down to the widget, + // which otherwise doesn't have access to it because it is not a + // stand-alone config entity. It may be relevant for some operations, + // such as using plural formatting as a field prefix. + $configuration['settings']['_settings_langcode'] = $this->langcode; $widget = $this->pluginManager->getInstance(array( 'field_definition' => $definition, 'form_mode' => $this->originalMode, diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php index 27884cd..f7d1f3c 100644 --- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php +++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php @@ -197,6 +197,11 @@ public function getRenderer($field_name) { // Instantiate the formatter object from the stored display properties. if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) { + // Pass the language of the display configuration down to the formatter, + // which otherwise doesn't have access to it because it is not a + // stand-alone config entity. It may be relevant for some operations, + // such as plural formatting. + $configuration['settings']['_settings_langcode'] = $this->langcode; $formatter = $this->pluginManager->getInstance(array( 'field_definition' => $definition, 'view_mode' => $this->originalMode, diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php index b835327..5b374b8 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php @@ -292,6 +292,11 @@ public function toArray() { if (!$definition->isDisplayConfigurable($this->displayContext)) { unset($properties['content'][$field_name]); unset($properties['hidden'][$field_name]); + // If _settings_langcode is in the settings, remove it. It should never + // be saved in the configuration. + if (isset($properties['content'][$field_name]['settings']['_settings_langcode'])) { + unset($properties['content'][$field_name]['settings']['_settings_langcode']); + } } } @@ -339,6 +344,12 @@ public function setComponent($name, array $options = array()) { // Ensure we always have an empty settings and array. $options += ['settings' => [], 'third_party_settings' => []]; + // If _settings_langcode is in the settings, remove it. It should never + // be saved in the configuration. + if (isset($options['settings']['_settings_langcode'])) { + unset($options['settings']['_settings_langcode']); + } + $this->content[$name] = $options; unset($this->hidden[$name]); unset($this->plugins[$name]); diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index d212474..12091df 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -13,6 +13,7 @@ use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Render\Element; use Drupal\Core\TypedData\TranslatableInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -453,10 +454,21 @@ protected function getSingleFieldDisplay($entity, $field_name, $display_options) $bundle = $entity->bundle(); $key = $entity_type_id . ':' . $bundle . ':' . $field_name . ':' . hash('crc32b', serialize($display_options)); if (!isset($this->singleFieldDisplays[$key])) { + // Assign the incoming '_settings_langcode' as the langcode of the + // EntityViewDisplay, or default to the current interface language. + if (isset($display_options['settings']['_settings_langcode'])) { + $langcode = $display_options['settings']['_settings_langcode']; + unset($display_options['settings']['_settings_langcode']); + } + else { + $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_INTERFACE)->getId(); + } + $this->singleFieldDisplays[$key] = EntityViewDisplay::create(array( 'targetEntityType' => $entity_type_id, 'bundle' => $bundle, 'status' => TRUE, + 'langcode' => $langcode, ))->setComponent($field_name, $display_options); } $display = $this->singleFieldDisplays[$key]; diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php index eff6516..42f8248 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php @@ -118,7 +118,11 @@ public function resetCache(array $entities = NULL); * 'default_formatter' for the field type. The default formatter will * also be used if the requested formatter is not available. * - settings: (array) Settings specific to the formatter. Defaults to the - * formatter's default settings. + * formatter's default settings. A '_settings_langcode' entry should be + * present to indicate the language of the settings that are being + * provided (typically, the language of the configuration entity that + * contains the formatter, such as an entity view mode). If absent, the + * current interface language will be assumed. * - weight: (float) The weight to assign to the renderable element. * Defaults to 0. * diff --git a/core/lib/Drupal/Core/Field/FormatterPluginManager.php b/core/lib/Drupal/Core/Field/FormatterPluginManager.php index e6054a9..9136c13 100644 --- a/core/lib/Drupal/Core/Field/FormatterPluginManager.php +++ b/core/lib/Drupal/Core/Field/FormatterPluginManager.php @@ -73,7 +73,7 @@ public function createInstance($plugin_id, array $configuration = array()) { } /** - * Overrides PluginManagerBase::getInstance(). + * Creates a formatter instance with the provided options. * * @param array $options * An array with the following key/value pairs: @@ -92,6 +92,11 @@ public function createInstance($plugin_id, array $configuration = array()) { * also be used if the requested formatter is not available. * - settings: (array) Settings specific to the formatter. Each setting * defaults to the default value specified in the formatter definition. + * A '_settings_langcode' entry should be present to indicate the + * language of the settings that are being provided (typically, the + * language of the configuration entity that contains the formatter, + * such as an entity view mode). If absent, the current interface + * language will be assumed. * - third_party_settings: (array) Settings provided by other extensions * through hook_field_formatter_third_party_settings_form(). * @@ -116,7 +121,7 @@ public function getInstance(array $options) { // - the formatter is not applicable to the field definition. $definition = $this->getDefinition($configuration['type'], FALSE); if (!isset($definition['class']) || !in_array($field_type, $definition['field_types']) || !$definition['class']::isApplicable($field_definition)) { - // Grab the default widget for the field type. + // Grab the default formatter for the field type. $field_type_definition = $this->fieldTypeManager->getDefinition($field_type); if (empty($field_type_definition['default_formatter'])) { return NULL; @@ -128,6 +133,7 @@ public function getInstance(array $options) { 'field_definition' => $field_definition, 'view_mode' => $options['view_mode'], ); + return $this->createInstance($plugin_id, $configuration); } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php index 0e43ac1..7c9a17b 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php @@ -32,10 +32,8 @@ class DecimalFormatter extends NumericFormatterBase { */ public static function defaultSettings() { return array( - 'thousand_separator' => '', 'decimal_separator' => '.', 'scale' => 2, - 'prefix_suffix' => TRUE, ) + parent::defaultSettings(); } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php index 8768474..20e5d4b 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/IntegerFormatter.php @@ -27,16 +27,6 @@ class IntegerFormatter extends NumericFormatterBase { /** * {@inheritdoc} */ - public static function defaultSettings() { - return array( - 'thousand_separator' => '', - 'prefix_suffix' => TRUE, - ) + parent::defaultSettings(); - } - - /** - * {@inheritdoc} - */ protected function numberFormat($number) { return number_format($number, 0, '', $this->getSetting('thousand_separator')); } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php index 28b0159..b0f5eec 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php @@ -11,6 +11,7 @@ use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; /** * Parent plugin for decimal and integer formatters. @@ -20,6 +21,32 @@ use AllowedTagsXssTrait; /** + * Indicates plural formatting using the setting from the field. + */ + const FORMAT_PLURAL_USE_FIELD_SETTING = 'field'; + + /** + * Indicates plural formatting using the setting from the formatter. + */ + const FORMAT_PLURAL_USE_FORMATTER_SETTING = 'formatter'; + + /** + * Indicates no plural formatting. + */ + const FORMAT_PLURAL_NONE = 'none'; + + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return array( + 'thousand_separator' => '', + 'format_plural' => NumericFormatterBase::FORMAT_PLURAL_NONE, + 'format_plural_string' => '', + ) + parent::defaultSettings(); + } + + /** * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state) { @@ -39,16 +66,82 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#weight' => 0, ); - $elements['prefix_suffix'] = array( - '#type' => 'checkbox', - '#title' => t('Display prefix and suffix'), - '#default_value' => $this->getSetting('prefix_suffix'), - '#weight' => 10, + $elements['format_plural'] = array( + '#type' => 'radios', + '#title' => $this->t('Display prefix/suffix'), + '#default_value' => $this->getSetting('format_plural'), + '#options' => array( + NumericFormatterBase::FORMAT_PLURAL_NONE => $this->t('No prefix/suffix'), + NumericFormatterBase::FORMAT_PLURAL_USE_FIELD_SETTING => $this->t('Use setting from the field settings'), + NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING => $this->t('Override the setting from the field settings'), + ), + ); + + // @todo This field needs to have some kind of #states magic so that it + // only shows if the option on format_plural is + // NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING. + // Need to do this before this patch is finalized! + $elements['format_plural_values'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Prefix/suffix override'), + '#after_build' => [[get_class($this), 'formatPluralValuesAfterBuild']], ); + $langcode = $this->getSetting('_settings_langcode'); + $plurals = $this->getNumberOfPlurals($langcode); + $labels = $this->getPluralLabels($langcode); + $plural_string = $this->getSetting('format_plural_string'); + if (!$plural_string) { + $settings = $this->getFieldSettings(); + if (isset($settings['format_plural_string'])) { + $plural_string = $settings['format_plural_string']; + } + } + $plural_array = explode(PluralTranslatableMarkup::DELIMITER, $plural_string); + for ($i = 0; $i < $plurals; $i++) { + $elements['format_plural_values'][$i] = array( + '#type' => 'textfield', + '#title' => $labels[$i]['label'], + '#description' => $labels[$i]['description'], + '#default_value' => isset($plural_array[$i]) ? $plural_array[$i] : '', + ); + } + return $elements; } + /** + * After-build callback: Sets the value for the format_plural_string property. + * + * This method is assigned as an #after_build function in the settings form. + * + * The user interface for setting up singular/plural forms is an array of text + * fields, for usability, but for translation purposes, the values have to be + * stored as a single string in the field settings. This function implodes the + * values. + * + * @param array $element + * Textfield element where the plural format should be set. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * Textfield element with plural string set. + */ + public static function formatPluralValuesAfterBuild($element, FormStateInterface $form_state) { + if ($form_state->isProcessingInput()) { + $keys = $element['#parents']; + $array_value = $form_state->getValue($keys); + $form_state->unsetValue($keys); + $string_value = implode(PluralTranslatableMarkup::DELIMITER, $array_value); + array_pop($keys); + $keys[] = 'format_plural_string'; + $form_state->setValue($keys, $string_value); + + } + return $element; + } + /** * {@inheritdoc} */ @@ -56,8 +149,11 @@ public function settingsSummary() { $summary = array(); $summary[] = $this->numberFormat(1234.1234567890); - if ($this->getSetting('prefix_suffix')) { - $summary[] = t('Display with prefix and suffix.'); + if ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FIELD_SETTING) { + $summary[] = $this->t('Display prefix/suffix from the field settings.'); + } + elseif ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING) { + $summary[] = $this->t('Display overridden prefix/suffix.'); } return $summary; @@ -68,19 +164,24 @@ public function settingsSummary() { */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); - $settings = $this->getFieldSettings(); + $field_settings = $this->getFieldSettings(); foreach ($items as $delta => $item) { $output = $this->numberFormat($item->value); // Account for prefix and suffix. - if ($this->getSetting('prefix_suffix')) { - $prefixes = isset($settings['prefix']) ? array_map(array('Drupal\Core\Field\FieldFilteredMarkup', 'create'), explode('|', $settings['prefix'])) : array(''); - $suffixes = isset($settings['suffix']) ? array_map(array('Drupal\Core\Field\FieldFilteredMarkup', 'create'), explode('|', $settings['suffix'])) : array(''); - $prefix = (count($prefixes) > 1) ? $this->formatPlural($item->value, $prefixes[0], $prefixes[1]) : $prefixes[0]; - $suffix = (count($suffixes) > 1) ? $this->formatPlural($item->value, $suffixes[0], $suffixes[1]) : $suffixes[0]; - $output = $prefix . $output . $suffix; + $format_string = ''; + if ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FIELD_SETTING) { + $format_string = $field_settings['format_plural_string']; + } + elseif ($this->getSetting('format_plural') == NumericFormatterBase::FORMAT_PLURAL_USE_FORMATTER_SETTING) { + $format_string = $this->getSetting('format_plural_string'); } + + if ($format_string) { + $output = PluralTranslatableMarkup::createFromTranslatedString($item->value, $format_string, ['@count' => $output], ['langcode' => $this->getSetting('_settings_langcode')]); + } + // Output the raw value in a content attribute if the text of the HTML // element differs from the raw value (for example when a prefix is used). if (isset($item->_attributes) && $item->value != $output) { diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php index 78d5d9e..2c3377a 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php @@ -40,18 +40,6 @@ public static function defaultStorageSettings() { /** * {@inheritdoc} */ - public static function defaultFieldSettings() { - return array( - 'min' => '', - 'max' => '', - 'prefix' => '', - 'suffix' => '', - ) + parent::defaultFieldSettings(); - } - - /** - * {@inheritdoc} - */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { $properties['value'] = DataDefinition::create('integer') ->setLabel(t('Integer value')) diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php index 9f9bd0b..a101942 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php @@ -9,6 +9,7 @@ use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; /** * Base class for numeric configurable field types. @@ -22,8 +23,7 @@ public static function defaultFieldSettings() { return array( 'min' => '', 'max' => '', - 'prefix' => '', - 'suffix' => '', + 'format_plural_string' => '', ) + parent::defaultFieldSettings(); } @@ -36,31 +36,66 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { $element['min'] = array( '#type' => 'number', - '#title' => t('Minimum'), + '#title' => $this->t('Minimum'), '#default_value' => $settings['min'], - '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'), + '#description' => $this->t('The minimum value that should be allowed in this field. Leave blank for no minimum.'), ); $element['max'] = array( '#type' => 'number', - '#title' => t('Maximum'), + '#title' => $this->t('Maximum'), '#default_value' => $settings['max'], - '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'), + '#description' => $this->t('The maximum value that should be allowed in this field. Leave blank for no maximum.'), ); - $element['prefix'] = array( - '#type' => 'textfield', - '#title' => t('Prefix'), - '#default_value' => $settings['prefix'], - '#size' => 60, - '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '€ '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), - ); - $element['suffix'] = array( - '#type' => 'textfield', - '#title' => t('Suffix'), - '#default_value' => $settings['suffix'], - '#size' => 60, - '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), + + $element['format_plural_values'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Plural formatted prefix/suffix'), + '#after_build' => [[get_class($this), 'formatPluralValuesAfterBuild']], ); + $langcode = $this->getFieldDefinition()->get('langcode'); + $plurals = $this->getNumberOfPlurals($langcode); + $labels = $this->getPluralLabels($langcode); + $plural_array = explode(PluralTranslatableMarkup::DELIMITER, $settings['format_plural_string']); + for ($i = 0; $i < $plurals; $i++) { + $element['format_plural_values'][$i] = array( + '#type' => 'textfield', + '#title' => $labels[$i]['label'], + '#description' => $labels[$i]['description'], + '#default_value' => isset($plural_array[$i]) ? $plural_array[$i] : '', + ); + } + + return $element; + } + /** + * After-build callback: Sets the value for the format_plural_string property. + * + * This method is assigned as an #after_build function in the settings form. + * + * The user interface for setting up singular/plural forms is an array of text + * fields, for usability, but for translation purposes, the values have to be + * stored as a single string in the field settings. This function implodes the + * values. + * + * @param array $element + * Textfield element where the plural format should be set. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * Textfield element with plural string set. + */ + public static function formatPluralValuesAfterBuild($element, FormStateInterface $form_state) { + if ($form_state->isProcessingInput()) { + $keys = $element['#parents']; + $array_value = $form_state->getValue($keys); + $form_state->unsetValue($keys); + $string_value = implode(PluralTranslatableMarkup::DELIMITER, $array_value); + array_pop($keys); + $keys[] = 'format_plural_string'; + $form_state->setValue($keys, $string_value); + } return $element; } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php index 57abc60..5bcfb7c 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php @@ -11,6 +11,7 @@ use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Symfony\Component\Validator\ConstraintViolationInterface; /** @@ -34,6 +35,7 @@ class NumberWidget extends WidgetBase { public static function defaultSettings() { return array( 'placeholder' => '', + 'format_plural' => TRUE, ) + parent::defaultSettings(); } @@ -47,6 +49,14 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#default_value' => $this->getSetting('placeholder'), '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'), ); + + $element['format_plural'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Display prefix/suffix'), + '#description' => $this->t('If checked, prefix/suffix from the field will be displayed.'), + '#default_value' => $this->getSetting('format_plural'), + ); + return $element; } @@ -58,10 +68,14 @@ public function settingsSummary() { $placeholder = $this->getSetting('placeholder'); if (!empty($placeholder)) { - $summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder)); + $summary[] = $this->t('Placeholder: @placeholder', array('@placeholder' => $placeholder)); } else { - $summary[] = t('No placeholder'); + $summary[] = $this->t('No placeholder'); + } + + if ($this->getSetting('format_plural')) { + $summary[] = $this->t('Prefix/suffix from the field are displayed'); } return $summary; @@ -100,13 +114,19 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen } // Add prefix and suffix. - if ($field_settings['prefix']) { - $prefixes = explode('|', $field_settings['prefix']); - $element['#field_prefix'] = FieldFilteredMarkup::create(array_pop($prefixes)); - } - if ($field_settings['suffix']) { - $suffixes = explode('|', $field_settings['suffix']); - $element['#field_suffix'] = FieldFilteredMarkup::create(array_pop($suffixes)); + if ($field_settings['format_plural_string'] && $this->getSetting('format_plural')) { + // The field setting is a string containing all plural variants, suitable + // for use in formatPlural() methods, but already translated into the + // UI language. Pop off the last plural variant, and use the parts + // before/after @count in that string as the prefix/suffix for this field. + $values = explode(PluralTranslatableMarkup::DELIMITER, $field_settings['format_plural_string']); + $labels = explode('@count', array_pop($values)); + if (isset($labels[0])) { + $element['#field_prefix'] = FieldFilteredMarkup::create(trim($labels[0])); + } + if (isset($labels[1])) { + $element['#field_suffix'] = FieldFilteredMarkup::create(trim($labels[1])); + } } return array('value' => $element); diff --git a/core/lib/Drupal/Core/Field/WidgetPluginManager.php b/core/lib/Drupal/Core/Field/WidgetPluginManager.php index 2bc41f1..510c685 100644 --- a/core/lib/Drupal/Core/Field/WidgetPluginManager.php +++ b/core/lib/Drupal/Core/Field/WidgetPluginManager.php @@ -55,7 +55,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac } /** - * Overrides PluginManagerBase::getInstance(). + * Creates a widget instance with the provided options. * * @param array $options * An array with the following key/value pairs: @@ -71,6 +71,11 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac * used if the requested widget is not available. * - settings: (array) Settings specific to the widget. Each setting * defaults to the default value specified in the widget definition. + * A '_settings_langcode' entry should be present to indicate the + * language of the settings that are being provided (typically, the + * language of the configuration entity that contains the widget, + * such as an entity form mode). If absent, the current interface + * language will be assumed. * - third_party_settings: (array) Settings provided by other extensions * through hook_field_formatter_third_party_settings_form(). * diff --git a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php index 583ab18..fc7a85b 100644 --- a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableMarkup.php @@ -63,7 +63,9 @@ class PluralTranslatableMarkup extends TranslatableMarkup { * See \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for * additional information about placeholders. Note that you do not need to * include @count in this array; this replacement is done automatically - * for the plural cases. + * for the plural cases. But you can include @count to override the + * replacement value (for instance, to use a formatted number instead of + * the raw number). * @param array $options * (optional) An associative array of additional options. See t() for * allowed keys. @@ -95,7 +97,9 @@ public function __construct($count, $singular, $plural, array $args = [], array * Based on the first character of the key, the value is escaped and/or * themed. See \Drupal\Component\Utility\SafeMarkup::format(). Note that you * do not need to include @count in this array; this replacement is done - * automatically for the plural cases. + * automatically for the plural cases. But you can include @count to + * override the replacement value (for instance, to use a formatted number + * instead of the raw number). * @param array $options * An associative array of additional options. See t() for allowed keys. * @@ -123,7 +127,10 @@ public function render() { } $arguments = $this->getArguments(); - $arguments['@count'] = $this->count; + // Allow overrides of what gets substituted in for @count. + if (!isset($arguments['@count'])) { + $arguments['@count'] = $this->count; + } $translated_array = explode(static::DELIMITER, $this->translatedString); if ($this->count == 1) { @@ -142,9 +149,9 @@ public function render() { } else { // If the index cannot be computed or there's no translation, use the - // second plural form as a fallback (which allows for most flexibility + // last plural form as a fallback (which allows for most flexibility // with the replaceable @count value). - $return = $translated_array[1]; + $return = $translated_array[count($translated_array) - 1]; } } diff --git a/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php b/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php index f4b4bd3..cfe8167 100644 --- a/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php +++ b/core/lib/Drupal/Core/StringTranslation/StringTranslationTrait.php @@ -84,6 +84,59 @@ protected function getNumberOfPlurals($langcode = NULL) { } /** + * Returns the labels and descriptions for plural variants in a language. + * + * @param string $langcode + * The language code. + * + * @return array[] + * An array of labels and descriptions to use in forms for the plural + * variants of $langcode, translated into the current interface language. + * Each item in the returned array is an array with elements 'label' and + * 'description' for the corresponding plural variant. + */ + protected function getPluralLabels($langcode) { + $plurals = $this->getNumberOfPlurals($langcode); + + // @todo The return values for both cases should be more relevant to the + // actual plural rules of the language; see + // https://www.drupal.org/node/2499639. + + if ($plurals == 1) { + return [ + [ + 'label' => $this->t('Form for all numbers'), + 'description' => $this->t('Text to use for all numbers; @count will be replaced with the value.'), + ], + ]; + } + + if ($plurals == 2) { + return [ + [ + 'label' => $this->t('Singular form'), + 'description' => $this->t('Text to use for the singular form; @count will be replaced with the value.'), + ], + [ + 'label' => $this->t('Plural form'), + 'description' => $this->t('Text to use for the plural form; @count will be replaced with the value.'), + ], + ]; + } + + // For the != 2 case, generate labels. + $labels = []; + for ($i = 0; $i < $plurals; $i++) { + $labels[] = [ + 'label' => ($i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form')), + 'description' => $this->t('Text to use for this variant; @count will be replaced with the value.'), + ]; + } + + return $labels; + } + + /** * Gets the string translation service. * * @return \Drupal\Core\StringTranslation\TranslationInterface diff --git a/core/modules/field/src/Plugin/migrate/process/d6/FieldFormatterSettingsDefaults.php b/core/modules/field/src/Plugin/migrate/process/d6/FieldFormatterSettingsDefaults.php index 4deeb16..41b6bd5 100644 --- a/core/modules/field/src/Plugin/migrate/process/d6/FieldFormatterSettingsDefaults.php +++ b/core/modules/field/src/Plugin/migrate/process/d6/FieldFormatterSettingsDefaults.php @@ -64,69 +64,69 @@ protected function numberSettings($type, $format) { 'scale' => 0, 'decimal_separator' => '.', 'thousand_separator' => ',', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'us_1' => [ 'scale' => 1, 'decimal_separator' => '.', 'thousand_separator' => ',', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'us_2' => [ 'scale' => 2, 'decimal_separator' => '.', 'thousand_separator' => ',', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'be_0' => [ 'scale' => 0, 'decimal_separator' => ',', 'thousand_separator' => '.', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'be_1' => [ 'scale' => 1, 'decimal_separator' => ',', 'thousand_separator' => '.', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'be_2' => [ 'scale' => 2, 'decimal_separator' => ',', 'thousand_separator' => '.', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'fr_0' => [ 'scale' => 0, 'decimal_separator' => ',', 'thousand_separator' => ' ', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'fr_1' => [ 'scale' => 1, 'decimal_separator' => ',', 'thousand_separator' => ' ', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'fr_2' => [ 'scale' => 2, 'decimal_separator' => ',', 'thousand_separator' => ' ', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], ], 'number_integer' => [ 'us_0' => [ 'thousand_separator' => ',', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'be_0' => [ 'thousand_separator' => '.', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], 'fr_0' => [ 'thousand_separator' => ' ', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ], ], ]; diff --git a/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceSettings.php b/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceSettings.php index a678820..f40816a 100644 --- a/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceSettings.php +++ b/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceSettings.php @@ -7,10 +7,12 @@ namespace Drupal\field\Plugin\migrate\process\d6; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\Row; + /** * @MigrateProcessPlugin( * id = "d6_field_field_settings" @@ -30,8 +32,27 @@ public function transform($value, MigrateExecutableInterface $migrate_executable case 'number': $settings['min'] = $field_settings['min']; $settings['max'] = $field_settings['max']; - $settings['prefix'] = $field_settings['prefix']; - $settings['suffix'] = $field_settings['suffix']; + $prefix = $field_settings['prefix']; + $suffix = $field_settings['suffix']; + if ($prefix || $suffix) { + // In case they were using the previous system to separate + // plural values by |, make this into a proper format_plural + // object. + $prefixes = explode('|', $prefix); + $suffixes = explode('|', $suffix); + $length = max(count($prefixes), count($suffixes)); + $strings = []; + for ($i = 0; $i < $length; $i++) { + $prefix = isset($prefixes[$i]) ?: $prefixes[count($prefixes) - 1]; + $suffix = isset($suffixes[$i]) ?: $suffixes[count($suffixes) - 1]; + $strings[] = $prefix . '@count' . $suffix; + } + $settings['format_plural_string'] = implode(PluralTranslatableMarkup::DELIMITER, $strings); + } + else { + $settings['format_plural_string'] = ''; + } + break; case 'link': diff --git a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php index 1ad3d36..8b4bc78 100644 --- a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php +++ b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldFormatterSettingsTest.php @@ -73,7 +73,8 @@ public function testEntityDisplaySettings() { $expected['type'] = 'number_integer'; $expected['settings'] = array( 'thousand_separator' => ',', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', + 'format_plural_string' => '', ); $component = $display->getComponent('field_test_two'); $this->assertIdentical($expected, $component); @@ -83,7 +84,8 @@ public function testEntityDisplaySettings() { 'scale' => 2, 'decimal_separator' => '.', 'thousand_separator' => ',', - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', + 'format_plural_string' => '', ); $component = $display->getComponent('field_test_three'); $this->assertIdentical($expected, $component); diff --git a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php index c3c0cb6..f6052a5 100644 --- a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php +++ b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldInstanceTest.php @@ -39,8 +39,7 @@ public function testFieldInstanceMigration() { $expected = array( 'min' => 10, 'max' => 100, - 'prefix' => 'pref', - 'suffix' => 'suf', + 'format_plural_string' => 'pref@countsuf', 'unsigned' => FALSE, 'size' => 'normal', ); @@ -51,8 +50,7 @@ public function testFieldInstanceMigration() { $expected = array( 'min' => 100.0, 'max' => 200.0, - 'prefix' => 'id-', - 'suffix' => '', + 'format_plural_string' => 'id-@count', ); $this->assertIdentical($expected, $field->getSettings()); diff --git a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php index 891eb69..2244d1f 100644 --- a/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php +++ b/core/modules/field/src/Tests/Migrate/d6/MigrateFieldWidgetSettingsTest.php @@ -44,7 +44,7 @@ public function testWidgetSettings() { $component = $form_display->getComponent('field_test_two'); $expected['type'] = 'number'; $expected['weight'] = 1; - $expected['settings'] = array('placeholder' => ''); + $expected['settings'] = array('placeholder' => '', 'format_plural' => TRUE); $this->assertIdentical($expected, $component); // Float field. @@ -56,6 +56,7 @@ public function testWidgetSettings() { $component = $form_display->getComponent('field_test_email'); $expected['type'] = 'email_default'; $expected['weight'] = 6; + $expected['settings'] = array('placeholder' => ''); $this->assertIdentical($expected, $component); // Link field. diff --git a/core/modules/field/src/Tests/Number/NumberFieldTest.php b/core/modules/field/src/Tests/Number/NumberFieldTest.php index f80dfab..aeeddef 100644 --- a/core/modules/field/src/Tests/Number/NumberFieldTest.php +++ b/core/modules/field/src/Tests/Number/NumberFieldTest.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\simpletest\WebTestBase; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; /** * Tests the creation of numeric fields. @@ -145,7 +146,9 @@ function testNumberIntegerField() { 'entity_type' => 'entity_test', 'bundle' => 'entity_test', 'settings' => array( - 'min' => $minimum, 'max' => $maximum, 'prefix' => 'ThePrefix', + 'min' => $minimum, + 'max' => $maximum, + 'format_plural_string' => 'ThePrefix@count' . PluralTranslatableMarkup::DELIMITER . 'PluralPrefix@countPluralSuffix', ) ))->save(); @@ -153,7 +156,8 @@ function testNumberIntegerField() { ->setComponent($field_name, array( 'type' => 'number', 'settings' => array( - 'placeholder' => '4' + 'placeholder' => '4', + 'plural_format' => TRUE, ), )) ->save(); @@ -161,7 +165,7 @@ function testNumberIntegerField() { ->setComponent($field_name, array( 'type' => 'number_integer', 'settings' => array( - 'prefix_suffix' => FALSE, + 'format_plural' => 'none', ), )) ->save(); @@ -185,6 +189,24 @@ function testNumberIntegerField() { $this->drupalGet('entity_test/add'); $this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed'); $this->assertRaw('placeholder="4"'); + $this->assertText('PluralPrefix'); + $this->assertText('PluralSuffix'); + + // Turn off the prefix and suffix and test. + entity_get_form_display('entity_test', 'entity_test', 'default') + ->setComponent($field_name, array( + 'type' => 'number', + 'settings' => array( + 'placeholder' => '7', + 'plural_format' => FALSE, + ), + )) + ->save(); + $this->drupalGet('entity_test/add'); + $this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed'); + $this->assertRaw('placeholder="7"'); + $this->assertNoText('PluralPrefix'); + $this->assertNoText('PluralSuffix'); // Submit a valid integer $value = rand($minimum, $maximum); @@ -248,15 +270,17 @@ function testNumberIntegerField() { $this->assertNoFieldByXpath('//div[@content="' . $valid_entry . '"]', NULL, 'The "content" attribute is not present since the Prefix is not being displayed'); } - // Test for the content attribute when a Prefix is displayed. Presumably this also tests for the attribute when a Suffix is displayed. + // Test for the content attribute using the field setting for plural + // prefix/suffix formatting. entity_get_display('entity_test', 'entity_test', 'default') ->setComponent($field_name, array( 'type' => 'number_integer', 'settings' => array( - 'prefix_suffix' => TRUE, + 'format_plural' => 'field', ), )) ->save(); + $integer_value = '123'; $this->drupalGet('entity_test/add'); $edit = array( @@ -267,7 +291,34 @@ function testNumberIntegerField() { $id = $match[1]; $this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created'); $this->drupalGet('entity_test/' . $id); - $this->assertFieldByXPath('//div[@content="' . $integer_value . '"]', 'ThePrefix' . $integer_value, 'The "content" attribute has been set to the value of the field, and the prefix is being displayed.'); + $this->assertFieldByXPath('//div[@content="' . $integer_value . '"]', 'PluralPrefix' . $integer_value . 'PluralSuffix', 'The "content" attribute has been set to the value of the field, and the prefix and suffix are being displayed.'); + + // Test with a singular value. + $integer_value = '1'; + $this->drupalGet('entity_test/add'); + $edit = array( + "{$field_name}[0][value]" => $integer_value, + ); + $this->drupalPostForm(NULL, $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created'); + $this->drupalGet('entity_test/' . $id); + $this->assertFieldByXPath('//div[@content="' . $integer_value . '"]', 'ThePrefix' . $integer_value, 'The "content" attribute has been set to the value of the field, and the singular prefix is being displayed.'); + + // Change to using an overridden plural formatting string. + entity_get_display('entity_test', 'entity_test', 'default') + ->setComponent($field_name, array( + 'type' => 'number_integer', + 'settings' => array( + 'format_plural' => 'formatter', + 'format_plural_string' => 'NewPrefix@count', + ), + )) + ->save(); + $this->drupalGet('entity_test/' . $id); + $this->assertFieldByXPath('//div[@content="' . $integer_value . '"]', 'NewPrefix' . $integer_value, 'The "content" attribute has been set to the value of the field, and the overridden prefix is being displayed.'); + } /** @@ -394,8 +445,7 @@ function testNumberFormatter() { 'entity_type' => 'node', 'bundle' => $type, 'settings' => array( - 'prefix' => $prefix, - 'suffix' => $suffix + 'format_plural_string' => $prefix . '@count' . $suffix, ), ))->save(); @@ -404,8 +454,7 @@ function testNumberFormatter() { 'entity_type' => 'node', 'bundle' => $type, 'settings' => array( - 'prefix' => $prefix, - 'suffix' => $suffix + 'format_plural_string' => $prefix . '@count' . $suffix, ), ))->save(); @@ -456,7 +505,7 @@ function testNumberFormatter() { $this->drupalPostAjaxForm(NULL, array(), "${float_field}_settings_edit"); $edit = array( - "fields[${float_field}][settings_edit_form][settings][prefix_suffix]" => TRUE, + "fields[${float_field}][settings_edit_form][settings][format_plural]" => 'field', "fields[${float_field}][settings_edit_form][settings][scale]" => $scale, "fields[${float_field}][settings_edit_form][settings][decimal_separator]" => $decimal_separator, "fields[${float_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator, @@ -482,7 +531,7 @@ function testNumberFormatter() { $this->drupalPostAjaxForm(NULL, array(), "${integer_field}_settings_edit"); $edit = array( - "fields[${integer_field}][settings_edit_form][settings][prefix_suffix]" => FALSE, + "fields[${integer_field}][settings_edit_form][settings][format_plural]" => 'none', "fields[${integer_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator, ); $this->drupalPostAjaxForm(NULL, $edit, "${integer_field}_plugin_settings_update"); diff --git a/core/modules/field_ui/src/Tests/EntityDisplayTest.php b/core/modules/field_ui/src/Tests/EntityDisplayTest.php index 7c7a48b..d9cd658 100644 --- a/core/modules/field_ui/src/Tests/EntityDisplayTest.php +++ b/core/modules/field_ui/src/Tests/EntityDisplayTest.php @@ -220,6 +220,7 @@ public function testFieldComponent() { // Check that the getFormatter() method returns the correct formatter plugin. $formatter = $display->getRenderer($field_name); $this->assertEqual($formatter->getPluginId(), $default_formatter); + $formatter_settings['_settings_langcode'] = 'en'; $this->assertEqual($formatter->getSettings(), $formatter_settings); // Check that the formatter is statically persisted, by assigning an diff --git a/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php b/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php index b002106..6138b7c 100644 --- a/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php +++ b/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php @@ -91,6 +91,7 @@ public function testFieldComponent() { // Check that the getWidget() method returns the correct widget plugin. $widget = $form_display->getRenderer($field_name); $this->assertEqual($widget->getPluginId(), $default_widget); + $widget_settings['_settings_langcode'] = 'en'; $this->assertEqual($widget->getSettings(), $widget_settings); // Check that the widget is statically persisted, by assigning an diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml index b61f1ad..c6b5b1c 100644 --- a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml +++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_nid_argument.yml @@ -106,7 +106,7 @@ display: type: number_integer settings: thousand_separator: '' - prefix_suffix: true + format_plural: 'field' group_column: value group_columns: { } group_rows: true diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 8addfd1..edcde9e 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -13,6 +13,7 @@ use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PrivateStream; use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Drupal\system\SystemRequirements; use Symfony\Component\HttpFoundation\Request; @@ -1845,5 +1846,120 @@ function system_update_8013() { } /** + * Update numeric field prefix/suffix configuration. + */ +function system_update_8014($sandbox) { + // Update prefix/suffix on numeric fields (types: float, decimal, integer) + // to new format_plural_string configuration. Also update associated + // formatters and widgets. + + $views_fields_to_update = []; + + // Locate all the numeric field storage config items. + foreach (['float', 'decimal', 'integer'] as $type) { + $field_storage_configs = \Drupal::entityManager()->getStorage('field_storage_config')->loadByProperties(array('type' => $type)); + if (!$field_storage_configs) { + continue; + } + foreach ($field_storage_configs as $field_storage) { + + // For each field storage item, locate the field config items. + $field_name = $field_storage->getName(); + $field_configs = \Drupal::entityManager()->getStorage('field_config')->loadByProperties(array('field_name' => $field_name)); + if (!$field_configs) { + continue; + } + + // Save the field name to a list to update it in views outside this loop. + $views_fields_to_update[] = $field_name; + + foreach ($field_configs as $field) { + + // Update the prefix/suffix settings on the field config to the new + // format_plural_string setting. + $settings = $field->get('settings'); + $prefix = isset($settings['prefix']) ?: ''; + $suffix = isset($settings['suffix']) ?: ''; + if (($prefix || $suffix) && (!isset($settings['format_plural_string']) || !$settings['format_plural_string'])) { + // In case they were using the previous system to separate + // plural values by |, make this into a proper format_plural + // object. + $prefixes = explode('|', $prefix); + $suffixes = explode('|', $suffix); + $length = max(count($prefixes), count($suffixes)); + $strings = []; + for ($i = 0; $i < $length; $i++) { + $prefix = isset($prefixes[$i]) ?: $prefixes[count($prefixes) - 1]; + $suffix = isset($suffixes[$i]) ?: $suffixes[count($suffixes) - 1]; + $strings[] = $prefix . '@count' . $suffix; + } + $settings['format_plural_string'] = implode(PluralTranslatableMarkup::DELIMITER, $strings); + } + else { + $settings['format_plural_string'] = ''; + } + + unset($settings['prefix']); + unset($settings['suffix']); + $field->set('settings', $settings); + $field->save(); + + // Locate formatters on entity view displays for each field. + $properties = array( + 'targetEntityType' => $field->getTargetEntityTypeId(), + 'bundle' => $field->getTargetBundle() + ); + if ($view_displays = \Drupal::entityManager()->getStorage('entity_view_display')->loadByProperties($properties)) { + foreach ($view_displays as $view_display) { + + // Update the formatter setting prefix_suffix (Boolean) to be + // format_plural (values: 'field' (TRUE) or 'none' (FALSE)), + // and set format_plural_string to an empty string. + if ($component = $view_display->getComponent($field_name)) { + $settings = $component['settings']; + $settings['format_plural'] = (isset($settings['prefix_suffix']) && $settings['prefix_suffix']) ? 'field' : 'none'; + unset($settings['prefix_suffix']); + $settings['format_plural_string'] = ''; + $component['settings'] = $settings; + $view_display->setComponent($field_name, $component)->save(); + } + } + } + } + } + } + + // Now do the same formatter update as above for formatters that are + // part of views. There isn't a way to query for which views might have + // them, so we have to go through all the displays on all the views. + if (count($views_fields_to_update)) { + $config_factory = \Drupal::configFactory(); + foreach ($config_factory->listAll('views.view.') as $view_config_name) { + $view = $config_factory->getEditable($view_config_name); + $displays = $view->get('display'); + $changed = FALSE; + foreach ($displays as $display_name => $display) { + if (!empty($display['display_options']['fields'])) { + foreach ($display['display_options']['fields'] as $field_name => $field) { + if (isset($field['field']) && in_array($field['field'], $views_fields_to_update) && !empty($field['settings'])) { + $settings = $field['settings']; + $settings['format_plural'] = (isset($settings['prefix_suffix']) && $settings['prefix_suffix']) ? 'field' : 'none'; + unset($settings['prefix_suffix']); + $settings['format_plural_string'] = ''; + $displays[$display_name]['display_options']['fields'][$field_name]['settings'] = $settings; + $changed = TRUE; + } + } + } + } + if ($changed) { + $view->set('display', $displays); + $view->save(); + } + } + } +} + +/** * @} End of "addtogroup updates-8.0.0-beta". */ diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml index 96d0559..a99eb9a 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml @@ -140,7 +140,7 @@ display: type: number_integer settings: thousand_separator: '' - prefix_suffix: true + format_plural: 'field' group_column: value group_columns: { } group_rows: true diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php index f2a3778..95922bd 100644 --- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php +++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php @@ -195,6 +195,7 @@ protected function buildFields(array $values) { if ($values && ($field_ids = $this->getRenderableFieldIds())) { $entity_type_id = $this->getEntityTypeId(); + $settings_langcode = $this->view->storage->language()->getId(); // Collect the entities for the relationship, fetch the right translation, // and group by bundle. For each result row, the corresponding entity can @@ -231,6 +232,7 @@ protected function buildFields(array $values) { 'targetEntityType' => $entity_type_id, 'bundle' => $bundle, 'status' => TRUE, + 'langcode' => $settings_langcode, ]); foreach ($display_fields['field_ids'] as $field) { $display->setComponent($field->definition['field_name'], [ diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php index 33253d1..9d0b6ec 100644 --- a/core/modules/views/src/Plugin/views/field/Field.php +++ b/core/modules/views/src/Plugin/views/field/Field.php @@ -471,6 +471,8 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $format = $this->options['type']; $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format); + $settings_langcode = $this->view->storage->language()->getId(); + $settings['_settings_langcode'] = $settings_langcode; $options = array( 'field_definition' => $field, diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml index ec19b13..c391c25 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml @@ -180,7 +180,7 @@ display: type: number_integer settings: thousand_separator: '' - prefix_suffix: true + format_plural: 'field' group_column: value group_columns: { } group_rows: true