diff --git a/core/includes/config.inc b/core/includes/config.inc
index 3e22534..5e51f65 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -3,6 +3,8 @@
 use Drupal\Core\Config\Config;
 use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Config\NullStorage;
+use Drupal\Core\Config\Metadata\TypedConfig;
+use Drupal\Core\Config\Metadata\MetadataLookup;
 use Drupal\Core\Config\StorageInterface;
 
 /**
@@ -169,6 +171,264 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
 }
 
 /**
+ * Retrieves metadata for a configuration object or key.
+ *
+ * The metadata has the same structure as the configuration object itself but
+ * the configuration values are replaced by an array containing some special
+ * elements, whose keys are prefixed by a dot:
+ * - '.definition', array containing the data type definition of the element.
+ *   The value is a $definition array to be passed to typed_data()->create()
+ *   or \Drupal\Core\TypedData\TypedDataManager::create().
+ * - '.include', name of additional metadata file to be added. The current
+ *   metadata will then be merged on top of it using
+ *   NestedArray::mergeRecursive()
+ * - '.list', contains an array of metadata to be used for each of the children
+ *   elements when there's no other metadata for them. This will also imply
+ *   that the type definition's 'list' property for the parent element will be
+ *   set to TRUE.
+ *
+ * For example, for the config file core/modules/system/config/system.site.yml:
+ * @code
+ *   name: Drupal
+ *   mail: ''
+ *   slogan: ''
+ *   page:
+ *     403: ''
+ *     404: ''
+ *     front: user
+ * @endcode
+ *
+ * The metadata in core/modules/system/meta/system.site.yml:
+ * @code
+ *   name:
+ *     .definition:
+ *       label: 'Site name'
+ *       type: text
+ *   mail:
+ *     .definition:
+ *       label: 'Site mail'
+ *   slogan:
+ *     .definition:
+ *       label: 'Site slogan'
+ *       type: text
+ *   page:
+ *     .definition:
+ *       label: 'Default pages'
+ *     403:
+ *       .definition:
+ *         label: 'Default 403 (access denied) page'
+ *     404:
+ *       .definition:
+ *         label: 'Default 404 (not found) page'
+ *     front:
+ *       .definition:
+ *         label: 'Default front page'
+ * @endcode
+ *
+ * Elements that don't have an explicit type will default to the type 'string'
+ * unless they have children elements (like 'page' in the example) or the
+ * 'list' property set to true, in which case they will default to the type
+ * 'config_element'.
+ *
+ * Nested lists of elements, that contain additional metadata for each of the
+ * elements in a different file are also supported.
+ *
+ * Here are parts of core/modules/user/config/user.mail.yml:
+ * @code
+ *  status_blocked:
+ *    body: "[user:name],\n\nYour account on [site:name] has been blocked.\n\n--  [site:name] team"
+ *    subject: 'Account details for [user:name] at [site:name] (blocked)'
+ *  status_canceled:
+ *    body: "[user:name],\n\nYour account on [site:name] has been canceled.\n\n--  [site:name] team"
+ *    subject: 'Account details for [user:name] at [site:name] (canceled)'
+ * @endcode
+ *
+ * The metadata is in core/modules/user/meta/user.mail.yml:
+ * @code
+ *   .definition:
+ *     label: 'User mails'
+ *   .list:
+ *     .definition:
+ *       label: 'Mail text'
+ *     subject:
+ *       .definition:
+ *         label: 'Subject'
+ *         type: 'text'
+ *     body:
+ *       .definition:
+ *         label: 'Body'
+ *         type: 'text'
+
+ * @endcode
+ *
+ * For every element in the list of user mails, the metadata is specified by
+ * the '.list' key which contains an array of metadata to be used for each of
+ * the nested configuration elements.
+ *
+ * As said above, the type for list elements is config_element, so the
+ * $definition array for the status_blocked array is:
+ * @code
+ *   array(
+ *     'type' => 'config_element',
+ *     'label' => 'Mail text',
+ *   );
+ * @endcode
+ *
+ * While the $definition array for the 'subject' nested property of it is:
+ * @code
+ *   array(
+ *     'label' => 'Subject',
+ *     'type' => 'text',
+ *   );
+ * @endcode
+ *
+ * The % in meta/user.mail.%.yml is a placeholder and the array key can be
+ * used for more specific metadata. So, meta/user.mail.status_canceled.yml
+ * could be used to specify some metadata only for the status_canceled list
+ * element. meta/user.mail.status_canceled.yml would override the values
+ * in meta/user.mail.%.yml as described in
+ * \Drupal\Component\Utility\NestedArray::mergeDeep().
+ *
+ * Other lists use a property inside a the '.include' element for this key.
+ * For example, core/modules/image/config/image.style.medium.yml:
+ * @code
+ *   name: medium
+ *   label: Medium (220x220)
+ *   effects:
+ *     bddf0d06-42f9-4c75-a700-a33cafa25ea0:
+ *       name: image_scale
+ *       data:
+ *         width: '220'
+ *         height: '220'
+ *         upscale: '1'
+ *       weight: '0'
+ *       ieid: bddf0d06-42f9-4c75-a700-a33cafa25ea0
+ * @endcode
+ *
+ * The metadata is in core/modules/image/meta/image.style.%.yml:
+ * @code
+ *   name:
+ *     .definition:
+ *       label: 'Machine name'
+ *       type: string
+ *   label:
+ *     .definition:
+ *       label: 'Label'
+ *       type: text
+ *   effects:
+ *     .definition:
+ *       label: 'Style effects'
+ *     .list:
+ *       .include: 'image.effect.[name]'
+ *       .definition:
+ *         label: 'Image style effect'
+ *       weight:
+ *         .definition:
+ *           label: 'Weight'
+ *           type: integer
+ *       ieid:
+ *         .definition:
+ *           label: 'IEID'
+ * @endcode
+ *
+ * For this example, for each image effect, we have the metadata under the
+ * '.list' key. Each of the effects will have:
+ * - Some common properties that depend on the image style (weight, ieid) and
+ *   since they are specific of image styles (not image effects) they are
+ *   defined right in this element.
+ * - Some common properties for all image effects that are defined in
+ *   image.effect.%.yml
+ * - Some properties specific of a given image effect, like the ones in
+ *   image.effect.image_scale.yml
+ *
+ * In this case we want to reuse the metadata for 'image_scale' every time
+ * that effect is used. So the '[name]' variable in the include is replaced
+ * by the element's 'name' value, which in this
+ * example will indeed be 'image_scale' so to find the metadata for the
+ * bddf0d06-42f9-4c75-a700-a33cafa25ea0 list element,
+ * meta/image.effect.image_scale.yml is used if it exists and
+ * meta/image.effect.%.yml if it does not.
+ *
+ * For example here is meta/image.effect.image_scale.yml:
+ * @code
+ *   .include: 'image.effect.%'
+ *   .definition:
+ *     label: 'Image scale'
+ *   data:
+ *     width:
+ *       .definition:
+ *         label: 'Width'
+ *         type: 'integer'
+ *     height:
+ *       .definition:
+ *         label: 'Height'
+ *         type: 'integer'
+ *     upscale:
+ *       .definition:
+ *         label: 'Upscale'
+ *         type: 'boolean'
+ * @endcode
+ *
+ * Note there is another '.include' at the top of the file which means that
+ * file is included first and then this one adds or overrides properties in
+ * image.effect.%.yml
+ *
+ * And here is meta/image.effect.%.yml:
+ * @code
+ *   .definition:
+ *     label: 'Image style effect'
+ *   name:
+ *     .definition:
+ *       label: 'Machine name'
+ *       type: string
+ *   data:
+ *     .definition:
+ *       label: 'Data'
+ *   weight:
+ *     .definition:
+ *       label: 'Weight'
+ *       type: integer
+ *   ieid:
+ *     .definition:
+ *       label: 'IEID'
+ * @endcode
+ *
+
+ *
+ * In this example, it's visible how the top-level label is overridden in
+ * meta/image.effect.image_scale.yml.
+ *
+ * @see \Drupal\Core\TypedData\TypedDataManager::create()
+ * @see \Drupal\Core\Config\Metadata\ElementBase
+ * @see \Drupal\Core\Config\Metadata\ListElement
+ *
+ * @param string $name
+ *   The name or key of the configuration object, the same as passed to
+ *   config().
+ *
+ * @return \Drupal\Core\Config\Metadata\MetadataLookup
+ *   A metadata array.
+ *
+ * @see \Drupal\Core\Config\Metadata\ElementInterface::getMetadata()
+ */
+function config_metadata($name) {
+  return drupal_container()->get('config.metadata')->offsetGet($name);
+}
+
+/**
+ * Retrieves a configuration wrapper object to access data as typed properties.
+ *
+ * @param string $name
+ *   The name of the configuration object to retrieve.
+ *
+ * @return \Drupal\Core\Config\Metadata\TypedConfig
+ *   A configuration wrapper object.
+ */
+function config_wrapper($name) {
+  return drupal_container()->get('config.typed')->get($name);
+}
+
+/**
  * Writes an array of config file changes from a source storage to a target storage.
  *
  * @param array $config_changes
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index d5983b6..fd2d7ba 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -690,6 +690,12 @@ function install_tasks($install_state) {
       'type' => 'batch',
       'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
     ),
+    'install_update_configuration_translations' => array(
+      'display_name' => st('Translate configuration'),
+      'display' => $needs_translations,
+      'type' => 'batch',
+      'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+    ),
     'install_finished' => array(
       'display_name' => st('Finished'),
     ),
@@ -1673,6 +1679,20 @@ function install_import_translations_remaining(&$install_state) {
 }
 
 /**
+ * Creates configuration translations.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return array
+ *   The batch definition, if there are configuration objects to update.
+ */
+function install_update_configuration_translations(&$install_state) {
+  include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
+  return locale_config_batch_update_components(array('langcode' => $install_state['parameters']['langcode']));
+}
+
+/**
  * Performs final installation steps and displays a 'finished' page.
  *
  * @param $install_state
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 6fc2a87..f07c798 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -323,7 +323,7 @@ public function clear($key) {
   /**
    * Loads configuration data into this object.
    *
-   * @return Drupal\Core\Config\Config
+   * @return \Drupal\Core\Config\Config
    *   The configuration object.
    */
   public function load() {
diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php
index ca36ce7..c959673 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactory.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactory.php
@@ -58,7 +58,7 @@ public function __construct(StorageInterface $storage, EventDispatcher $event_di
    * @param string $name
    *   The name of the configuration object to construct.
    *
-   * @return Drupal\Core\Config\Config
+   * @return \Drupal\Core\Config\Config
    *   A configuration object with the given $name.
    */
   public function get($name) {
diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php
index eceef01..f4344de 100644
--- a/core/lib/Drupal/Core/Config/InstallStorage.php
+++ b/core/lib/Drupal/Core/Config/InstallStorage.php
@@ -15,6 +15,13 @@
 class InstallStorage extends FileStorage {
 
   /**
+   * Folder map indexed by configuration name.
+   *
+   * @var array
+   */
+  protected $folders;
+
+  /**
    * Overrides Drupal\Core\Config\FileStorage::__construct().
    */
   public function __construct() {
@@ -38,17 +45,9 @@ public function __construct() {
    *   afterwards check for a corresponding module or theme.
    */
   public function getFilePath($name) {
-    // Extract the owner.
-    $owner = strtok($name, '.');
-    // Determine the path to the owner.
-    $path = FALSE;
-    foreach (array('profile', 'module', 'theme') as $type) {
-      if ($path = drupal_get_path($type, $owner)) {
-        $file = $path . '/config/' . $name . '.' . static::getFileExtension();
-        if (file_exists($file)) {
-          return $file;
-        }
-      }
+    $folders = $this->getAllFolders();
+    if (isset($folders[$name])) {
+      return $folders[$name] . '/' . $name . '.' . $this->getFileExtension();
     }
     // If any code in the early installer requests a configuration object that
     // does not exist anywhere as default config, then that must be mistake.
@@ -90,6 +89,76 @@ public function rename($name, $new_name) {
    * @throws Drupal\Core\Config\StorageException
    */
   public function listAll($prefix = '') {
-    throw new StorageException('List operation is not allowed during install.');
+    $names = array_keys($this->getAllFolders());
+    if (!$prefix) {
+      return $names;
+    }
+    else {
+      $return = array();
+      foreach ($names as $index => $name) {
+        if (strpos($name, $prefix) === 0 ) {
+          $return[$index] = $names[$index];
+        }
+      }
+      return $return;
+    }
+  }
+
+  /**
+   * Returns a map of all metadata names and the module they belong to.
+   *
+   * @return array
+   *   An array mapping metadata names with directories.
+   */
+  protected function getAllFolders() {
+    if (!isset($this->folders)) {
+      $this->folders = $this->getComponentNames('profile', array(drupal_get_profile()));
+      $this->folders += $this->getComponentNames('module', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0)));
+      $this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes')));
+    }
+    return $this->folders;
   }
+
+  /**
+   * Get all configuration names and folders for a list of modules or themes.
+   *
+   * @param string $type
+   *   Type of components: 'module' | 'theme' | 'profile'
+   * @param array $list
+   *   Array of theme or module names.
+   *
+   * @return array
+   *   Folders indexed by configuration name.
+   */
+  public function getComponentNames($type, $list) {
+    $extension = '.' . $this->getFileExtension();
+    $folders = array();
+    foreach ($list as $name) {
+      $directory = $this->getComponentFolder($type, $name);
+      if (file_exists($directory)) {
+        $files = glob($directory . '/*' . $extension);
+        foreach ($files as $filename) {
+          $name = basename($filename, $extension);
+          $folders[$name] = $directory;
+        }
+      }
+    }
+    return $folders;
+  }
+
+  /**
+   * Get folder inside each component that contains the files.
+   *
+   * @param string $type
+   *   Component type: 'module' | 'theme' | 'profile'
+   * @param string $name
+   *   Component name.
+   *
+   * @return string
+   *   The configuration folder name for this component.
+   */
+  protected function getComponentFolder($type, $name) {
+    return drupal_get_path($type, $name) . '/config';
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigElement.php b/core/lib/Drupal/Core/Config/Metadata/ConfigElement.php
new file mode 100644
index 0000000..b3c4479
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ConfigElement.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ConfigElement.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use InvalidArgumentException;
+
+/**
+ * Defines a generic 'configuration element' data type.
+ *
+ * Wraps configuration data and metadata allowing access to configuration data
+ * using the ComplexDataInterface API. This object may contain any number and
+ * type of nested properties.
+ */
+class ConfigElement extends ElementBase implements ComplexDataInterface {
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::get().
+   */
+  public function get($property_name) {
+    if ($property = $this->getElement($property_name)) {
+      return $property;
+    }
+    else {
+      throw new InvalidArgumentException(format_string("The configuration property @key doesn't exist.", array(
+          '@key' => $property_name,
+      )));
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::set().
+   */
+  public function set($property_name, $value) {
+    // Set the data into the configuration array but behave according to the
+    // interface specification when we've got a null value.
+    if (isset($value)) {
+      $this->value[$property_name] = $value;
+      return $this->get($property_name);
+    }
+    else {
+      // In these objects, when clearing the value, the property is gone.
+      // As this needs to return a property, we get it before we delete it.
+      $property = $this->get($property_name);
+      unset($this->value[$property_name]);
+      $property->setValue($value);
+      return $property;
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties().
+   */
+  public function getProperties($include_computed = FALSE) {
+    return $this->toArray();
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues().
+   */
+  public function getPropertyValues() {
+    return $this->value;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues().
+   */
+  public function setPropertyValues($values) {
+    foreach ($values as $name => $value) {
+      $this->value[$name] = $value;
+    }
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
+   */
+  public function getPropertyDefinition($name) {
+    $value = isset($this->value[$name]) ? $this->value[$name] : NULL;
+    $metadata = $this->getElementMetadata($key);
+    $definition = ElementBase::buildDefinition($value, $metadata);
+    return $definition ? $definition : FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   */
+  public function getPropertyDefinitions() {
+    $list = array();
+    foreach ($this->getAllKeys() as $key) {
+      $list[$key] = $this->getPropertyDefinition($key);
+    }
+    return $list;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
+   */
+  public function isEmpty() {
+    return empty($this->value);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/ElementBase.php b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php
new file mode 100644
index 0000000..b5bc6af
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php
@@ -0,0 +1,392 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ElementBase.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\Type\TypedData;
+
+use ArrayIterator;
+use IteratorAggregate;
+
+/**
+ * Defines a 'configuration wrapper element' data type.
+ *
+ * Wraps configuration data and metadata allowing access to configuration data
+ * using the TypedDataInterface API. These objects may be nested.
+ */
+abstract class ElementBase extends TypedData implements IteratorAggregate, ElementInterface {
+
+  /**
+   * The configuration key relative to the parent's.
+   *
+   * @var string
+   */
+  protected $key;
+
+  /**
+   * The configuration data.
+   *
+   * @var array
+   */
+  protected $value;
+
+  /**
+   * Full metadata for the element.
+   *
+   * @var array
+   */
+  protected $metadata;
+
+  /**
+   * Parent configuration wrapper.
+   *
+   * @var \Drupal\Core\Config\Metadata\ElementInterface
+   */
+  protected $parent;
+
+  /**
+   * Gets contained typed data object.
+   *
+   * @param string $key
+   *   Configuration key.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface|null
+   *   The property object or null if the property doesn't exist.
+   */
+  protected function getElement($key) {
+    // Only attempt to build the element if it actually has configuration data.
+    if (isset($this->value[$key])) {
+      return $this->buildElement($this->value[$key], $this->getElementMetadata($key), $key, $this);
+    }
+    else {
+      return NULL;
+    }
+  }
+
+  /**
+   * Gets the built metadata array for a child element.
+   *
+   * @param string $key
+   *   Key from the configuration data array.
+   *
+   * @return array
+   *   The metadata for the element, defaults to an empty array.
+   */
+  protected function getElementMetadata($key) {
+    // Metadata for nested elements must be built before.
+    if (isset($this->metadata[$key])) {
+      return $this->metadata[$key];
+    }
+    elseif (isset($this->metadata['.list'])) {
+      return $this->metadata['.list'];
+    }
+    else {
+      return array();
+    }
+  }
+
+  /**
+   * Gets valid configuration data keys.
+   *
+   * @return array
+   *   Array of valid configuration data keys.
+   */
+  protected function getAllKeys() {
+    return is_array($this->value) ? array_keys($this->value) : array();
+  }
+
+  /**
+   * Gets typed data properties as array.
+   *
+   * @return array
+   *   List of TypedDataInterface objects indexed by key.
+   */
+  protected function toArray() {
+    $array = array();
+    foreach ($this->getAllKeys() as $key) {
+      $array[$key] = $this->getElement($key);
+    }
+    return array_filter($array);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ElementInterface::getKey().
+   */
+  public function getKey() {
+    $parent = isset($this->parent) ? $this->parent->getKey() : NULL;
+    return $parent || is_numeric($parent) ? $parent . '.' . $this->key : $this->key;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ElementInterface::getLabel().
+   */
+  public function getLabel() {
+    return isset($this->definition['label']) ? $this->definition['label'] : $this->getKey();
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ElementInterface::getMetadata().
+   */
+  public function getMetadata() {
+    return $this->metadata;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ElementInterface::setMetadata().
+   */
+  public function setMetadata($metadata) {
+    // Process '.include' directive if present.
+    $this->metadata = $this->processInclude($metadata);
+  }
+
+
+  /**
+   * Implements Drupal\Core\TypedData\TypedDataInterface::validate().
+   */
+  public function validate() {
+    // This will be ok if we have any config data at all.
+    return isset($this->value);
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ContextAwareInterface::getName().
+   */
+  public function getName() {
+    return $this->key;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ContextAwareInterface::setName().
+   */
+  public function setName($name) {
+    $this->key = $name;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ContextAwareInterface::getParent().
+  */
+  public function getParent() {
+    return $this->parent;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ContextAwareInterface::setParent().
+  */
+  public function setParent($parent) {
+    $this->parent = $parent;
+  }
+
+  /**
+   * Implements ArrayAccess::offsetExists().
+   */
+  public function offsetExists($offset) {
+    return isset($this->value[$offset]);
+  }
+
+  /**
+   * Implements ArrayAccess::offsetGet().
+   */
+  public function offsetGet($offset) {
+    return $this->getElement($offset);
+  }
+
+  /**
+   * Implements ArrayAccess::offsetSet().
+   */
+  public function offsetSet($offset, $value) {
+    if ($value instanceof TypedDataInterface) {
+      $value = $value->getValue();
+    }
+    $this->value[$offset] = $value;
+  }
+
+  /**
+   * Implements ArrayAccess::offsetUnset().
+   */
+  public function offsetUnset($offset) {
+    unset($this->value[$offset]);
+  }
+
+  /**
+   * Implements Countable::count().
+   */
+  public function count() {
+    return count($this->getValue());
+  }
+
+  /**
+   * Implements IteratorAggregate::getIterator().
+   */
+  public function getIterator() {
+    return new ArrayIterator($this->toArray());
+  }
+
+  /**
+   * Builds a typed data element for a piece of configuration data.
+   *
+   * @param mixed $value
+   *   Configuration data to be used as value.
+   * @param array $metadata
+   *   Metadata for the element.
+   * @param string $key
+   *   Nested configuration key to be used as element's name.
+   * @param \Drupal\Core\Config\Metadata\ElementInterface $parent
+   *   (optional) Element to be used as parent of this one. Defaults to NULL.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   A TypedDataInterface object containing the configuration value.
+   */
+  public static function buildElement($value, $metadata, $key, $parent = NULL) {
+    $definition = self::buildDefinition($value, $metadata);
+    $context = array('name' => $key, 'parent' => $parent);
+    $element = typed_data()->create($definition, $value, $context);
+    if ($element instanceof ElementInterface) {
+      // If this is a nested ConfigElement we need to set the metadata for it.
+      $element->setMetadata($metadata);
+    }
+    return $element;
+  }
+
+  /**
+   * Builds element definition from configuration metadata.
+   *
+   * Though the configuration metadata contains basically the typed data
+   * element's definition, we need to take out the dot prefix from property
+   * names and add some default data types.
+   *
+   * @param mixed $data
+   *   The configuration data for the element.
+   *
+   * @param array $metadata
+   *   The raw metadata array for the element.
+   *
+   * @return array
+   *   The elemement's data definition.
+   */
+  public static function buildDefinition($data, $metadata) {
+    $definition = isset($metadata['.definition']) ? $metadata['.definition'] : array();
+    // If we have a '.list' directive we assume this is a list.
+    if (isset($metadata['.list'])) {
+      $definition += array('list' => TRUE);
+    }
+    // The default type will depend on whether data is an array.
+    if (!isset($definition['type'])) {
+      if (is_array($data) || !empty($definition['list']) || !empty($definition['list settings'])) {
+        $definition['type'] = 'config_element';
+      }
+      else {
+        $definition['type'] = 'string';
+      }
+    }
+    return $definition;
+  }
+
+  /**
+   * Processes '.include' keyword applying variable replacements.
+   *
+   * The include name may contain one or more variables to be replaced. These
+   * are enclosed in square brackets like '[name]' and follow the replacement
+   * rules defined by the replaceVariable() method.
+   *
+   * This method is called recursively in case the included metadata has also
+   * an '.include' keyword.
+   *
+   * @param array $metadata
+   *   Metadata array for the element.
+   */
+  protected function processInclude($metadata) {
+    if (isset($metadata['.include'])) {
+      // Parse the include string for variable names in squae brackets.
+      $self = $this;
+      $name = preg_replace_callback(
+        "/\[(.*)\]/",
+        function($matches) use ($self) {return $self->replaceVariable($matches[1]);},
+        $metadata['.include']
+      );
+      if ($include = config_metadata($name)) {
+        // There may be nested '.include' directives, we need to process them too.
+        $include = $this->processInclude($include);
+        $metadata = NestedArray::mergeDeep($include, $metadata);
+      }
+    }
+    return $metadata;
+  }
+
+  /**
+   * Replaces variable values in included names with configuration data.
+   *
+   * Variable values are nested configuration keys that will be replaced by
+   * their value or some of these special strings:
+   * - '%key', will be replaced by the element's key.
+   * - '%parent', to reference the parent element.
+   *
+   * There may be nested configuration keys separated by dots or more complex
+   * patterns like '%parent.name' which references the 'name' value of the
+   * parent element.
+   *
+   * Example patterns:
+   * - 'name.subkey', indicates a nested value of the current element.
+   * - '%parent.name', will be replaced by the 'name' value of the parent.
+   * - '%parent.%key', will be replaced by the parent element's key.
+   * - '%parent.%parent.name', will be replaced by the parent's parent 'name'.
+   *
+   * @param string $value
+   *   Variable value to be replaced.
+   *
+   * @return string
+   *   The replaced value if a replacement found or the original value if not.
+   */
+  public function replaceVariable($value) {
+    $parts = explode('.', $value);
+    $element = $this;
+    $data = $this->getValue();
+    // Process each value part, one at a time.
+    while ($name = array_shift($parts)) {
+      switch ($name) {
+        case '%key':
+          // Element's key, it should be a final property.
+          if (!$parts) {
+            return $element->getName();
+          }
+          else {
+            // If '%key' is not a final property, return original value.
+            return $value;
+          }
+          break;
+        case '%parent':
+          // Property from the parent element.
+          $element = $element->getParent();
+          if ($element && $element instanceof ElementIterface) {
+            $data = $element->getValue();
+          }
+          else {
+            // No parent or not the right type, return original value.
+            return $value;
+          }
+          break;
+        default:
+          // This should be a configuration key.
+          if (is_array($data) && isset($data[$name])) {
+            $data = $data[$name];
+            // If no more parts left, this is the final property.
+            if (!$parts) {
+              return (string)$data;
+            }
+          }
+          else {
+            // Non existent key, return original value
+            return $value;
+          }
+          break;
+      }
+    }
+    // If no return value found, return the original value.
+    return $value;
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/ElementInterface.php b/core/lib/Drupal/Core/Config/Metadata/ElementInterface.php
new file mode 100644
index 0000000..c1d5276
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ElementInterface.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ElementInterface.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\TypedData\ContextAwareInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+use ArrayAccess;
+use Countable;
+use Traversable;
+
+/**
+ * Interface for data structures that contain configuration data and metadata.
+ */
+interface ElementInterface extends Traversable, ArrayAccess, Countable, TypedDataInterface, ContextAwareInterface {
+
+  /**
+   * Gets configuration key for this object.
+   *
+   * @return string
+   *   Configuration key for this structure.
+   */
+  public function getKey();
+
+  /**
+   * Gets human readable label for this structure or children of it.
+   *
+   * @return string
+   *   The object label.
+   */
+  public function getLabel();
+
+  /**
+   * Gets raw configuration metadata array for this element.
+   *
+   * @return array
+   *   The full metadata array for this element.
+   *
+   * @see config_metadata()
+   */
+  public function getMetadata();
+
+  /**
+   * Sets the configuration metadata array for the element.
+   *
+   * @param array $metadata
+   *   Array of configuration metadata.
+   *
+   * @see config_metadata()
+   */
+  public function setMetadata($metadata);
+
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/ListElement.php b/core/lib/Drupal/Core/Config/Metadata/ListElement.php
new file mode 100644
index 0000000..4cb494d
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ListElement.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ListElement.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\TypedData\ListInterface;
+
+/**
+ * Defines a 'configuration wrapper' that contains a list of elements.
+ *
+ * This is a configuration element that wraps a list of nested elements in the
+ * configuration data.
+ */
+class ListElement extends ElementBase implements ListInterface {
+  /**
+   * Implements Drupal\Core\TypedData\ListInterface::isEmpty().
+   */
+  public function isEmpty() {
+    return empty($this->data);
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php b/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php
new file mode 100644
index 0000000..779005b
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\MetadataLookup.
+ *
+ * Metadata lives in a modules 'meta' directory, next to the 'config' directory,
+ * which contains the actual configuration. For each configuration file in the
+ * 'config' directory there may be another one with the same name in the 'meta'
+ * directory that contains the metadata for that configuration object.
+ *
+ * The name of the configuration object may contain the placeholder '%' as the
+ * last component, which denotes generic metadata for all objects of this
+ * type. For the 'large' image style, for example, both "image.style.%" and
+ * "image.style.large" would be valid metadata files. In case both of these
+ * files exist, we load the generic metadata (in this example from
+ * "image.style.%") first and add the specific metadata (in this example from
+ * "image.style.large") on top of it.
+ *
+ * @see config_metadata()
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Utility\CacheArray;
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * Extends CacheArray to allow searching and caching metadata.
+ */
+class MetadataLookup extends CacheArray {
+
+  /**
+   * Filesystem marker for base metadata name.
+   */
+  const BASE_MARK = '%';
+
+  /**
+   * Storage to retrieve the metadata.
+   *
+   * @var \Drupal\Core\Config\Metadata\MetadataStorage
+   */
+  protected $metadataStorage;
+
+  /**
+   * Constructs a ConfigMetadata cache object.
+   */
+  public function __construct(MetadataStorage $metadata_storage) {
+    $this->metadataStorage = $metadata_storage;
+    parent::__construct('config:metadata', 'cache');
+  }
+
+  /**
+   * Overrides DrupalCacheArray::resolveCacheMiss().
+   */
+  protected function resolveCacheMiss($offset) {
+    $metadata = $this->metadataStorage->read($offset);
+    // If no metadata with this exact name, try the fallback name.
+    if ($metadata === FALSE && ($basename = $this->getFallbackName($offset))) {
+      $metadata = $this->offsetGet($basename);
+    }
+    $this->storage[$offset] = $metadata ? $metadata : array();
+    return $this->storage[$offset];
+  }
+
+  /**
+   * Gets fallback metadata name.
+   *
+   * @param string $name
+   *   Configuration name or key.
+   *
+   * @return string
+   *   Same name with the last part replaced by the filesystem marker.
+   */
+  protected static function getFallbackName($name) {
+    $replaced = preg_replace('/\.[^.]+$/', '.' . self::BASE_MARK, $name);
+    if ($replaced != $name) {
+      return $replaced;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
new file mode 100644
index 0000000..5f16f41
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\MetadataStorage.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Config\InstallStorage;
+
+/**
+ * Defines the file storage controller for metadata files.
+ */
+class MetadataStorage extends InstallStorage {
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::exists().
+   */
+  public function exists($name) {
+    return array_key_exists($name, $this->getAllFolders());
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\InstallStorage::getComponentFolder().
+   */
+  protected function getComponentFolder($type, $name) {
+    return drupal_get_path($type, $name) . '/meta';
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php b/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php
new file mode 100644
index 0000000..1964ee1
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\TypedConfig.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Config\Config;
+use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListInterface;
+
+/**
+ * Defines the configuration wrapper object.
+ *
+ * This object wraps the full configuration object's data and is the outermost
+ * level for configuration wrappers. All of the others are nested in this one.
+ */
+class TypedConfig extends Config {
+
+  /**
+   * The base typed data element for this configuration data.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected $base;
+
+  /**
+   * Constructs a configuration wrapper object.
+   *
+   * @param string $name
+   *   The configuration object name.
+   * @param array $data
+   *   The configuration data.
+   */
+  public function __construct($name, $data) {
+    $this->name = $name;
+    $this->data = $data;
+  }
+
+  /**
+   * Gets data from this config object.
+   *
+   * @return mixed
+   *   The data that was requested.
+   */
+  public function getData() {
+    return $this->data;
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\Config::setData().
+   */
+  public function setData(array $data) {
+    $this->data = $data;
+    $this->setBase();
+    return $this;
+  }
+
+  /**
+   * Overrides Drupal\Core\Config::get().
+   *
+   * Unlike the get() method of the base configuration object, this one returns
+   * a typed data object instead of raw configuration data.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface|null
+   *   The data that was requested or NULL if the base was not a ListInterface
+   *   or a ComplexDataInterface.
+   */
+  public function get($key = '') {
+    $element = $this->getBase();
+    if (!empty($key)) {
+      // Get nested element recursively.
+      $parts = explode('.', $key);
+      while ($element && $name = array_shift($parts)) {
+        if ($element instanceof ListInterface) {
+          $element = $element[$name];
+        }
+        elseif ($element instanceof ComplexDataInterface) {
+          $element = $element->get($name);
+        }
+        else {
+          $element = NULL;
+        }
+      }
+
+    }
+    return $element;
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\Config::set().
+   */
+  public function set($key, $value) {
+    parent::set($key, $value);
+    $this->setBase();
+    return $this;
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\Config::clear().
+   */
+  public function clear($key) {
+    parent::clear($key);
+    $this->setBase();
+    return $this;
+  }
+
+  /**
+   * Gets the configuration language.
+   *
+   * @return \Drupal\Core\Language\Language
+   *   The language object.
+   */
+  public function getLanguage() {
+    // The default language will be English as this is hardcoded information.
+    $langcode = isset($this->data['language']) ? $this->data['language'] : 'en';
+    return new Language(array('langcode' => $langcode));
+  }
+
+  /**
+   * Gets the base configuration element.
+   */
+  protected function getBase() {
+    if (!isset($this->base)) {
+      $this->base = ElementBase::buildElement($this->data, config_metadata($this->name), '');
+    }
+    return $this->base;
+  }
+
+  /**
+   * Resets the base element's data.
+   */
+  protected function setBase() {
+    if (isset($this->base)) {
+      $this->base->setValue($this->data);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/TypedConfigFactory.php b/core/lib/Drupal/Core/Config/Metadata/TypedConfigFactory.php
new file mode 100644
index 0000000..aa416ad
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/TypedConfigFactory.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\TypedConfigFactory.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Config\ConfigFactory;
+
+/**
+ * Factory for configuration wrapper objects to access data as typed properties.
+ */
+class TypedConfigFactory {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs the typed data config factory.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   */
+  function __construct(ConfigFactory $config_factory) {
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * Returns configuration wrapper object to access data as typed properties.
+   *
+   * @param string $name
+   *   Name of the data.
+   *
+   * @return TypedConfig
+   *   The configuration wrapper as an object.
+   */
+  function get($name) {
+    return new TypedConfig($name, $this->configFactory->get($name)->load()->get());
+  }
+
+}
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 48684e5..fb0da1f 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -68,6 +68,13 @@ public function build(ContainerBuilder $container) {
     $container->register('entity.query', 'Drupal\Core\Entity\Query\QueryFactory')
       ->addArgument(new Reference('service_container'));
 
+    // Add the config metadata service.
+    $container->register('config.metadata.storage', 'Drupal\Core\Config\Metadata\MetadataStorage');
+    $container->register('config.metadata', 'Drupal\Core\Config\Metadata\MetadataLookup')
+      ->addArgument(new Reference('config.metadata.storage'));
+    $container->register('config.typed', 'Drupal\Core\Config\Metadata\TypedConfigFactory')
+      ->addArgument(new Reference('config.factory'));
+
     $container->register('router.dumper', 'Drupal\Core\Routing\MatcherDumper')
       ->addArgument(new Reference('database'));
     $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php
new file mode 100644
index 0000000..6f358b7
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\Tests\ConfigMetadataTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\Core\Config\TypedConfig;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests Metadata for configuration objects.
+ */
+class ConfigMetadataTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('locale', 'image');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Configuration metadata',
+      'description' => 'Tests Metadata for configuration objects.',
+      'group' => 'Configuration',
+    );
+  }
+
+  /**
+   * Tests the basic metadata retrieval layer.
+   */
+  function testBasicMetadata() {
+    // Simple case, straight metadata.
+    $metadata = config_metadata('system.maintenance');
+    $expected = array();
+    $expected['enabled']['.definition'] = array(
+      'label' => 'Put site into maintenance mode',
+      'type' => 'boolean'
+    );
+    $expected['message']['.definition'] = array(
+      'label' =>  'Message to display when in maintenance mode',
+      'type' => 'text',
+    );
+    $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for system.maintenance');
+    // More complex case, fallback to parent name.
+    $metadata = config_metadata('image.effect.image_scale');
+    $expected = array();
+    $expected['.include'] = 'image.effect.image_resize';
+    $expected['width']['.definition'] = array(
+      'label' => 'Width',
+      'type' => 'integer',
+    );
+    $expected['height']['.definition'] = array(
+      'label' => 'Height',
+      'type' => 'integer',
+    );
+    $expected['upscale']['.definition'] = array(
+      'label' => 'Upscale',
+      'type' => 'boolean',
+    );
+    $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.large');
+
+    // Most complex case, metadata from actual configuration element using includes.
+    $metadata = config_wrapper('image.style.medium')->get('effects.bddf0d06-42f9-4c75-a700-a33cafa25ea0.data')->getMetadata();
+    // Build with the previous case expected metadata
+    $expected['.include'] = 'image.effect.[%parent.name]';
+    $expected['.definition']['label'] = 'Data';
+    $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
+  }
+
+  /**
+   * Tests metadata applied to configuration objects.
+   */
+  function testConfigMetadata() {
+    // Try some simple properties.
+    $meta = config_wrapper('system.site');
+    $property = $meta->get('name');
+    $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\String'), 'Got the right wrapper fo the site name property.');
+    $this->assertEqual($property->getType(), 'text', 'Got the right string type for site name data.');
+    $this->assertEqual($property->getValue(), 'Drupal', 'Got the right string value for site name data.');
+
+    $property = $meta->get('page.front');
+    $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\String'), 'Got the right wrapper fo the page.front property.');
+    $this->assertEqual($property->getType(), 'string', 'Got the right type for page.front data (undefined).');
+    $this->assertEqual($property->getValue(), 'user', 'Got the right value for page.front data.');
+
+    // Check nested array of properties.
+    $list = $meta->get('page');
+    $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
+    $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
+    $this->assertEqual($list['front']->getValue(), 'user', 'Got the right value for page.front data from the list.');
+    // And test some ComplexDataInterface methods.
+    $properties = $list->getProperties();
+    $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
+    $values = $list->getPropertyValues();
+    $this->assertTrue(count($values) == 3 && $values['front'] == 'user', 'Got the right property values for site page.');
+
+    // Now let's try something more complex, with nested objects.
+    $wrapper = config_wrapper('image.style.large');
+    $effects = $wrapper->get('effects');
+    // The function is_array() doesn't work with ArrayAccess, so we use count().
+    $this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data');
+    $ieid = key($effects->getValue());
+    $effect = $wrapper->get('effects.' . $ieid);
+    $this->assertTrue(count($effect['data']) && $effect['name']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
+    $this->assertEqual($effect['data']['width']->getType(), 'integer', 'Got the right type for the scale effect width.');
+    $this->assertEqual($effect['data']['width']->getValue(), 480, 'Got the right value for the scale effect width.' );
+    // Try config key and getting nested properties.
+    $key = 'effects.' . $ieid . '.data';
+    $this->assertEqual($effect['data']->getKey(), $key, 'Got the right configuration key for a nested image styple effect');
+    $object = $wrapper->get($key);
+    $this->assertEqual(get_class($effect['data']), get_class($object), 'Got the same object class using different keys with dot notation.');
+    $this->assertEqual($effect['data']->getValue(), $object->getValue(), 'Got the same object values using different keys with dot notation.');
+
+    // Finally update some object using a configuration wrapper.
+    $new_slogan = 'Site slogan for testing configuration metadata';
+    $wrapper = config_wrapper('system.site');
+    $wrapper->set('slogan', $new_slogan);
+    $site_slogan = $wrapper->get('slogan');
+    $this->assertEqual($site_slogan->getValue(), $new_slogan, 'Successfully updated the contained configuration data');
+  }
+
+}
+
diff --git a/core/modules/contact/meta/contact.category.%.yml b/core/modules/contact/meta/contact.category.%.yml
new file mode 100644
index 0000000..3e6b077
--- /dev/null
+++ b/core/modules/contact/meta/contact.category.%.yml
@@ -0,0 +1,18 @@
+id:
+  .definition:
+    label: 'Id'
+label:
+  .definition:
+    type: text
+    label: 'Label'
+recipients:
+  .definition:
+    label: 'Recipients'
+reply:
+  .definition:
+    type: text
+    label: 'Reply'
+weight:
+  .definition:
+    type: integer
+    label: 'Weight'
diff --git a/core/modules/image/meta/image.effect.image_crop.yml b/core/modules/image/meta/image.effect.image_crop.yml
new file mode 100644
index 0000000..f03df09
--- /dev/null
+++ b/core/modules/image/meta/image.effect.image_crop.yml
@@ -0,0 +1,4 @@
+.include: image.effect.image_resize
+anchor:
+  .definition:
+    label: 'Anchor'
diff --git a/core/modules/image/meta/image.effect.image_resize.yml b/core/modules/image/meta/image.effect.image_resize.yml
new file mode 100644
index 0000000..ad647b4
--- /dev/null
+++ b/core/modules/image/meta/image.effect.image_resize.yml
@@ -0,0 +1,8 @@
+width:
+  .definition:
+    label: 'Width'
+    type: integer
+height:
+  .definition:
+    label: 'Height'
+    type: integer
diff --git a/core/modules/image/meta/image.effect.image_rotate.yml b/core/modules/image/meta/image.effect.image_rotate.yml
new file mode 100644
index 0000000..040f38c
--- /dev/null
+++ b/core/modules/image/meta/image.effect.image_rotate.yml
@@ -0,0 +1,10 @@
+degrees:
+  .definition:
+    label: 'Rotation angle'
+bgcolor:
+  .definition:
+    label: 'Background color'
+random:
+  .definition:
+    label: 'Randomize'
+    type: boolean
diff --git a/core/modules/image/meta/image.effect.image_scale.yml b/core/modules/image/meta/image.effect.image_scale.yml
new file mode 100644
index 0000000..e75b418
--- /dev/null
+++ b/core/modules/image/meta/image.effect.image_scale.yml
@@ -0,0 +1,5 @@
+.include: 'image.effect.image_resize'
+upscale:
+  .definition:
+    label: 'Upscale'
+    type: boolean
diff --git a/core/modules/image/meta/image.effect.image_scale_and_crop.yml b/core/modules/image/meta/image.effect.image_scale_and_crop.yml
new file mode 100644
index 0000000..fd39bee
--- /dev/null
+++ b/core/modules/image/meta/image.effect.image_scale_and_crop.yml
@@ -0,0 +1 @@
+.include: image.effect.image_resize
\ No newline at end of file
diff --git a/core/modules/image/meta/image.style.%.yml b/core/modules/image/meta/image.style.%.yml
new file mode 100644
index 0000000..3dcd4d7
--- /dev/null
+++ b/core/modules/image/meta/image.style.%.yml
@@ -0,0 +1,28 @@
+name:
+  .definition:
+    label: 'Machine name'
+label:
+  .definition:
+    label: 'Label'
+    type: text
+effects:
+  .definition:
+    label: 'Style effects'
+  .list:
+    .definition:
+      label: 'Image effect'
+    weight:
+      .definition:
+        label: 'Weight'
+        type: integer
+    ieid:
+      .definition:
+        label: 'IEID'
+    name:
+      .definition:
+        label: 'Machine name'
+    data:
+      .include: 'image.effect.[%parent.name]'
+      .definition:
+        label: 'Data'
+      
\ No newline at end of file
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php
index 3d2fd4a..fb09c0b 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php
@@ -18,6 +18,7 @@
  * $config is always a DrupalConfig object.
  */
 class LocaleConfigSubscriber implements EventSubscriberInterface {
+
   /**
    * Override configuration values with localized data.
    *
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
new file mode 100644
index 0000000..1e72c64
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
@@ -0,0 +1,261 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\locale\LocaleTypedConfig.
+ */
+
+namespace Drupal\locale;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\Config\Metadata\TypedConfig;
+use Drupal\Core\Config\Metadata\ElementInterface;
+use Drupal\Core\TypedData\TranslatableInterface;
+
+/**
+ * Defines the locale configuration wrapper object.
+ *
+ * This is an extension of the base TypedConfig class that provides
+ * translations using the localization system.
+ */
+class LocaleTypedConfig extends TypedConfig implements TranslatableInterface {
+
+  /**
+   * The language code for which this is a translation.
+   *
+   * @var string
+   */
+  protected $translationLangcode;
+
+  /**
+   * Configuration wrapper used as translation source.
+   *
+   * @var \Drupal\locale\LocaleTypedConfig
+   */
+  protected $translationSource;
+
+  /**
+   * String storage object that will be used for translations.
+   *
+   * @var \Drupal\locale\StringStorageInterface $localeStorage
+   */
+  protected $localeStorage;
+
+  /**
+   * Temporary translations cache.
+   *
+   * @var array
+   */
+  protected $translations;
+
+  /**
+   * Constructs a configuration wrapper object.
+   *
+   * @param string $name
+   *   The configuration object name.
+   * @param array $data
+   *   The configuration data.
+   * @param \Drupal\locale\StringStorageInterface $localeStorage;
+   *   (optional) Locale string storage object that will be used for
+   *   translations.
+   */
+  public function __construct($name, $data, StringStorageInterface $localeStorage = NULL) {
+    parent::__construct($name, $data);
+    $this->localeStorage = $localeStorage;
+  }
+
+  /**
+   * Sets translation parameters.
+   *
+   * @param string $langcode
+   *   The language code for the translation.
+   * @param \Drupal\locale\LocaleTypedConfig $source
+   *   Configuration wrapper used as translation source.
+   */
+  public function setTranslation($langcode, LocaleTypedConfig $source) {
+    $this->translationLangcode = $langcode;
+    $this->translationSource = $source;
+    return $this;
+  }
+
+  /**
+   * Checks whether we can translate these languages.
+   *
+   * @param string $from_langcode
+   *   Source language code.
+   * @param string $to_langcode
+   *   Destination language code.
+   *
+   * @return boolean
+   *   TRUE if this translator supports translations for these languages.
+   */
+  protected function canTranslate($from_langcode, $to_langcode) {
+    if ($from_langcode == 'en') {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
+   */
+  public function getTranslationLanguages($include_default = TRUE) {
+    $languages = locale_translatable_language_list();
+    $default = $this->language();
+    if ($include_default) {
+      $languages[$default->langcode] = $default;
+    }
+    else {
+      unset($languages[$default->langcode]);
+    }
+    return $languages;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\TranslatableInterface::getTranslation().
+   */
+  public function getTranslation($langcode, $strict = TRUE) {
+    if (isset($this->translationSource)) {
+      return $this->translationSource->getTranslation($langcode, $strict);
+    }
+    else {
+      $options = array(
+        'source' => $this->language()->langcode,
+        'target' => $langcode,
+        'strict' => $strict,
+      );
+      $data = $this->getTranslatedData($this->get(), $options);
+      $translation = new self($this->name, $data);
+      $translation->setTranslation($langcode, $this);
+      return $translation;
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\TranslatableInterface::language().
+   */
+  public function language() {
+    if (isset($this->translationLangcode)) {
+      return new Language(array('langcode' => $this->translationLangcode));
+    }
+    else {
+      return $this->getLanguage();
+    }
+  }
+
+  /**
+   * Get translated configuration data.
+   *
+   * @param \Traversable $elements
+   *   Configuration elements.
+   * @param array $options
+   *   Array with options that will depend on the translator used.
+   *
+   * @return array
+   *   Configuration data translated to the requested language.
+   */
+  protected function getTranslatedData($elements, $options) {
+    $strict = !empty($options['strict']);
+    $translation = array();
+    foreach ($elements as $key => $element) {
+      $value = NULL;
+      if ($element instanceof ElementInterface) {
+        $value = $this->getTranslatedData($element, $options);
+      }
+      elseif ($this->translateElement($element, $options)) {
+        $value = $element->getValue();
+      }
+      elseif (!$strict) {
+        $value = $element->getValue();
+      }
+      if ($value || !$strict) {
+        $translation[$key] = $value;
+      }
+    }
+    return $translation;
+  }
+
+  /**
+   * Translates element if it fits our translation criteria.
+   *
+   * For an element to be translatable by locale module it needs to be of type
+   * 'text'. Translatable elements may use these additional keys in their data
+   * definition:
+   * - 'translatable', FALSE to opt out of translation.
+   * - 'locale context', to define the string context.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataInterface $element
+   *   Configuration element.
+   * @param array $options
+   *   Array with translation options that are dependent on the translator.
+   *
+   * @return bool
+   *   Whether the element fits the translation criteria.
+   */
+  public function translateElement($element, $options) {
+    if ($this->canTranslate($options['source'], $options['target'])) {
+      $definition = $element->getDefinition();
+      $value = $element->getValue();
+      if ($value && is_string($value) && $element->getType() == 'text' && (!isset($definition['translatable']) || $definition['translatable'])) {
+        $context = isset($definition['locale context']) ? $definition['locale context'] : '';
+        if ($translation = $this->translateString($options['target'], $value, $context)) {
+          $element->setValue($translation);
+          return TRUE;
+        }
+      }
+    }
+    // The element doesn't have a translation, if strict mode we drop it.
+    return empty($options['strict']);
+  }
+
+  /**
+   * Translates string using the localization system.
+   *
+   * So far we only know how to translate strings from English so we check
+   * whether the source data is English.
+   * Unlike regular t() translations, strings will be added to the source
+   * tables only if this is marked as default data.
+   *
+   * @param string $langcode
+   *   Language code to translate to.
+   * @param string $source
+   *   The source string.
+   * @param string $context
+   *   The string context.
+   *
+   * @return string|bool
+   *   Translated string if there is a translation, FALSE if not.
+   */
+  protected function translateString($langcode, $source, $context) {
+    if ($source) {
+      // Preload all translations for this configuration name and language.
+      if (!isset($this->translations[$langcode])) {
+        $this->translations[$langcode] = array();
+        foreach ($this->localeStorage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => $this->name)) as $string){
+          $this->translations[$langcode][$string->context][$string->source] = $string;
+        }
+      }
+      if (!isset($this->translations[$langcode][$context][$source])) {
+        if ($translation = $this->localeStorage->findTranslation(array('source' => $source, 'context' => $context, 'language' => $langcode))) {
+          // The translation was there but the location was missing.
+          // Convert to SourceString because it may not have translation.
+          $string = $this->localeStorage->createString((array) $translation)
+            ->addLocation('configuration', $this->name)
+            ->save();
+        }
+        else {
+          // Add missing source string with the right location.
+          $translation = $this->localeStorage
+            ->createString(array('source' => $source, 'context' => $context))
+            ->addLocation('configuration', $this->name)
+            ->save();
+        }
+        $this->translations[$langcode][$context][$source] = $translation;
+      }
+      $translation = $this->translations[$langcode][$context][$source];
+      return $translation->isTranslation() ? $translation->getString() : FALSE;
+    }
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/StringBase.php b/core/modules/locale/lib/Drupal/locale/StringBase.php
index e2d9bcc..2d86cb8 100644
--- a/core/modules/locale/lib/Drupal/locale/StringBase.php
+++ b/core/modules/locale/lib/Drupal/locale/StringBase.php
@@ -52,7 +52,7 @@
   /**
    * The locale storage this string comes from or is to be saved to.
    *
-   * @var Drupal\locale\StringStorageInterface
+   * @var \Drupal\locale\StringStorageInterface
    */
   protected $storage;
 
@@ -189,7 +189,7 @@ public function save() {
     }
     else {
       throw new StringStorageException(format_string('The string cannot be saved because its not bound to a storage: @string', array(
-        '@string' => $string->getString()
+        '@string' => $this->getString()
       )));
     }
     return $this;
diff --git a/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php b/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php
index d02774f..0fb26ad 100644
--- a/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php
+++ b/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php
@@ -79,7 +79,7 @@ public function getLocations(array $conditions = array());
    *   (optional) Array with conditions that will be used to filter the strings
    *   returned and may include all of the conditions defined by getStrings().
    *
-   * @return Drupal\locale\SourceString|null
+   * @return \Drupal\locale\SourceString|null
    *   Minimal TranslationString object if found, NULL otherwise.
    */
   public function findString(array $conditions);
@@ -95,7 +95,7 @@ public function findString(array $conditions);
    *   (optional) Array with conditions that will be used to filter the strings
    *   returned and may include all of the conditions defined by getStrings().
    *
-   * @return Drupal\locale\TranslationString|null
+   * @return \Drupal\locale\TranslationString|null
    *   Minimal TranslationString object if found, NULL otherwise.
    */
   public function findTranslation(array $conditions);
@@ -103,13 +103,13 @@ public function findTranslation(array $conditions);
   /**
    * Save string object to storage.
    *
-   * @param Drupal\locale\StringInterface $string
+   * @param \Drupal\locale\StringInterface $string
    *   The string object.
    *
-   * @return Drupal\locale\StringStorageInterface
+   * @return \Drupal\locale\StringStorageInterface
    *   The called object.
    *
-   * @throws Drupal\locale\StringStorageException
+   * @throws \Drupal\locale\StringStorageException
    *   In case of failures, an exception is thrown.
    */
   public function save($string);
@@ -117,13 +117,13 @@ public function save($string);
   /**
    * Delete string from storage.
    *
-   * @param Drupal\locale\StringInterface $string
+   * @param \Drupal\locale\StringInterface $string
    *   The string object.
    *
-   * @return Drupal\locale\StringStorageInterface
+   * @return \Drupal\locale\StringStorageInterface
    *   The called object.
    *
-   * @throws Drupal\locale\StringStorageException
+   * @throws \Drupal\locale\StringStorageException
    *   In case of failures, an exception is thrown.
    */
   public function delete($string);
@@ -166,7 +166,7 @@ public function countTranslations();
    * @param array $values
    *   (optional) Array with initial values. Defaults to empty array.
    *
-   * @return Drupal\locale\SourceString
+   * @return \Drupal\locale\SourceString
    *   New source string object.
    */
   public function createString($values = array());
@@ -177,7 +177,7 @@ public function createString($values = array());
    * @param array $values
    *   (optional) Array with initial values. Defaults to empty array.
    *
-   * @return Drupal\locale\TranslationString
+   * @return \Drupal\locale\TranslationString
    *   New string translation object.
    */
   public function createTranslation($values = array());
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
new file mode 100644
index 0000000..77f138d
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\locale\Tests\LocaleConfigTranslationTest.
+ */
+
+namespace Drupal\locale\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\simpletest\WebTestBase;
+use Drupal\locale\LocaleTypedConfig;
+
+/**
+ * Tests Metadata for configuration objects.
+ */
+class LocaleConfigTranslationTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('locale');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Configuration translation',
+      'description' => 'Tests translation of configuration strings.',
+      'group' => 'Locale',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    // Add a default locale storage for all these tests.
+    $this->storage = locale_storage();
+  }
+
+  /**
+   * Tests basic configuration translation.
+   */
+  function testConfigTranslation() {
+    // Add custom language
+    $langcode = 'xx';
+    $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface', 'administer modules'));
+    $this->drupalLogin($admin_user);
+    $name = $this->randomName(16);
+    $edit = array(
+      'predefined_langcode' => 'custom',
+      'langcode' => $langcode,
+      'name' => $name,
+      'direction' => '0',
+    );
+    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+    $language = new Language(array('langcode' => $langcode));
+    // Set path prefix.
+    $edit = array( "prefix[$langcode]" => $langcode );
+    $this->drupalPost('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
+
+    // Check site name string exists and create translation for it.
+    $string = $this->storage->findString(array('source' => 'Drupal', 'context' => '', 'type' => 'configuration'));
+    $this->assertTrue($string, 'Configuration strings have been created upon installation.');
+
+    // Translate using the UI so configuration is refreshed.
+    $site_name = $this->randomName(20);
+    $search = array(
+      'string' => $string->source,
+      'langcode' => $langcode,
+      'translation' => 'all',
+    );
+    $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+    $textareas = $this->xpath('//textarea');
+    $textarea = current($textareas);
+    $lid = (string) $textarea[0]['name'];
+    $edit = array(
+      $lid => $site_name,
+    );
+    $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
+
+    $data = config('system.site')->get();
+    $wrapper = new LocaleTypedConfig('system.site', $data, $this->storage);
+
+    // Get strict translation and check we've got only the site name.
+    $translation = $wrapper->getTranslation($langcode, TRUE);
+    $properties = $translation->get()->getProperties();
+    $this->assertEqual(count($properties), 1, 'Got the right number of properties with strict translation');
+    $this->assertEqual($properties['name']->getValue(), $site_name, 'Got the right translation for site name with strict translation');
+    // Get non strict translation and check we've got all properties.
+    $translation = $wrapper->getTranslation($langcode, FALSE);
+    $properties = $translation->get()->getProperties();
+    $this->assertTrue(count($properties) == 4 && count($wrapper->get('page')) == 3, 'Got the right number of properties with non strict translation');
+    $this->assertEqual($properties['name']->getValue(), $site_name, 'Got the right translation for site name with non strict translation');
+
+    // Check the translated site name is displayed.
+    $this->drupalGet($langcode);
+    $this->assertText($site_name, 'The translated site name is displayed after translations refreshed.');
+
+    // Assert strings from image module config are not available.
+    $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration'));
+    $this->assertFalse($string, 'Configuration strings have been created upon installation.');
+
+    // Enable the image module
+    $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => "1"), t('Save configuration'));
+    $this->resetAll();
+
+    $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration'));
+    $this->assertTrue($string, 'Configuration strings have been created upon installation.');
+    $locations = $string->getLocations();
+    $this->assertTrue(isset($locations['configuration']) && isset($locations['configuration']['image.style.medium']), 'Configuration string has been created with the right location');
+    // Check the string is unique and has no translation yet.
+    $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium'));
+    $translation = reset($translations);
+    $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && empty($translation->translation), 'Got only one string for image configuration and has no translation.');
+
+    // Translate using the UI so configuration is refreshed.
+    $image_style_label = $this->randomName(20);
+    $search = array(
+      'string' => $string->source,
+      'langcode' => $langcode,
+      'translation' => 'all',
+    );
+    $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+    $textarea = current($this->xpath('//textarea'));
+    $lid = (string) $textarea[0]['name'];
+    $edit = array(
+      $lid => $image_style_label,
+    );
+    $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
+    // Check the right single translation has been created.
+    $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium'));
+    $translation = reset($translations);
+    $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && $translation->translation == $image_style_label, 'Got only one translation for image configuration.');
+
+    // This really should be testing entity_load_multiple.
+    $data = config('image.style.medium')->get();
+    $wrapper = new LocaleTypedConfig('image.style.medium', $data, $this->storage);
+    $translation = $wrapper->getTranslation($langcode, TRUE);
+    $property = $translation->get('label');
+    $this->assertEqual($property->getValue(), $image_style_label, 'Got the right translation for image style name with strict translation');
+
+    // Quick test to ensure translation file exists.
+    $this->assertEqual(config('locale.config.xx.image.style.medium')->get('label'), $image_style_label);
+
+    // Disable and uninstall the module.
+    $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => FALSE), t('Save configuration'));
+    $this->drupalPost('admin/modules/uninstall', array('uninstall[image]' => "image"), t('Uninstall'));
+    $this->drupalPost(NULL, array(), t('Uninstall'));
+
+    // Ensure that the translated configuration has been removed.
+    $this->assertFalse(config('locale.config.xx.image.style.medium')->get('label'), 'Translated configuration for image module removed.');
+  }
+
+}
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 0be2aec..637f92f 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -6,10 +6,13 @@
  */
 
 use Drupal\Component\Gettext\PoStreamWriter;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\locale\LocaleTypedConfig;
 use Drupal\locale\Gettext;
 use Drupal\locale\PoDatabaseReader;
 use Drupal\Core\Language\Language;
-
+use Drupal\Core\Config\InstallStorage;
+use Drupal\Core\Config\StorageException;
 
 /**
  * Form constructor for the translation import screen.
@@ -123,6 +126,7 @@ function locale_translate_import_form_submit($form, &$form_state) {
       'langcode' => $form_state['values']['langcode'],
       'overwrite_options' => $form_state['values']['overwrite_options'],
       'customized' => $form_state['values']['customized'] ? LOCALE_CUSTOMIZED : LOCALE_NOT_CUSTOMIZED,
+      'refresh_configuration' => TRUE,
     );
     $batch = locale_translate_batch_build(array($file->uri => $file), $options);
     batch_set($batch);
@@ -290,6 +294,10 @@ function locale_translate_add_language_set_batch($options) {
   if ($batch = locale_translate_batch_import_files($options)) {
     batch_set($batch);
   }
+  // Create or update all configuration translations for this language.
+  if ($batch = locale_config_batch_update_components($options)) {
+    batch_set($batch);
+  }
 }
 
 /**
@@ -389,8 +397,12 @@ function locale_translate_get_interface_translation_files($langcode = NULL) {
  *     are customized translations or come from a community source. Use
  *     LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED. Optional, defaults to
  *     LOCALE_NOT_CUSTOMIZED.
+ *   - 'refresh_configuration': Whether or not to refresh Configuration strings
+ *     after the import. Optional, defaults to FALSE.
  *   - 'finish_feedback': Whether or not to give feedback to the user when the
  *     batch is finished. Optional, defaults to TRUE.
+ *   - 'components': Array of arrays of components to refresh the configuration
+ *     indexed by type ('module' or 'theme'). Optional, defaults to none.
  *
  * @return
  *   A batch structure or FALSE if $files was empty.
@@ -400,6 +412,7 @@ function locale_translate_batch_build($files, $options) {
     'overwrite_options' => array(),
     'customized' => LOCALE_NOT_CUSTOMIZED,
     'finish_feedback' => TRUE,
+    'refresh_configuration' => FALSE,
   );
   $t = get_t();
   if (count($files)) {
@@ -408,6 +421,8 @@ function locale_translate_batch_build($files, $options) {
       // We call locale_translate_batch_import for every batch operation.
       $operations[] = array('locale_translate_batch_import', array($file->uri, $options));
     }
+    // Add a final step to refresh JavaScript and configuration strings.
+    $operations[] = array('locale_translate_batch_refresh', array($options));
     $batch = array(
       'operations'    => $operations,
       'title'         => $t('Importing interface translations'),
@@ -517,12 +532,65 @@ function locale_translate_batch_import($filepath, $options, &$context) {
 }
 
 /**
+ * Refresh translations after importing strings.
+ *
+ * @param array $options
+ *   An array with options that can have the following elements:
+ *   - 'refresh_configuration': Whether or not to refresh Configuration strings
+ *     after the import. Optional, defaults to FALSE.
+ *
+ * @param array $context
+ *   Contains a list of strings updated and information about the progress.
+ */
+function locale_translate_batch_refresh($options, &$context) {
+  if (!isset($context['sandbox']['refresh'])) {
+    $strings = $langcodes = array();
+    if (isset($context['results']['stats'])) {
+      // Get list of unique string identifiers and language codes updated.
+      $langcodes = array_unique(array_values($context['results']['languages']));
+      foreach ($context['results']['stats'] as $filepath => $report) {
+        $strings = array_merge($strings, $report['strings']);
+      }
+    }
+    if ($strings) {
+      $context['message'] = t('Updating translations for JavaScript and Configuration strings.');
+      $strings = array_unique($strings);
+      // Clear cache and force refresh of JavaScript translations.
+      _locale_refresh_translations($langcodes, $strings);
+      // Check whether we need to refresh configuration objects.
+      if ($options['refresh_configuration'] && $names = _locale_config_string_names($strings)) {
+        $context['sandbox']['refresh']['names'] = $names;
+        $context['sandbox']['refresh']['languages'] = $langcodes;
+        $context['sandbox']['refresh']['count'] = count($names);
+        $context['results']['stats']['configuration'] = 0;
+      }
+    }
+    if (isset($context['sandbox']['refresh'])) {
+      // We will update configuration on next steps.
+      $context['finished'] = 1 / $context['sandbox']['refresh']['count'];
+    }
+    else {
+      $context['finished'] = 1;
+    }
+  }
+  elseif ($name = array_shift($context['sandbox']['refresh']['names'])) {
+    // Refresh all languages for one object at a time.
+    $count = locale_config_update_multiple(array($name), $context['sandbox']['refresh']['languages']);
+    $context['results']['stats']['configuration'] += $count;
+    // Not perfect but will give some idea of progress.
+    $context['finished'] = 1 - count($context['sandbox']['refresh']['names']) / $context['sandbox']['refresh']['count'];
+  }
+  else {
+    $context['finished'] = 1;
+  }
+}
+
+/**
  * Finished callback of system page locale import batch.
  */
 function locale_translate_batch_finished($success, $results) {
   if ($success) {
-    $additions = $updates = $deletes = $skips = 0;
-    $strings = $langcodes = array();
+    $additions = $updates = $deletes = $skips = $configuration = 0;
     drupal_set_message(format_plural(count($results['files']), 'One translation file imported.', '@count translation files imported.'));
     $skipped_files = array();
     // If there are no results and/or no stats (eg. coping with an empty .po
@@ -536,11 +604,7 @@ function locale_translate_batch_finished($success, $results) {
         if ($report['skips'] > 0) {
           $skipped_files[] = $filepath;
         }
-        $strings = array_merge($strings, $report['strings']);
       }
-      // Get list of unique string identifiers and language codes updated.
-      $strings = array_unique($strings);
-      $langcodes = array_unique(array_values($results['languages']));
     }
     drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)));
     watchdog('locale', 'The translation was succesfully imported. %number new strings added, %update updated and %delete removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes));
@@ -554,10 +618,9 @@ function locale_translate_batch_finished($success, $results) {
       drupal_set_message($skip_message, 'error');
       watchdog('locale', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING);
     }
-
-    if ($strings) {
-      // Clear cache and force refresh of JavaScript translations.
-      _locale_refresh_translations($langcodes, $strings);
+    // Merge feedback about configuration updates too.
+    if (isset($results['stats']['configuration'])) {
+      locale_config_batch_finished($success, $results);
     }
   }
 }
@@ -627,3 +690,251 @@ function locale_translate_delete_translation_files($langcode) {
   }
   return $return;
 }
+
+/**
+ * Build a locale batch to refresh configuration.
+ *
+ * @param array $options
+ *   An array with options that can have the following elements:
+ *   - 'langcode': The language code. Optional, defaults to NULL, which means
+ *     that all translatable languages will be refreshed.
+ *   - 'finish_feedback': Whether or not to give feedback to the user when the
+ *     batch is finished. Optional, defaults to TRUE.
+ * @param array $components
+ *   (optional) Array of component lists indexed by type. If not present or it
+ *   is an empty array, it will update all components.
+ *
+ * @return array
+ *   The batch definition.
+ */
+function locale_config_batch_update_components($options, $components = array()) {
+  $langcodes = isset($options['langcode']) ? array($options['langcode']) : array_keys(locale_translatable_language_list());
+  if ($langcodes && $names = _locale_config_component_names($components)) {
+    return locale_config_batch_build($names, $langcodes, $options);
+  }
+}
+
+/**
+ * Creates a locale batch to refresh specific configuration.
+ *
+ * @param array $names
+ *   List of configuration object names to update.
+ * @param array $langcodes
+ *   List of language codes to refresh.
+ * @param array $options
+ *   An array with options that can have the following elements:
+ *   - 'finish_feedback': Whether or not to give feedback to the user when the
+ *     batch is finished. Optional, defaults to TRUE.
+ *
+ * @return array
+ *   The batch definition.
+ */
+function locale_config_batch_build($names, $langcodes, $options = array()) {
+  $options += array('finish_feedback' => TRUE);
+  $t = get_t();
+  foreach ($names as $name) {
+    $operations[] = array('locale_config_batch_refresh_name', array($name, $langcodes));
+  }
+  $batch = array(
+      'operations'    => $operations,
+      'title'         => $t('Updating configuration translations'),
+      'init_message'  => $t('Starting update'),
+      'error_message' => $t('Error updating configuration translations'),
+      'file'          => drupal_get_path('module', 'locale') . '/locale.bulk.inc',
+  );
+  if (!empty($options['finish_feedback'])) {
+    $batch['completed'] = 'locale_config_batch_finished';
+  }
+  return $batch;
+}
+
+/**
+ * Perform configuration translation refresh as a batch step.
+ *
+ * @param array $name
+ *   Name of configuration object to update.
+ * @param array $langcodes
+ *   (optional) Array of language codes to update. Defaults to all languages.
+ * @param $context
+ *   Contains a list of files imported.
+ */
+function locale_config_batch_refresh_name($name, $langcodes, &$context) {
+  if (!isset($context['result']['stats']['configuration'])) {
+    $context['result']['stats']['configuration'] = 0;
+  }
+  $context['result']['stats']['configuration'] += locale_config_update_multiple(array($name), $langcodes);
+  $context['result']['names'][] = $name;
+  $context['result']['langcodes'] = $langcodes;
+  $context['finished'] = 1;
+}
+
+/**
+ * Finished callback of system page locale import batch.
+ *
+ * @param bool $success
+ *   Information about the success of the batch import.
+ * @param array $results
+ *   Information about the results of the batch import.
+ */
+function locale_config_batch_finished($success, $results) {
+  if ($success) {
+    $configuration = isset($results['stats']['configuration']) ? $results['stats']['configuration'] : 0;
+    if ($configuration) {
+      drupal_set_message(t('The configuration was successfully updated. There are %number configuration objects updated.', array('%number' => $configuration)));
+      watchdog('locale', 'The configuration was successfully updated. %number configuration objects updated.', array('%number' => $configuration));
+    }
+    else {
+      drupal_set_message(t('No configuration objects have been updated.'));
+      watchdog('locale', 'No configuration objects have been updated.', array(), WATCHDOG_WARNING);
+    }
+  }
+}
+
+/**
+ * Delete configuration for language.
+ *
+ * @param $langcode
+ *   Language code to delete.
+ */
+function locale_config_delete_language($langcode) {
+  $locale_name = 'locale.config.' . $langcode;
+  $storage = drupal_container()->get('config.storage');
+  foreach ($storage->listAll($locale_name) as $name) {
+    $storage->delete($name);
+  }
+}
+
+/**
+ * Update all configuration for names / languages.
+ *
+ * @param array $names
+ *   Array of names of configuration objects to update.
+ * @param array $langcodes
+ *   (optional) Array of language codes to update. Defaults to all languages.
+ * @return int
+ *   Number of configuration objects retranslated.
+ */
+function locale_config_update_multiple($names, $langcodes = array()) {
+  $default_storage = new InstallStorage();
+  $config_storage = drupal_container()->get('config.storage');
+  $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
+  $count = 0;
+  foreach ($names as $name) {
+    $wrapper = NULL;
+    try {
+      if ($data = $default_storage->read($name)) {
+        $updated = $config_storage->read($name);
+        if ($data = _locale_config_data_compare($data, $updated)) {
+          $wrapper = new LocaleTypedConfig($name, $data, locale_storage());
+        }
+      }
+      foreach ($langcodes as $langcode) {
+        $locale_name = 'locale.config.' . $langcode . '.' . $name;
+        $translation = $wrapper ? $wrapper->getTranslation($langcode, TRUE)->getData() : NULL;
+        if ($translation) {
+          $config_storage->write($locale_name, $translation);
+          $count++;
+        }
+        else {
+          $config_storage->delete($locale_name);
+        }
+      }
+    }
+    catch (StorageException $e) {
+      // The configuration name does not exist, just continue.
+    }
+  }
+  return $count;
+}
+
+/**
+ * Compare default configuration with updated data.
+ *
+ * @param array $default
+ *   Default configuration data.
+ * @param array $updated
+ *   Current configuration data.
+ *
+ * @return array
+ *   The elements of default configuration that haven't changed.
+ */
+function _locale_config_data_compare($default, $updated) {
+  // Speed up comparison, specially for install operations.
+  if ($default === $updated) {
+    return $default;
+  }
+  $result = array();
+  foreach ($default as $key => $value) {
+    if (isset($updated[$key])) {
+      if (is_array($value)) {
+        $result[$key] = _locale_config_data_compare($value, $updated[$key]);
+      }
+      elseif ($value === $updated[$key]) {
+        $result[$key] = $value;
+      }
+    }
+  }
+  return $result;
+}
+
+/**
+ * Delete configuration for uninstalled components.
+ *
+ * @param array $components
+ *   Array with string identifiers.
+ */
+function locale_config_delete_components($components) {
+  $names = _locale_config_component_names($components);
+  $langcodes = array_keys(locale_translatable_language_list());
+  if ($names && $langcodes) {
+    $storage = drupal_container()->get('config.storage');
+    foreach ($names as $name) {
+      foreach ($langcodes as $langcode) {
+        $storage->delete('locale.config.' . $langcode . '.' . $name);
+      }
+    }
+  }
+}
+
+/**
+ * Get configuration names associated with components.
+ *
+ * @param array $components
+ *   Array with string identifiers.
+ *
+ * @return array
+ *   Array of configuration object names.
+ */
+function _locale_config_component_names($components) {
+  $storage = new InstallStorage();
+  if ($components) {
+    $names = array();
+    foreach ($components as $type => $list) {
+      // InstallStorage::getComponentNames returns a list of folders keyed by
+      // config name.
+      $names = array_merge($names, array_keys($storage->getComponentNames($type, $list)));
+    }
+    return $names;
+  }
+  else {
+    return $storage->listAll();
+  }
+}
+
+/**
+ * Get configuration names associated with strings.
+ *
+ * @param array $lids
+ *   Array with string identifiers.
+ *
+ * @return array
+ *   Array of configuration object names.
+ */
+function _locale_config_string_names($lids) {
+  $names = array();
+  $locations = locale_storage()->getLocations(array('sid' => $lids, 'type' => 'configuration'));
+  foreach ($locations as $location) {
+    $names[$location->name] = $location->name;
+  }
+  return $names;
+}
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 85ed5d1..a0d695a 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -265,6 +265,7 @@ function locale_language_delete($language) {
   // Remove interface translation files.
   module_load_include('inc', 'locale', 'locale.bulk');
   locale_translate_delete_translation_files($language->langcode);
+  locale_config_delete_language($language->langcode);
 
   _locale_invalidate_js($language->langcode);
 
@@ -415,12 +416,21 @@ function locale_get_plural($count, $langcode = NULL) {
   return $plural_indexes[$langcode][$count];
 }
 
-
 /**
  * Implements hook_modules_installed().
  */
 function locale_modules_installed($modules) {
-  locale_system_update($modules);
+  $components['module'] = $modules;
+  locale_system_update($components);
+}
+
+/**
+ * Implements hook_modules_uninstalled().
+ */
+function locale_modules_uninstalled($modules) {
+  include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
+  $components['module'] = $modules;
+  locale_config_delete_components($components);
 }
 
 /**
@@ -430,7 +440,8 @@ function locale_modules_installed($modules) {
  *   initial installation. The theme system is missing an installation hook.
  */
 function locale_themes_enabled($themes) {
-  locale_system_update($themes);
+  $components['theme'] = $themes;
+  locale_system_update($components);
 }
 
 /**
@@ -440,8 +451,8 @@ function locale_themes_enabled($themes) {
  * right away, or start a batch if more files need to be imported.
  *
  * @param $components
- *   An array of component (theme and/or module) names to import
- *   translations for.
+ *   An array of arrays of component (theme and/or module) names to import
+ *   translations for, indexed by type.
  *
  * @todo
  *   This currently imports all .po files available, independent of
@@ -458,6 +469,9 @@ function locale_system_update($components) {
     if ($batch = locale_translate_batch_import_files(array(), TRUE)) {
       batch_set($batch);
     }
+    if ($batch = locale_config_batch_update_components(array(), $components)) {
+      batch_set($batch);
+    }
   }
 }
 
@@ -794,6 +808,27 @@ function _locale_refresh_translations($langcodes, $lids) {
 }
 
 /**
+ * Refresh configuration after string translations have been updated.
+ *
+ * The information that will be refreshed includes:
+ * - JavaScript translations.
+ * - Locale cache.
+ *
+ * @param array $langcodes
+ *   Language codes for updated translations.
+ * @param array $lids
+ *   List of string identifiers that have been updated / created.
+ */
+function _locale_refresh_configuration($langcodes, $lids) {
+  if ($lids && $langcodes) {
+    include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
+    if ($names = _locale_config_string_names($lids)) {
+      locale_config_update_multiple($names, $langcodes);
+    }
+  }
+}
+
+/**
  * Parses a JavaScript file, extracts strings wrapped in Drupal.t() and
  * Drupal.formatPlural() and inserts them into the database.
  *
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index 8ea8780..8842ba5 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -439,7 +439,8 @@ function locale_translate_edit_form_submit($form, &$form_state) {
   }
 
   if ($updated) {
-    // Clear cache and force refresh of JavaScript translations.
+    // Clear cache and refresh configuration and JavaScript translations.
+    _locale_refresh_configuration(array($langcode), $updated);
     _locale_refresh_translations(array($langcode), $updated);
   }
 
diff --git a/core/modules/system/meta/system.maintenance.yml b/core/modules/system/meta/system.maintenance.yml
new file mode 100644
index 0000000..fb5e88b
--- /dev/null
+++ b/core/modules/system/meta/system.maintenance.yml
@@ -0,0 +1,8 @@
+enabled:
+  .definition:
+    label: 'Put site into maintenance mode'
+    type: boolean
+message:
+  .definition:
+    label: 'Message to display when in maintenance mode'
+    type: text
diff --git a/core/modules/system/meta/system.site.yml b/core/modules/system/meta/system.site.yml
new file mode 100644
index 0000000..918f48b
--- /dev/null
+++ b/core/modules/system/meta/system.site.yml
@@ -0,0 +1,23 @@
+name:
+  .definition:
+    label: 'Site name'
+    type: text
+mail:
+  .definition:
+    label: 'Site mail'
+slogan:
+  .definition:
+    label: 'Site slogan'
+    type: text
+page:
+  .definition:
+    label: 'Default pages'
+  403:
+    .definition:
+      label: 'Default 403 (access denied) page'
+  404:
+    .definition:
+      label: 'Default 404 (not found) page'
+  front:
+    .definition:
+      label: 'Default front page'
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 6401361..e815191 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2030,6 +2030,12 @@ function system_data_type_info() {
       'class' => '\Drupal\Core\TypedData\Type\String',
       'primitive type' => Primitive::STRING,
     ),
+    'text' => array(
+      'label' => t('Text'),
+      'description' => t('Human readable string.'),
+      'class' => '\Drupal\Core\TypedData\Type\String',
+      'primitive type' => Primitive::STRING,
+    ),
     'integer' => array(
       'label' => t('Integer'),
       'class' => '\Drupal\Core\TypedData\Type\Integer',
@@ -2111,6 +2117,12 @@ function system_data_type_info() {
       'class' => '\Drupal\Core\Entity\Field\Type\EntityReferenceItem',
       'list class' => '\Drupal\Core\Entity\Field\Type\Field',
     ),
+    'config_element' => array(
+      'label' => t('Configuration element'),
+      'description' => t('Configuration data'),
+      'class' => 'Drupal\Core\Config\Metadata\ConfigElement',
+      'list class' => 'Drupal\Core\Config\Metadata\ListElement',
+    ),
   );
 }
 
diff --git a/core/modules/user/meta/user.mail.yml b/core/modules/user/meta/user.mail.yml
new file mode 100644
index 0000000..cd710a9
--- /dev/null
+++ b/core/modules/user/meta/user.mail.yml
@@ -0,0 +1,13 @@
+.definition:
+  label: 'User mails'
+.list:
+  .definition:
+    label: 'Mail text'
+  subject:
+    .definition:
+      label: 'Subject'
+      type: text
+  body:
+    .definition:
+      label: 'Body'
+      type: text
\ No newline at end of file
