diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php
index 4db85da..cb5a26e 100644
--- a/core/lib/Drupal/Core/Language/Language.php
+++ b/core/lib/Drupal/Core/Language/Language.php
@@ -25,6 +25,7 @@ class Language {
   public $default = FALSE;
   public $method_id = NULL;
   public $locked = FALSE;
+  public $fallback_langcode = '';
 
   /**
    * Language constructor builds the default language object.
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index 1872146..4367525 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -258,6 +258,26 @@ function _language_admin_common_controls(&$form, $language = NULL) {
     '#default_value' => @$language->name,
     '#required' => TRUE,
   );
+
+  // Fallback language can be any enabled language.
+  $languages = language_list();
+  $fallback_language_options = array();
+  foreach ($languages as $langcode => $lang) {
+    $fallback_language_options[$langcode] = $lang->name;
+  }
+  // Remove current language from the fallback options.
+  if (isset($language->langcode)) {
+    unset($fallback_language_options[$language->langcode]);
+  }
+  $form['fallback_langcode'] = array(
+    '#type' => 'select',
+    '#title' => t('Fallback language'),
+    '#default_value' => @$language->fallback_langcode,
+    '#options' => $fallback_language_options,
+    '#empty_value' => '',
+    '#description' => t('Select the fallback language for translations.'),
+  );
+
   $form['direction'] = array(
     '#type' => 'radios',
     '#title' => t('Direction'),
@@ -314,6 +334,7 @@ function language_admin_add_form_submit($form, &$form_state) {
       'langcode' => $langcode,
       'name' => $form_state['values']['name'],
       'direction' => $form_state['values']['direction'],
+      'fallback_langcode' => $form_state['values']['fallback_langcode'],
     ));
   }
   else {
@@ -353,6 +374,7 @@ function language_admin_edit_form_submit($form, &$form_state) {
   $language = $languages[$langcode];
   $language->name = $form_state['values']['name'];
   $language->direction = $form_state['values']['direction'];
+  $language->fallback_langcode = $form_state['values']['fallback_langcode'];
   language_save($language);
   $form_state['redirect'] = 'admin/config/regional/language';
 }
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index b16c53b..2634124 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -64,6 +64,13 @@ function language_schema() {
         'default' => '',
         'description' => "Language code, e.g. 'de' or 'en-US'.",
       ),
+      'fallback_langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "Fallback language code.",
+      ),
       'name' => array(
         'type' => 'varchar',
         'length' => 64,
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
index 57b68ff..88c11e5 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\locale\Tests;
 
 use Drupal\simpletest\WebTestBase;
+use Drupal\Core\Language\Language;
 
 /**
  * Functional test for string translation and validation.
@@ -207,6 +208,81 @@ function testStringTranslation() {
     $this->assertNoText(t('No strings available.'), t('The translation has been removed'));
   }
 
+  /**
+   * Adds a language and tests string translation by users with the appropriate permissions.
+   */
+  function testStringTranslationFallback() {
+
+    // Code for the language.
+    $langcode = 'xx';
+    // Code for the second language.
+    $fallback_langcode = 'xx-xx';
+    // The English name for the language. This will be translated.
+    $name = $this->randomName(16);
+    // The English name for the fallback language.
+    $fallback_name = $this->randomName(16);
+    // The English name for the fallback language.
+    $translation = $this->randomName(16);
+
+    // Add custom language.
+    $language = new Language(array(
+      'langcode' => $langcode,
+      'name' => $name,
+      'direction' => 0,
+      'fallback_langcode' => '',
+    ));
+    language_save($language);
+
+    // Add custom language with fallback.
+    $language = new Language(array(
+      'langcode' => $fallback_langcode,
+      'name' => $fallback_name,
+      'direction' => 0,
+      'fallback_langcode' => $langcode,
+    ));
+    language_save($language);
+
+    // Reset language list cache.
+    drupal_static_reset('language_list');
+
+    // Check if the fallback is set
+    $fallback_language = language_load($fallback_langcode);
+    $this->assertEqual($fallback_language->fallback_langcode, $langcode);
+
+    // Add string.
+    t($name, array(), array('langcode' => $langcode));
+
+    // Reset locale cache.
+    locale_reset();
+
+    // Search for the name and translate it for language $langcode.
+    $conditions = array('language' => $langcode, 'translated' => FALSE);
+    $options = array('pager limit' => 30, 'translated' => TRUE, 'untranslated' => TRUE);
+    $options['filters']['source'] = $name;
+    $strings = locale_storage()->getTranslations($conditions, $options);
+    $string = $strings[0];
+
+    $target = $string;
+    $target->language = $langcode;
+    $target->setPlurals(array($translation))
+      ->setCustomized()
+      ->save();
+    _locale_refresh_translations(array($langcode), array($target->getId()));
+
+    locale_reset();
+    drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+
+    // Check translation in language xx.
+    $this->assertNotEqual(t($name, array(), array('langcode' => $langcode)), $name);
+
+    // Check translation in language xx-xx.
+    $this->assertEqual(t($name, array(), array('langcode' => $fallback_langcode)), t($name, array(), array('langcode' => $langcode)));
+
+    // Delete the custom languages.
+    language_delete($langcode);
+    language_delete($fallback_langcode);
+  }
+
   /*
    * Adds a language and checks that the JavaScript translation files are
    * properly created and rebuilt on deletion.
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 59c13ec..9792d6d 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -338,6 +338,13 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
   // LocaleLookup class to handle string lookup and caching.
   if (!isset($locale_t[$langcode][$context]) && isset($language_interface)) {
     $locale_t[$langcode][$context] = new LocaleLookup($langcode, $context, locale_storage());
+    // If there's no translation check for a fallback language.
+    if ($locale_t[$langcode][$context][$string] === TRUE) {
+      $language = language_load($langcode);
+      if ($language && !empty($language->fallback_langcode) && $language->fallback_langcode !== $language->langcode) {
+        return locale($string, $context, $language->fallback_langcode);
+      }
+    }
   }
   return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]);
 }
