diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 65349a3..9ffd50e 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -627,8 +627,14 @@ function theme($hook, $variables = array()) {
   if (isset($info['base hook'])) {
     $suggestions[] = $hook;
   }
-  // Allow suggestions to be altered via hook_theme_suggestions_HOOK_alter().
-  Drupal::moduleHandler()->alter('theme_suggestions_' . $base_theme_hook, $suggestions, $variables);
+
+  // Invoke hook_theme_suggestions_alter() and
+  // hook_theme_suggestions_HOOK_alter().
+  $hooks = array(
+    'theme_suggestions',
+    'theme_suggestions_' . $base_theme_hook,
+  );
+  \Drupal::moduleHandler()->alter($hooks, $suggestions, $variables, $base_theme_hook);
 
   // Check if each suggestion exists in the theme registry, and if so,
   // use it instead of the hook that theme() was called with. For example, a
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php
index 00cd008..8f6e9b6 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSuggestionsAlterTest.php
@@ -52,24 +52,45 @@ function testTemplateSuggestions() {
   }
 
   /**
+   * Tests hook_theme_suggestions_alter().
+   */
+  function testGeneralSuggestionsAlter() {
+    $this->drupalGet('theme-test/general-suggestion-alter');
+    $this->assertText('Original template for testing hook_theme_suggestions_alter().');
+
+    // Enable test_theme and test that themes can alter template suggestions.
+    config('system.theme')
+      ->set('default', 'test_theme')
+      ->save();
+    $this->drupalGet('theme-test/general-suggestion-alter');
+    $this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_alter().');
+
+    // Enable the theme_suggestions_test module to test modules implementing
+    // suggestions alter hooks.
+    \Drupal::moduleHandler()->install(array('theme_suggestions_test'));
+    $this->drupalGet('theme-test/general-suggestion-alter');
+    $this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_alter().');
+  }
+
+  /**
    * Tests that theme suggestion alter hooks work for templates.
    */
   function testTemplateSuggestionsAlter() {
     $this->drupalGet('theme-test/suggestion-alter');
-    $this->assertText('Original template.');
+    $this->assertText('Original template for testing hook_theme_suggestions_HOOK_alter().');
 
     // Enable test_theme and test that themes can alter template suggestions.
     config('system.theme')
       ->set('default', 'test_theme')
       ->save();
     $this->drupalGet('theme-test/suggestion-alter');
-    $this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme.');
+    $this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_HOOK_alter().');
 
     // Enable the theme_suggestions_test module to test modules implementing
     // suggestions alter hooks.
     \Drupal::moduleHandler()->install(array('theme_suggestions_test'));
     $this->drupalGet('theme-test/suggestion-alter');
-    $this->assertText('Template overridden based on new theme suggestion provided by a module.');
+    $this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_HOOK_alter().');
   }
 
   /**
@@ -117,4 +138,34 @@ function testThemeFunctionSuggestionsAlter() {
     $this->assertText('Theme function overridden based on new theme suggestion provided by a module.');
   }
 
+  /**
+   * Tests execution order of theme suggestion alter hooks.
+   *
+   * hook_theme_suggestions_alter() should fire before
+   * hook_theme_suggestions_HOOK_alter() within an extension (module or theme).
+   */
+  function testExecutionOrder() {
+    // Enable our test theme and module.
+    config('system.theme')
+      ->set('default', 'test_theme')
+      ->save();
+    \Drupal::moduleHandler()->install(array('theme_suggestions_test'));
+
+    // Send two requests so that we get all the messages we've set via
+    // drupal_set_message().
+    $this->drupalGet('theme-test/suggestion-alter');
+    // Ensure that the order is first by extension, then for a given extension,
+    // the hook-specific one after the generic one.
+    $expected = array(
+      'theme_suggestions_test_theme_suggestions_alter() executed.',
+      'theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.',
+      'theme_test_theme_suggestions_alter() executed.',
+      'theme_test_theme_suggestions_theme_test_suggestions_alter() executed.',
+      'test_theme_theme_suggestions_alter() executed.',
+      'test_theme_theme_suggestions_theme_test_suggestions_alter() executed.',
+    );
+    $content = preg_replace('/\s+/', ' ', filter_xss($this->content, array()));
+    $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Suggestion alter hooks executed in the expected order.');
+  }
+
 }
