diff --git a/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php b/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php
index 4e041ce..9988f5e 100644
--- a/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php
+++ b/core/modules/config_translation/src/Access/ConfigTranslationFormAccess.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\config_translation\Access;
 
+use Drupal\config_translation\ConfigMapperInterface;
+use Drupal\config_translation\Exception\ConfigMapperLanguageException;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -12,27 +14,69 @@
 class ConfigTranslationFormAccess extends ConfigTranslationOverviewAccess {
 
   /**
-   * {@inheritdoc}
+   * Checks access to the overview based on permissions and translatability.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route_match to check against.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account to check access for.
+   * @param string $target_langcode
+   *   The language code of the target language.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
    */
-  public function access(RouteMatchInterface $route_match, AccountInterface $account, $langcode = NULL) {
-    // For the translation forms we have a target language, so we need some
-    // checks in addition to the checks performed for the translation overview.
-    $base_access = parent::access($route_match, $account);
-    if ($base_access->isAllowed()) {
-      $target_language = $this->languageManager->getLanguage($langcode);
-
-      // Make sure that the target language is not locked, and that the target
-      // language is not the original submission language. Although technically
-      // configuration can be overlaid with translations in the same language,
-      // that is logically not a good idea.
-      $access =
-        !empty($target_language) &&
-        !$target_language->isLocked() &&
-        (empty($this->sourceLanguage) || ($target_language->getId() != $this->sourceLanguage->getId()));
-
-      return $base_access->andIf(AccessResult::allowedIf($access));
+  public function access(RouteMatchInterface $route_match, AccountInterface $account, $target_langcode = NULL) {
+    $mapper = $this->getMapperFromRouteMatch($route_match);
+
+    try {
+      $source_langcode = $mapper->getLangcode();
+      $source_language = $this->languageManager->getLanguage($source_langcode);
+
+      $target_language = $this->languageManager->getLanguage($target_langcode);
+
+      return $this->doCheckAccess($account, $mapper, $source_language, $target_language);
     }
-    return $base_access;
+    catch (ConfigMapperLanguageException $exception) {
+      return AccessResult::forbidden();
+    }
+  }
+
+  /**
+   * Checks access given an account, configuration mapper, and source language.
+   *
+   * In addition to the checks performed by
+   * ConfigTranslationOverviewAccess::doCheckAccess() this makes sure the target
+   * language is not locked and the target language is not the source language.
+   *
+   * Although technically configuration can be overlaid with translations in the
+   * same language, that is logically not a good idea.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account to check access for.
+   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
+   *   The configuration mapper to check access for.
+   * @param \Drupal\Core\Language\LanguageInterface|null $source_language
+   *   The source language to check for, if any.
+   * @param \Drupal\Core\Language\LanguageInterface|null $target_language
+   *   The target language to check for, if any.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The result of the access check.
+   *
+   * @see \Drupal\config_translation\Access\ConfigTranslationOverviewAccess::doCheckAccess()
+   */
+  protected function doCheckAccess(AccountInterface $account, ConfigMapperInterface $mapper, $source_language = NULL, $target_language = NULL) {
+    $base_access_result = parent::doCheckAccess($account, $mapper, $source_language);
+
+    $access =
+      $target_language &&
+      !$target_language->isLocked() &&
+      (!$source_language || ($target_language->getId() !== $source_language->getId()));
+
+    return $base_access_result->andIf(AccessResult::allowedIf($access));
+
   }
 
+
 }
diff --git a/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php b/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php
index 91ccf49..ceea03a 100644
--- a/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php
+++ b/core/modules/config_translation/src/Access/ConfigTranslationOverviewAccess.php
@@ -2,6 +2,9 @@
 
 namespace Drupal\config_translation\Access;
 
+use Drupal\config_translation\ConfigMapperInterface;
+use Drupal\config_translation\Exception\ConfigMapperLanguageException;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\config_translation\ConfigMapperManagerInterface;
 use Drupal\Core\Access\AccessResult;
@@ -29,13 +32,6 @@ class ConfigTranslationOverviewAccess implements AccessInterface {
   protected $languageManager;
 
   /**
-   * The source language.
-   *
-   * @var \Drupal\Core\Language\LanguageInterface
-   */
-  protected $sourceLanguage;
-
-  /**
    * Constructs a ConfigTranslationOverviewAccess object.
    *
    * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
@@ -54,28 +50,66 @@ public function __construct(ConfigMapperManagerInterface $config_mapper_manager,
    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
    *   The route_match to check against.
    * @param \Drupal\Core\Session\AccountInterface $account
-   *   The currently logged in account.
+   *   The account to check access for.
    *
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
   public function access(RouteMatchInterface $route_match, AccountInterface $account) {
-    $route = $route_match->getRouteObject();
+    $mapper = $this->getMapperFromRouteMatch($route_match);
+
+    try {
+      $langcode = $mapper->getLangcode();
+    }
+    catch (ConfigMapperLanguageException $exception) {
+      // ConfigTranslationController shows a helpful message if the language
+      // codes do not match, so do not let that prevent granting access.
+      $langcode = 'en';
+    }
+    $source_language = $this->languageManager->getLanguage($langcode);
 
-    /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
-    $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
+    return $this->doCheckAccess($account, $mapper, $source_language);
+  }
+
+  /**
+   * Gets a configuration mapper using a route match.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match to populate the mapper with.
+   *
+   * @return \Drupal\config_translation\ConfigMapperInterface
+   *   The configuration mapper.
+   */
+  protected function getMapperFromRouteMatch(RouteMatchInterface $route_match) {
+    $mapper = $this->configMapperManager->createInstance($route_match->getRouteObject()
+      ->getDefault('plugin_id'));
     $mapper->populateFromRouteMatch($route_match);
-    $this->sourceLanguage = $this->languageManager->getLanguage($mapper->getLangcode());
+    return $mapper;
+  }
 
-    // Allow access to the translation overview if the proper permission is
-    // granted, the configuration has translatable pieces, and the source
-    // language is not locked if it is present.
-    $source_language_access = is_null($this->sourceLanguage) || !$this->sourceLanguage->isLocked();
+  /**
+   * Checks access given an account, configuration mapper, and source language.
+   *
+   * Grants access if the proper permission is granted to the account, the
+   * configuration has translatable pieces, and the source language is not
+   * locked given it is present.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account to check access for.
+   * @param \Drupal\config_translation\ConfigMapperInterface $mapper
+   *   The configuration mapper to check access for.
+   * @param \Drupal\Core\Language\LanguageInterface|null $source_language
+   *   The source language to check for, if any.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The result of the access check.
+   */
+  protected function doCheckAccess(AccountInterface $account, ConfigMapperInterface $mapper, $source_language = NULL) {
     $access =
       $account->hasPermission('translate configuration') &&
       $mapper->hasSchema() &&
       $mapper->hasTranslatable() &&
-      $source_language_access;
+      (!$source_language || !$source_language->isLocked());
 
     return AccessResult::allowedIf($access)->cachePerPermissions();
   }
diff --git a/core/modules/config_translation/src/ConfigMapperInterface.php b/core/modules/config_translation/src/ConfigMapperInterface.php
index be26d4e..57c1ccd 100644
--- a/core/modules/config_translation/src/ConfigMapperInterface.php
+++ b/core/modules/config_translation/src/ConfigMapperInterface.php
@@ -203,6 +203,17 @@ public function getConfigData();
   public function getLangcode();
 
   /**
+   * Returns the language code of a configuration object given its name.
+   *
+   * @param string $config_name
+   *   The name of the configuration object.
+   *
+   * @return string
+   *   The language code of the configuration object.
+   */
+  public function getLangcodeFromConfig($config_name);
+
+  /**
    * Sets the original language code.
    *
    * @param string $langcode
diff --git a/core/modules/config_translation/src/ConfigNamesMapper.php b/core/modules/config_translation/src/ConfigNamesMapper.php
index 5b6028a..bb82fe5 100644
--- a/core/modules/config_translation/src/ConfigNamesMapper.php
+++ b/core/modules/config_translation/src/ConfigNamesMapper.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\config_translation;
 
+use Drupal\config_translation\Exception\ConfigMapperLanguageException;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\Language\LanguageInterface;
@@ -380,17 +381,10 @@ public function getTypeLabel() {
    * {@inheritdoc}
    */
   public function getLangcode() {
-    $config_factory = $this->configFactory;
-    $langcodes = array_map(function($name) use ($config_factory) {
-      // Default to English if no language code was provided in the file.
-      // Although it is a best practice to include a language code, if the
-      // developer did not think about a multilingual use-case, we fall back
-      // on assuming the file is English.
-      return $config_factory->get($name)->get('langcode') ?: 'en';
-    }, $this->getConfigNames());
+    $langcodes = array_map([$this, 'getLangcodeFromConfig'], $this->getConfigNames());
 
     if (count(array_unique($langcodes)) > 1) {
-      throw new \RuntimeException('A config mapper can only contain configuration for a single language.');
+      throw new ConfigMapperLanguageException('A config mapper can only contain configuration for a single language.');
     }
 
     return reset($langcodes);
@@ -399,6 +393,17 @@ public function getLangcode() {
   /**
    * {@inheritdoc}
    */
+  public function getLangcodeFromConfig($config_name) {
+    // Default to English if no language code was provided in the file.
+    // Although it is a best practice to include a language code, if the
+    // developer did not think about a multilingual use case, we fall back
+    // on assuming the file is English.
+    return $this->configFactory->get($config_name)->get('langcode') ?: 'en';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function setLangcode($langcode) {
     $this->langcode = $langcode;
     return $this;
diff --git a/core/modules/config_translation/src/Controller/ConfigTranslationController.php b/core/modules/config_translation/src/Controller/ConfigTranslationController.php
index 376def0..927a1e2 100644
--- a/core/modules/config_translation/src/Controller/ConfigTranslationController.php
+++ b/core/modules/config_translation/src/Controller/ConfigTranslationController.php
@@ -3,11 +3,14 @@
 namespace Drupal\config_translation\Controller;
 
 use Drupal\config_translation\ConfigMapperManagerInterface;
+use Drupal\config_translation\Exception\ConfigMapperLanguageException;
 use Drupal\Core\Access\AccessManagerInterface;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -64,6 +67,13 @@ class ConfigTranslationController extends ControllerBase {
   protected $languageManager;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs a ConfigTranslationController.
    *
    * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
@@ -78,14 +88,17 @@ class ConfigTranslationController extends ControllerBase {
    *   The current user.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager) {
+  public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager, RendererInterface $renderer) {
     $this->configMapperManager = $config_mapper_manager;
     $this->accessManager = $access_manager;
     $this->router = $router;
     $this->pathProcessor = $path_processor;
     $this->account = $account;
     $this->languageManager = $language_manager;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -98,7 +111,8 @@ public static function create(ContainerInterface $container) {
       $container->get('router'),
       $container->get('path_processor_manager'),
       $container->get('current_user'),
-      $container->get('language_manager')
+      $container->get('language_manager'),
+      $container->get('renderer')
     );
   }
 
@@ -127,7 +141,33 @@ public function itemPage(Request $request, RouteMatchInterface $route_match, $pl
     if (count($languages) == 1) {
       drupal_set_message($this->t('In order to translate configuration, the website must have at least two <a href=":url">languages</a>.', array(':url' => $this->url('entity.configurable_language.collection'))), 'warning');
     }
-    $original_langcode = $mapper->getLangcode();
+
+    try {
+      $original_langcode = $mapper->getLangcode();
+      $operations_access = TRUE;
+    }
+    catch (ConfigMapperLanguageException $exception) {
+      $items = [];
+      foreach ($mapper->getConfigNames() as $config_name) {
+        $langcode = $mapper->getLangcodeFromConfig($config_name);
+        $items[] = $this->t('@name: @langcode', [
+          '@name' => $config_name,
+          '@langcode'  => $langcode,
+        ]);
+      }
+      $message = [
+        'message' => ['#markup' => $this->t('The configuration objects have different language codes so they cannot be translated:')],
+        'items' => [
+          '#theme' => 'item_list',
+          '#items' => $items,
+        ],
+      ];
+      drupal_set_message($this->renderer->renderPlain($message), 'warning');
+
+      $original_langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
+      $operations_access = FALSE;
+    }
+
     if (!isset($languages[$original_langcode])) {
       // If the language is not configured on the site, create a dummy language
       // object for this listing only to ensure the user gets useful info.
@@ -205,6 +245,9 @@ public function itemPage(Request $request, RouteMatchInterface $route_match, $pl
       $page['languages'][$langcode]['operations'] = array(
         '#type' => 'operations',
         '#links' => $operations,
+        // Even if the mapper contains multiple language codes, the source
+        // configuration can still be edited.
+        '#access' => ($langcode == $original_langcode) || $operations_access,
       );
     }
     return $page;
diff --git a/core/modules/config_translation/src/Exception/ConfigMapperLanguageException.php b/core/modules/config_translation/src/Exception/ConfigMapperLanguageException.php
new file mode 100644
index 0000000..db4d202
--- /dev/null
+++ b/core/modules/config_translation/src/Exception/ConfigMapperLanguageException.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace Drupal\config_translation\Exception;
+
+/**
+ * Provides an exception for configuration mappers with multiple languages.
+ */
+class ConfigMapperLanguageException extends \RuntimeException {
+}
diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
index 68d58ab..5660663 100644
--- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
+++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php
@@ -4,6 +4,7 @@
 
 use Drupal\config_translation\ConfigMapperManagerInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\Form\BaseFormIdInterface;
@@ -138,7 +139,12 @@ public function buildForm(array $form, FormStateInterface $form_state, RouteMatc
 
     $this->mapper = $mapper;
     $this->language = $language;
-    $this->sourceLanguage = $this->languageManager->getLanguage($this->mapper->getLangcode());
+
+    // ConfigTranslationFormAccess will not grant access if this raises an
+    // exception, so we can call this without a try-catch block here.
+    $langcode = $this->mapper->getLangcode();
+
+    $this->sourceLanguage = $this->languageManager->getLanguage($langcode);
 
     // Get base language configuration to display in the form before setting the
     // language to use for the form. This avoids repetitively settings and
diff --git a/core/modules/node/src/Tests/NodeTypeTranslationTest.php b/core/modules/node/src/Tests/NodeTypeTranslationTest.php
index f86ccc4..1c3a768 100644
--- a/core/modules/node/src/Tests/NodeTypeTranslationTest.php
+++ b/core/modules/node/src/Tests/NodeTypeTranslationTest.php
@@ -23,7 +23,9 @@ class NodeTypeTranslationTest extends WebTestBase {
    * @var array
    */
   public static $modules = array(
+    'block',
     'config_translation',
+    'field_ui',
     'node',
   );
 
@@ -53,6 +55,8 @@ protected function setUp() {
 
     $admin_permissions = array(
       'administer content types',
+      'administer node fields',
+      'administer languages',
       'administer site configuration',
       'administer themes',
       'translate configuration',
@@ -144,6 +148,29 @@ public function testNodeTypeTitleLabelTranslation() {
     $this->assertText('Edited title');
     $this->drupalGet("$langcode/node/add/$type");
     $this->assertText('Translated title');
+
+    // Add an e-mail field.
+    $this->drupalPostForm("admin/structure/types/manage/$type/fields/add-field", array('new_storage_type' => 'email', 'label' => 'Email', 'field_name' => 'email'), 'Save and continue');
+    $this->drupalPostForm(NULL, array(), 'Save field settings');
+    $this->drupalPostForm(NULL, array(), 'Save settings');
+
+    $type = Unicode::strtolower($this->randomMachineName(16));
+    $name = $this->randomString();
+    $this->drupalCreateContentType(array('type' => $type, 'name' => $name));
+
+    // Set tabs.
+    $this->drupalPlaceBlock('local_tasks_block', array('primary' => TRUE));
+
+    // Change default language.
+    $this->drupalPostForm('admin/config/regional/language', array('site_default_language' => 'es'), 'Save configuration');
+
+    // Try re-using the email field.
+    $this->drupalGet("es/admin/structure/types/manage/$type/fields/add-field");
+    $this->drupalPostForm(NULL, array('existing_storage_name' => 'field_email', 'existing_storage_label' => 'Email'), 'Save and continue');
+    $this->assertResponse(200);
+    $this->drupalGet("es/admin/structure/types/manage/$type/fields/node.$type.field_email/translate");
+    $this->assertResponse(200);
+    $this->assertText("The configuration objects have different language codes so they cannot be translated");
   }
 
 }
