diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml index 130d4cc..8b17713 100644 --- a/core/config/schema/core.data_types.schema.yml +++ b/core/config/schema/core.data_types.schema.yml @@ -56,6 +56,12 @@ label: label: 'Label' translatable: true +# String containing singular and plural forms, separated by EXT. +plural_string: + type: string + label: 'Plural string' + translatable: true + # Internal Drupal path path: type: string diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module index b4f882b..91350c7 100644 --- a/core/modules/config_translation/config_translation.module +++ b/core/modules/config_translation/config_translation.module @@ -188,6 +188,7 @@ function config_translation_config_schema_info_alter(&$definitions) { 'text_format' => '\Drupal\config_translation\FormElement\TextFormat', 'mapping' => '\Drupal\config_translation\FormElement\ListElement', 'sequence' => '\Drupal\config_translation\FormElement\ListElement', + 'plural_string' => '\Drupal\config_translation\FormElement\PluralString', ); // Enhance the text and date type definitions with classes to generate proper diff --git a/core/modules/config_translation/src/FormElement/FormElementBase.php b/core/modules/config_translation/src/FormElement/FormElementBase.php index e13cc31..7db01e1 100644 --- a/core/modules/config_translation/src/FormElement/FormElementBase.php +++ b/core/modules/config_translation/src/FormElement/FormElementBase.php @@ -91,7 +91,6 @@ public function getTranslationBuild(LanguageInterface $source_language, Language * A render array for the source value. */ protected function getSourceElement(LanguageInterface $source_language, $source_config) { - // @todo Should support singular+plurals https://www.drupal.org/node/2454829 if ($source_config) { $value = '' . nl2br($source_config) . ''; } @@ -162,7 +161,6 @@ protected function getSourceElement(LanguageInterface $source_language, $source_ */ protected function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) { // Add basic properties that apply to all form elements. - // @todo Should support singular+plurals https://www.drupal.org/node/2454829 return array( '#title' => $this->t('!label (!source_language)', array( '!label' => $this->t($this->definition['label']), diff --git a/core/modules/config_translation/src/FormElement/PluralString.php b/core/modules/config_translation/src/FormElement/PluralString.php new file mode 100644 index 0000000..99e4cbf --- /dev/null +++ b/core/modules/config_translation/src/FormElement/PluralString.php @@ -0,0 +1,81 @@ +getNumberOfPlurals($source_language->getId()); + $values = explode(LOCALE_PLURAL_DELIMITER, $source_config); + $element = array( + '#type' => 'fieldset', + '#title' => $this->t($this->definition->getLabel()), + '#tree' => TRUE, + ); + for ($i = 0; $i < $plurals; $i++) { + $element[] = array( + '#type' => 'item', + '#title' => SafeMarkup::format('@label (@source_language)', array( + '@label' => $i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form'), + '@source_language' => $source_language->getName(), + )), + '#markup' => SafeMarkup::format('@value', array( + '@langcode' => $source_language->getId(), + '@value' => $values[$i] ?: $this->t('(Empty)'), + )), + ); + } + return $element; + } + + /** + * {@inheritdoc} + */ + protected function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) { + $plurals = $this->getNumberOfPlurals($translation_language->getId()); + $values = explode(LOCALE_PLURAL_DELIMITER, $translation_config); + $element = array( + '#type' => 'fieldset', + '#title' => $this->t($this->definition->getLabel()), + '#tree' => TRUE, + ); + for ($i = 0; $i < $plurals; $i++) { + $element[] = array( + '#type' => 'textfield', + '#title' => SafeMarkup::format('@label (@translation_language)', array( + '@label' => $i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form'), + '@translation_language' => $translation_language->getName(), + )), + '#default_value' => isset($values[$i]) ? $values[$i] : '', + '#attributes' => array('lang' => $translation_language->getId()), + ); + } + return $element; + } + + /** + * {@inheritdoc} + */ + public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL) { + $config_values = implode(LOCALE_PLURAL_DELIMITER, $config_values); + parent::setConfig($base_config, $config_translation, $config_values, $base_key); + } + +} diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php index c7218df..172bc8e 100644 --- a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php +++ b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php @@ -89,6 +89,7 @@ protected function setUp() { 'administer themes', 'bypass node access', 'administer content types', + 'translate interface', ] ); // Create and login user. @@ -610,6 +611,53 @@ public function testViewsTranslationUI() { } /** + * Test translation of plural strings with multiple plural forms in config. + */ + public function testPluralConfigStrings() { + $this->drupalLogin($this->adminUser); + + // First import a .po file with multiple plural forms. + // This will also automatically add the 'sl' language. + $name = tempnam('temporary://', "sl_") . '.po'; + file_put_contents($name, $this->getPoFile()); + $this->drupalPostForm('admin/config/regional/translate/import', array( + 'langcode' => 'sl', + 'files[file]' => $name, + ), t('Import')); + + // Translate the files view, as this one uses numeric formatters. + $description = 'Singular form'; + $field_value = '1 place'; + $field_value_plural = '@count places'; + $translation_url = 'admin/structure/views/view/files/translate/sl/add'; + $this->drupalGet($translation_url); + + // Make sure original text is present on this page, in addition to 2 new + // empty fields. + $this->assertRaw($description); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]', $field_value); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]', $field_value_plural); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]', ''); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]', ''); + + // Then make sure it also works. + $edit = [ + 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]' => $field_value . ' SL', + 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]' => $field_value_plural . ' 1 SL', + 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]' => $field_value_plural . ' 2 SL', + 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]' => $field_value_plural . ' 3 SL', + ]; + $this->drupalPostForm($translation_url, $edit, t('Save translation')); + + // Make sure the values have changed. + $this->drupalGet($translation_url); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]', "$field_value SL"); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]', "$field_value_plural 1 SL"); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]', "$field_value_plural 2 SL"); + $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]', "$field_value_plural 3 SL"); + } + + /** * Test translation storage in locale storage. */ public function testLocaleDBStorage() { @@ -936,4 +984,19 @@ protected function assertDisabledTextarea($id) { ))); } + /** + * Helper function that returns a .po file with multiple plural forms. + */ + public function getPoFile() { + return <<< EOF +msgid "" +msgstr "" +"Project-Id-Version: Drupal 8\\n" +"MIME-Version: 1.0\\n" +"Content-Type: text/plain; charset=UTF-8\\n" +"Content-Transfer-Encoding: 8bit\\n" +"Plural-Forms: nplurals=4; plural=(((n%100)==1)?(0):(((n%100)==2)?(1):((((n%100)==3)||((n%100)==4))?(2):3)));\\n" +EOF; + } + } diff --git a/core/modules/views/config/schema/views.field.schema.yml b/core/modules/views/config/schema/views.field.schema.yml index 1c884fb..39a21d8 100644 --- a/core/modules/views/config/schema/views.field.schema.yml +++ b/core/modules/views/config/schema/views.field.schema.yml @@ -117,7 +117,7 @@ views.field.numeric: type: boolean label: 'Format plural' format_plural_string: - type: label + type: plural_string label: 'Singular and one or more plurals' prefix: type: label