diff --git a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
index 9ecaeeb..1b19fee 100644
--- a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
+++ b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
@@ -6,9 +6,20 @@
  */
 
 /**
+ * Implements hook_theme_suggestions_alter().
+ */
+function theme_suggestions_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+  drupal_set_message(__FUNCTION__ . '() executed.');
+  if ($hook == 'theme_test_general_suggestions') {
+    $suggestions[] = $hook . '__module_override';
+  }
+}
+
+/**
  * Implements hook_theme_suggestions_HOOK_alter().
  */
 function theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
+  drupal_set_message(__FUNCTION__ . '() executed.');
   $suggestions[] = 'theme_test_suggestions__' . 'module_override';
 }
 
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php
index 8da6c62..29bfa4e 100644
--- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php
@@ -107,6 +107,13 @@ function suggestionAlter() {
   }
 
   /**
+   * Menu callback for testing hook_theme_suggestions_alter().
+   */
+  function generalSuggestionAlter() {
+    return array('#theme' => 'theme_test_general_suggestions');
+  }
+
+  /**
    * Menu callback for testing suggestion alter hooks with specific suggestions.
    */
   function specificSuggestionAlter() {
diff --git a/core/modules/system/tests/modules/theme_test/templates/theme-test-general-suggestions.html.twig b/core/modules/system/tests/modules/theme_test/templates/theme-test-general-suggestions.html.twig
new file mode 100644
index 0000000..086eb47
--- /dev/null
+++ b/core/modules/system/tests/modules/theme_test/templates/theme-test-general-suggestions.html.twig
@@ -0,0 +1,2 @@
+{# Output for Theme API test #}
+Original template for testing hook_theme_suggestions_alter().
diff --git a/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig b/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig
index dfc848c..42bb3b9 100644
--- a/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig
+++ b/core/modules/system/tests/modules/theme_test/templates/theme-test-suggestions.html.twig
@@ -1,2 +1,2 @@
 {# Output for Theme API test #}
-Original template.
+Original template for testing hook_theme_suggestions_HOOK_alter().
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index b8a528d..bdb1404 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -26,6 +26,10 @@ function theme_test_theme($existing, $type, $theme, $path) {
     'template' => 'theme-test-suggestions',
     'variables' => array(),
   );
+  $items['theme_test_general_suggestions'] = array(
+    'template' => 'theme-test-general-suggestions',
+    'variables' => array(),
+  );
   $items['theme_test_function_suggestions'] = array(
     'variables' => array(),
   );
@@ -152,3 +156,17 @@ function theme_theme_test_function_suggestions($variables) {
 function theme_test_theme_suggestions_theme_test_suggestion_provided(array $variables) {
   return array('theme_test_suggestion_provided__' . 'foo');
 }
+
+/**
+ * Implements hook_theme_suggestions_alter().
+ */
+function theme_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+  drupal_set_message(__FUNCTION__ . '() executed.');
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK_alter().
+ */
+function theme_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
+  drupal_set_message(__FUNCTION__ . '() executed.');
+}
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml
index 9f08d5b..6b78b13 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml
+++ b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml
@@ -65,6 +65,13 @@ suggestion_alter:
   requirements:
     _permission: 'access content'
 
+theme_test.general_suggestion_alter:
+  path: '/theme-test/general-suggestion-alter'
+  defaults:
+    _content: '\Drupal\theme_test\ThemeTestController::generalSuggestionAlter'
+  requirements:
+    _permission: 'access content'
+
 suggestion_provided:
   path: '/theme-test/suggestion-provided'
   defaults:
diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-general-suggestions--module-override.html.twig
similarity index 69%
copy from core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig
copy to core/modules/system/tests/themes/test_theme/templates/theme-test-general-suggestions--module-override.html.twig
index 26ce57b..5969332 100644
--- a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig
+++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-general-suggestions--module-override.html.twig
@@ -1,2 +1,2 @@
 {# Output for Theme API test #}
-Template overridden based on new theme suggestion provided by a module.
+Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_alter().
diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-general-suggestions--theme-override.html.twig
similarity index 63%
copy from core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig
copy to core/modules/system/tests/themes/test_theme/templates/theme-test-general-suggestions--theme-override.html.twig
index dee829f..29322da 100644
--- a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig
+++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-general-suggestions--theme-override.html.twig
@@ -1,2 +1,2 @@
 {# Output for Theme API test #}
-Template overridden based on new theme suggestion provided by the test_theme theme.
+Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_alter().
diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig
index 26ce57b..2a005cb 100644
--- a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig
+++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--module-override.html.twig
@@ -1,2 +1,2 @@
 {# Output for Theme API test #}
-Template overridden based on new theme suggestion provided by a module.
+Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_HOOK_alter().
diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig
index dee829f..4516238 100644
--- a/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig
+++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-suggestions--theme-override.html.twig
@@ -1,2 +1,2 @@
 {# Output for Theme API test #}
-Template overridden based on new theme suggestion provided by the test_theme theme.
+Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_HOOK_alter().
diff --git a/core/modules/system/tests/themes/test_theme/test_theme.theme b/core/modules/system/tests/themes/test_theme/test_theme.theme
index 9b10b2b..13cb2b9 100644
--- a/core/modules/system/tests/themes/test_theme/test_theme.theme
+++ b/core/modules/system/tests/themes/test_theme/test_theme.theme
@@ -31,9 +31,24 @@ function test_theme_theme_test_alter_alter(&$data) {
 }
 
 /**
+ * Implements hook_theme_suggestions_alter().
+ */
+function test_theme_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+  drupal_set_message(__FUNCTION__ . '() executed.');
+  // Theme alter hooks run after module alter hooks, so add this theme
+  // suggestion to the beginning of the array so that the suggestion added by
+  // the theme_suggestions_test module can be picked up when that module is
+  // enabled.
+  if ($hook == 'theme_test_general_suggestions') {
+    array_unshift($suggestions, 'theme_test_general_suggestions__' . 'theme_override');
+  }
+}
+
+/**
  * Implements hook_theme_suggestions_HOOK_alter().
  */
 function test_theme_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
+  drupal_set_message(__FUNCTION__ . '() executed.');
   // Theme alter hooks run after module alter hooks, so add this theme
   // suggestion to the beginning of the array so that the suggestion added by
   // the theme_suggestions_test module can be picked up when that module is
diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php
index 34758da..db11a21 100644
--- a/core/modules/system/theme.api.php
+++ b/core/modules/system/theme.api.php
@@ -190,6 +190,55 @@ function hook_theme_suggestions_HOOK(array $variables) {
 }
 
 /**
+ * Alters named suggestions for all theme hooks.
+ *
+ * This hook is invoked for all theme hooks, if you are targeting a specific
+ * theme hook it's best to use hook_theme_suggestions_HOOK_alter().
+ *
+ * The call order is as follows: all existing suggestion alter functions are
+ * called for module A, then all for module B, etc., followed by all for any
+ * base theme(s), and finally for the active theme. The order is
+ * determined by system weight, then by extension (module or theme) name.
+ *
+ * Within each module or theme, suggestion alter hooks are called in the
+ * following order: first, hook_theme_suggestions_alter(); second,
+ * hook_theme_suggestions_HOOK_alter(). So, for each module or theme, the more
+ * general hooks are called first followed by the more specific.
+ *
+ * In the following example, we provide an alternative template suggestion to
+ * node and taxonomy term templates based on the user being logged in.
+ * @code
+ * function MYMODULE_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+ *   if (\Drupal::currentUser()->isAuthenticated() && in_array($hook, array('node', 'taxonomy_term'))) {
+ *     $suggestions[] = $hook . '__' . 'logged_in';
+ *   }
+ * }
+ *
+ * @endcode
+ *
+ * @param array $suggestions
+ *   An array of alternate, more specific names for template files or theme
+ *   functions.
+ * @param array $variables
+ *   An array of variables passed to the theme hook. Note that this hook is
+ *   invoked before any variable preprocessing.
+ * @param string $hook
+ *   The base hook name. For example, if '#theme' => 'node__article' is called,
+ *   then $hook will be 'node', not 'node__article'. The specific hook called
+ *   (in this case 'node__article') is available in
+ *   $variables['theme_hook_original'].
+ *
+ * @return array
+ *   An array of theme suggestions.
+ *
+ * @see hook_theme_suggestions_HOOK_alter()
+ */
+function hook_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+  // Add an interface-language specific suggestion to all theme hooks.
+  $suggestions[] = $hook . '__' . \Drupal::languageManager()->getLanguage()->id;
+}
+
+/**
  * Alters named suggestions for a specific theme hook.
  *
  * This hook allows any module or theme to provide altenative theme function or
@@ -210,6 +259,7 @@ function hook_theme_suggestions_HOOK(array $variables) {
  *   An array of variables passed to the theme hook. Note that this hook is
  *   invoked before any preprocessing.
  *
+ * @see hook_theme_suggestions_alter()
  * @see hook_theme_suggestions_HOOK()
  */
 function hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables) {
