diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index fc534df..0295bf0 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -214,3 +214,16 @@ config_dependencies:
label: 'Theme dependencies'
sequence:
- type: string
+
+# Human readable string that is associated with a format.
+text_with_format:
+ type: mapping
+ label: 'Text with format'
+ mapping:
+ value:
+ type: text
+ label: 'Content'
+ translatable: true
+ format:
+ type: string
+ label: 'Text format'
diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module
index 74047f7..d608b0f 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -160,12 +160,30 @@ function config_translation_entity_operation(EntityInterface $entity) {
/**
* Implements hook_config_translation_type_info_alter().
+ *
+ * @todo Convert this hook into a real typed data alter hook,
+ * https://drupal.org/node/2145633.
*/
function config_translation_config_translation_type_info_alter(&$definitions) {
+ $map = array(
+ 'label' => '\Drupal\config_translation\FormElement\Textfield',
+ 'text' => '\Drupal\config_translation\FormElement\Textarea',
+ 'date_format' => '\Drupal\config_translation\FormElement\DateFormat',
+ 'text_with_format' => '\Drupal\config_translation\FormElement\TextFormat',
+ );
+
// Enhance the text and date type definitions with classes to generate proper
// form elements in ConfigTranslationFormBase. Other translatable types will
// appear as a one line textfield.
- $definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea';
- $definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat';
+ foreach ($definitions as $type => &$definition) {
+ if (!isset($definition['form_element_class'])) {
+ if (isset($map[$definition['type']])) {
+ $definition['form_element_class'] = $map[$definition['type']];
+ }
+ elseif (!empty($definition['translatable'])) {
+ $definition['form_element_class'] = '\Drupal\config_translation\FormElement\Textfield';
+ }
+ }
+ }
}
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php
index 4d2bd68..8d60281 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php
@@ -198,7 +198,7 @@ public function buildForm(array $form, array &$form_state, Request $request = NU
);
foreach ($this->mapper->getConfigNames() as $name) {
$form['config_names'][$name] = array('#type' => 'container');
- $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name), $this->config($name)->get(), $this->baseConfigData[$name]);
+ $form['config_names'][$name] += $this->buildConfigForm($name, $this->typedConfigManager->get($name), $this->config($name)->get(), $this->baseConfigData[$name]);
}
$form['actions']['#type'] = 'actions';
@@ -230,7 +230,7 @@ public function submitForm(array &$form, array &$form_state) {
$config_translation = $this->languageManager->getLanguageConfigOverride($this->language->id, $name);
$locations = $this->localeStorage->getLocations(array('type' => 'configuration', 'name' => $name));
- $this->setConfig($this->language, $base_config, $config_translation, $form_values[$name], !empty($locations));
+ $this->setConfig($this->language, $base_config, $config_translation, $form_values[$name], $this->typedConfigManager->get($name), !empty($locations));
// If no overrides, delete language specific configuration file.
$saved_config = $config_translation->get();
@@ -252,6 +252,8 @@ public function submitForm(array &$form, array &$form_state) {
/**
* Formats configuration schema as a form tree.
*
+ * @param string $name
+ * The configuration name.
* @param \Drupal\Core\Config\Schema\Element $schema
* Schema definition of configuration.
* @param array|string $config_data
@@ -269,16 +271,26 @@ public function submitForm(array &$form, array &$form_state) {
* @return array
* An associative array containing the structure of the form.
*/
- protected function buildConfigForm(Element $schema, $config_data, $base_config_data, $open = TRUE, $base_key = '') {
+ protected function buildConfigForm($name, Element $schema, $config_data, $base_config_data, $open = TRUE, $base_key = '') {
$build = array();
foreach ($schema as $key => $element) {
// Make the specific element key, "$base_key.$key".
$element_key = implode('.', array_filter(array($base_key, $key)));
$definition = $element->getDataDefinition() + array('label' => $this->t('N/A'));
- if ($element instanceof Element) {
+
+ // Invoke hook_config_translation_type_info_alter() implementations to
+ // alter the configuration types.
+ $definitions = array(
+ $definition['type'] => &$definition,
+ );
+
+ $this->moduleHandler->alter('config_translation_type_info', $definitions);
+ $element_type = $definition['type'];
+
+ if ($element instanceof Element && !isset($definitions[$element_type]['form_element_class'])) {
// Build sub-structure and include it with a wrapper in the form
// if there are any translatable elements there.
- $sub_build = $this->buildConfigForm($element, $config_data[$key], $base_config_data[$key], FALSE, $element_key);
+ $sub_build = $this->buildConfigForm($name, $element, $config_data[$key], $base_config_data[$key], FALSE, $element_key);
if (!empty($sub_build)) {
// For some configuration elements the same element structure can
// repeat multiple times, (like views displays, filters, etc.).
@@ -307,27 +319,12 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
) + $sub_build;
}
}
- else {
- $definition = $element->getDataDefinition();
-
- // Invoke hook_config_translation_type_info_alter() implementations to
- // alter the configuration types.
- $definitions = array(
- $definition['type'] => &$definition,
- );
- $this->moduleHandler->alter('config_translation_type_info', $definitions);
-
- // Create form element only for translatable items.
- if (!isset($definition['translatable']) || !isset($definition['type'])) {
- continue;
- }
-
+ elseif (isset($definition['form_element_class'])) {
$value = $config_data[$key];
$build[$element_key] = array(
'#theme' => 'config_translation_manage_form_element',
);
$build[$element_key]['source'] = array(
- '#markup' => $base_config_data[$key] ? ('' . nl2br($base_config_data[$key] . '')) : t('(Empty)'),
'#title' => $this->t(
'!label (!source_language)',
array(
@@ -336,13 +333,15 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
)
),
'#type' => 'item',
+ '#input' => FALSE,
);
- $definition += array('form_element_class' => '\Drupal\config_translation\FormElement\Textfield');
-
/** @var \Drupal\config_translation\FormElement\ElementInterface $form_element */
$form_element = new $definition['form_element_class']();
+
+ $build[$element_key]['source']['#markup'] = $form_element->getRenderedSource($base_config_data, $key, $this->sourceLanguage, $this->language);
$build[$element_key]['translation'] = $form_element->getFormElement($definition, $this->language, $value);
+ $build[$element_key]['translation']['#parents'] = array_merge(array('config_names', $name), explode('.', $element_key));
}
}
return $build;
@@ -368,6 +367,8 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
* );
* Either format is used, the nested arrays are just containers and not
* needed for saving the data.
+ * @param \Drupal\Core\Config\Schema\Element $schema
+ * Schema definition of configuration.
* @param bool $shipped_config
* (optional) Flag to specify whether the configuration had a shipped
* version and therefore should also be stored in the locale database.
@@ -375,14 +376,19 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
* @return array
* Translation configuration override data.
*/
- protected function setConfig(Language $language, Config $base_config, Config $config_translation, array $config_values, $shipped_config = FALSE) {
- foreach ($config_values as $key => $value) {
- if (is_array($value) && !isset($value['translation'])) {
+ protected function setConfig(Language $language, Config $base_config, Config $config_translation, array $config_values, Element $schema, $shipped_config = FALSE, $base_key = NULL) {
+ foreach ($schema as $key => $element) {
+ if (!isset($config_values[$key])) {
+ continue;
+ }
+ $element_key = implode('.', array_filter(array($base_key, $key)));
+ $definition = $element->getDataDefinition();
+ $value = $config_values[$key];
+ if ($element instanceof Element && empty($definition['translatable'])) {
// Traverse into this level in the configuration.
- $this->setConfig($language, $base_config, $config_translation, $value, $shipped_config);
+ $this->setConfig($language, $base_config, $config_translation, $value, $element, $shipped_config, $element_key);
}
else {
-
// If the configuration file being translated was originally shipped, we
// should update the locale translation storage. The string should
// already be there, but we make sure to check.
@@ -397,10 +403,40 @@ protected function setConfig(Language $language, Config $base_config, Config $co
// If we got a translation, take that, otherwise create a new one.
$translation = reset($translations) ?: $this->localeStorage->createTranslation($conditions);
+ // Some schema types provide a form_element_class which is responsible
+ // for providing us with a translation string. We iterate through the
+ // $key string to check if we have a schema definition for that.
+ $types = explode(".", $key);
+ $type = array_shift($types);
+ // If there is a schema definition, run the
+ // hook_config_translation_type_info_alter.
+ if (isset($schema[$type])) {
+ $element = $schema[$type];
+ $definition = $element->getDataDefinition();
+
+ // Invoke hook_config_translation_type_info_alter() implementations to
+ // alter the configuration types.
+ $definitions = array(
+ $definition['type'] => &$definition,
+ );
+ $this->moduleHandler->alter('config_translation_type_info', $definitions);
+ }
+
+ // If form_element_class is set, create a new object of this class and
+ // let it return the string of the translation.
+ if (isset($definition['form_element_class'])) {
+ /** @var \Drupal\config_translation\FormElement\ElementInterface $form_element */
+ $form_element = new $definition['form_element_class']();
+
+ $translation_string = $form_element->getTranslationString($value);
+ } else {
+ $translation_string = $value;
+ }
+
// If we have a new translation or different from what is stored in
// locale before, save this as an updated customize translation.
- if ($translation->isNew() || $translation->getString() != $value['translation']) {
- $translation->setString($value['translation'])
+ if ($translation->isNew() || $translation->getString() != $translation_string) {
+ $translation->setString($translation_string)
->setCustomized()
->save();
}
@@ -408,8 +444,8 @@ protected function setConfig(Language $language, Config $base_config, Config $co
// Save value, if different from the source value in the base
// configuration. If same as original configuration, remove override.
- if ($base_config->get($key) !== $value['translation']) {
- $config_translation->set($key, $value['translation']);
+ if ($base_config->get($key) !== $value) {
+ $config_translation->set($element_key, $value);
}
else {
$config_translation->clear($key);
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/FormElement/Element.php b/core/modules/config_translation/lib/Drupal/config_translation/FormElement/Element.php
index 40a43c2..ce33fcb 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/FormElement/Element.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/FormElement/Element.php
@@ -7,6 +7,8 @@
namespace Drupal\config_translation\FormElement;
+use Drupal\Core\Language\Language;
+
/**
* Defines a base class for form elements.
*/
@@ -41,4 +43,18 @@ protected function translationManager() {
return $this->translationManager;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getRenderedSource($base_config_data, $key, Language $source_language, Language $language) {
+ return $base_config_data[$key] ? ('' . nl2br($base_config_data[$key] . '')) : t('(Empty)');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTranslationString($value) {
+ return $value;
+ }
+
}
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/FormElement/ElementInterface.php b/core/modules/config_translation/lib/Drupal/config_translation/FormElement/ElementInterface.php
index 9072cfc..3713024 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/FormElement/ElementInterface.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/FormElement/ElementInterface.php
@@ -29,4 +29,34 @@
*/
public function getFormElement(array $definition, Language $language, $value);
+ /**
+ * Returns the rendered source element for a given configuration definition.
+ *
+ * @param array|string $base_config_data
+ * Configuration object of base language, a string when done traversing
+ * the data building each sub-structure for the form.
+ * @param string $key
+ * The key in the configuration object.
+ * @param \Drupal\Core\Language\Language $source_language
+ * Thee source language of the configuration object.
+ * @param \Drupal\Core\Language\Language $language
+ * Language object to display the translation form for.
+ *
+ * @return string
+ * The rendered value in source language.
+ */
+ public function getRenderedSource($base_config_data, $key, Language $source_language, Language $language);
+
+
+ /**
+ * Returns the translation to be used in string storage.
+ *
+ * @param mixed $value
+ * The value of the translation element.
+ *
+ * @return string
+ * String of translation.
+ */
+ public function getTranslationString($value);
+
}
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/FormElement/TextFormat.php b/core/modules/config_translation/lib/Drupal/config_translation/FormElement/TextFormat.php
new file mode 100644
index 0000000..522dbda
--- /dev/null
+++ b/core/modules/config_translation/lib/Drupal/config_translation/FormElement/TextFormat.php
@@ -0,0 +1,46 @@
+ 'text_format',
+ '#default_value' => $value['value'],
+ '#format' => $value['format'],
+ '#title' => $this->t($definition['label']) . ' (' . $language->name . ')',
+ '#attributes' => array('lang' => $language->id),
+ );
+ return $element;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRenderedSource($base_config_data, $key, Language $source_language, Language $language) {
+ $value = check_markup($base_config_data[$key]['value'], $base_config_data[$key]['format'], $source_language->id);
+ return $value ? '' . $value . '' : t('(Empty)');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTranslationString($value) {
+ return $value['value'];
+ }
+
+}
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationFormTest.php b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationFormTest.php
index c5bce3f..591c796 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationFormTest.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationFormTest.php
@@ -20,7 +20,7 @@ class ConfigTranslationFormTest extends WebTestBase {
*
* @var array
*/
- public static $modules = array('config_translation', 'config_translation_test');
+ public static $modules = array('config_translation', 'config_translation_test', 'editor');
/**
* The plugin ID of the mapper to test.
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
index 405aaf5..d32e894 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
@@ -23,7 +23,7 @@ class ConfigTranslationUiTest extends WebTestBase {
*
* @var array
*/
- public static $modules = array('node', 'contact', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual');
+ public static $modules = array('node', 'contact', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual', 'filter');
/**
* Languages to enable.
@@ -675,6 +675,57 @@ public function testThemeDiscovery() {
}
/**
+ * Test text_format translation.
+ */
+ public function testTextFormatTranslation() {
+ $this->drupalLogin($this->admin_user);
+ $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
+
+ $config_parsed = $file_storage->read('config_translation_test.content');
+ $config_parsed_compare = array(
+ 'content' => array(
+ 'value' => $config_parsed['content']['value'],
+ 'format' => $config_parsed['content']['format'],
+ ),
+ );
+
+ $expected = array(
+ 'content' => array(
+ 'value' => 'Hello World',
+ 'format' => 'plain_text',
+ ),
+ );
+ $this->assertEqual($expected, $config_parsed_compare);
+
+ $translation_base_url = 'admin/config/media/file-system/translate';
+ $this->drupalGet($translation_base_url);
+
+ // 'Add' link should be present for French translation.
+ $translation_page_url = "$translation_base_url/fr/add";
+ $this->assertLinkByHref($translation_page_url);
+
+ $this->drupalGet($translation_page_url);
+
+ // Update translatable fields.
+ $edit = array(
+ 'config_names[config_translation_test.content][content][translation][value]' => 'Hello World - FR',
+ );
+
+ // Save language specific version of form.
+ $this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
+
+ // Get translation and check we've got the right value.
+ $config_parsed = $file_storage->read('language.config.fr.config_translation_test.content');
+ $expected = array(
+ 'content' => array(
+ 'value' => 'Hello World - FR',
+ 'format' => 'plain_text',
+ ),
+ );
+ $this->assertEqual($expected, $config_parsed);
+ }
+
+ /**
* Gets translation from locale storage.
*
* @param $config_name
diff --git a/core/modules/config_translation/tests/modules/config_translation_test/config/config_translation_test.content.yml b/core/modules/config_translation/tests/modules/config_translation_test/config/config_translation_test.content.yml
new file mode 100644
index 0000000..55aa430
--- /dev/null
+++ b/core/modules/config_translation/tests/modules/config_translation_test/config/config_translation_test.content.yml
@@ -0,0 +1,6 @@
+id: test
+label: 'Test'
+langcode: en
+content:
+ value: "Hello World"
+ format: plain_text
diff --git a/core/modules/config_translation/tests/modules/config_translation_test/config/schema/config_translation_test.schema.yml b/core/modules/config_translation/tests/modules/config_translation_test/config/schema/config_translation_test.schema.yml
new file mode 100644
index 0000000..cea0fa9
--- /dev/null
+++ b/core/modules/config_translation/tests/modules/config_translation_test/config/schema/config_translation_test.schema.yml
@@ -0,0 +1,18 @@
+# Schema for the configuration files of the Configuration translation test module.
+
+config_translation_test.content:
+ type: mapping
+ label: 'Content'
+ mapping:
+ id:
+ type: string
+ label: 'Category identifier'
+ label:
+ type: label
+ label: 'Label'
+ langcode:
+ type: string
+ label: 'Default language'
+ content:
+ type: text_with_format
+ label: 'Content'
\ No newline at end of file
diff --git a/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.config_translation.yml b/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.config_translation.yml
new file mode 100644
index 0000000..070245c
--- /dev/null
+++ b/core/modules/config_translation/tests/modules/config_translation_test/config_translation_test.config_translation.yml
@@ -0,0 +1,6 @@
+# Attach to file settings for testing. The base route does not matter.
+system.file_system_settings:
+ title: 'Test config translation'
+ base_route_name: system.file_system_settings
+ names:
+ - config_translation_test.content
diff --git a/core/modules/config_translation/tests/themes/config_translation_test_theme/config_translation_test_theme.config_translation.yml b/core/modules/config_translation/tests/themes/config_translation_test_theme/config_translation_test_theme.config_translation.yml
index 7c8cdd8..5d84070 100644
--- a/core/modules/config_translation/tests/themes/config_translation_test_theme/config_translation_test_theme.config_translation.yml
+++ b/core/modules/config_translation/tests/themes/config_translation_test_theme/config_translation_test_theme.config_translation.yml
@@ -1,3 +1,4 @@
+# Attach to performance settings for testing. The base route does not matter.
system.performance_settings:
title: 'Theme translation test'
base_route_name: system.performance_settings