diff --git a/core/modules/help_topics/config/optional/block.block.seven_help_search.yml b/core/modules/help_topics/config/optional/block.block.seven_help_search.yml
new file mode 100644
index 0000000..6c3cfde
--- /dev/null
+++ b/core/modules/help_topics/config/optional/block.block.seven_help_search.yml
@@ -0,0 +1,26 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - search
+    - system
+  theme:
+    - seven
+id: seven_help_search
+theme: seven
+region: help
+weight: -4
+provider: null
+plugin: search_form_block
+settings:
+  id: search_form_block
+  label: 'Search help'
+  provider: search
+  label_display: visible
+  page_id: help_search
+visibility:
+  request_path:
+    id: request_path
+    pages: /admin/help
+    negate: false
+    context_mapping: {  }
diff --git a/core/modules/help_topics/config/optional/search.page.help_search.yml b/core/modules/help_topics/config/optional/search.page.help_search.yml
new file mode 100644
index 0000000..5ae158e
--- /dev/null
+++ b/core/modules/help_topics/config/optional/search.page.help_search.yml
@@ -0,0 +1,11 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - help_topics
+id: help_search
+label: Help
+path: help
+weight: 0
+plugin: help_search
+configuration: {  }
diff --git a/core/modules/help_topics/config/schema/help_topics.schema.yml b/core/modules/help_topics/config/schema/help_topics.schema.yml
new file mode 100644
index 0000000..77f1e80
--- /dev/null
+++ b/core/modules/help_topics/config/schema/help_topics.schema.yml
@@ -0,0 +1,3 @@
+search.plugin.help_search:
+  type: sequence
+  label: 'Help search'
diff --git a/core/modules/help_topics/help_topics.install b/core/modules/help_topics/help_topics.install
new file mode 100644
index 0000000..b0b604f
--- /dev/null
+++ b/core/modules/help_topics/help_topics.install
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Install and uninstall functions for help_topics module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function help_topics_schema() {
+  $schema['help_search_items'] = [
+    'description' => 'Stores information about indexed help search items',
+    'fields' => [
+      'sid' => [
+        'description' => 'Numeric index of this item in the search index',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ],
+      'plugin_type' => [
+        'description' => 'The help plugin type the item comes from',
+        'type' => 'varchar_ascii',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ],
+      'permission' => [
+        'description' => 'The permission needed to view this item',
+        'type' => 'varchar_ascii',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ],
+      'id' => [
+        'description' => 'The plugin ID of the item',
+        'type' => 'varchar_ascii',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ],
+    ],
+    'primary key' => ['sid'],
+    'indexes' => [
+      'plugin_type' => ['plugin_type'],
+      'id' => ['id'],
+    ],
+  ];
+
+  return $schema;
+}
diff --git a/core/modules/help_topics/help_topics.module b/core/modules/help_topics/help_topics.module
index 5b89b36..e9c6c46 100644
--- a/core/modules/help_topics/help_topics.module
+++ b/core/modules/help_topics/help_topics.module
@@ -15,10 +15,12 @@ function help_topics_help($route_name, RouteMatchInterface $route_match) {
   switch ($route_name) {
     case 'help.page.help_topics':
       $help_home = Url::fromRoute('help.main')->toString();
-      $locale_help = (\Drupal::moduleHandler()->moduleExists('locale')) ? Url::fromRoute('help.page', ['name' => 'locale'])->toString() : '#';
+      $module_handler = \Drupal::moduleHandler();
+      $locale_help = ($module_handler->moduleExists('locale')) ? Url::fromRoute('help.page', ['name' => 'locale'])->toString() : '#';
+      $search_help = ($module_handler->moduleExists('search')) ? Url::fromRoute('help.page', ['name' => 'search'])->toString() : '#';
       $output = '';
       $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Help Topics module adds module- and theme-provided help topics to the module overviews from the core Help module. For more information, see the <a href=":online">online documentation for the Help Topics module</a>.', [':online' => 'https://www.drupal.org/modules/help_topics']) . '</p>';
+      $output .= '<p>' . t('The Help Topics module adds module- and theme-provided help topics to the module overviews from the core Help module. If the core Search module is enabled, these topics are also searchable. For more information, see the <a href=":online">online documentation for the Help Topics module</a>.', [':online' => 'https://www.drupal.org/modules/help_topics']) . '</p>';
       $output .= '<h3>' . t('Uses') . '</h3>';
       $output .= '<dl>';
       $output .= '<dt>' . t('Viewing help topics') . '</dt>';
@@ -27,6 +29,8 @@ function help_topics_help($route_name, RouteMatchInterface $route_match) {
       $output .= '<dd>' . t("Modules and themes can provide help topics as Twig-file-based plugins in a project sub-directory called <em>help_topics</em>; plugin meta-data is provided in meta tags within each Twig file. Plugin-based help topics provided by modules and themes will automatically be updated when a module or theme is updated. Use the plugins in <em>core/modules/help_topics/help_topics</em> as a guide when writing and formatting a help topic plugin for your theme or module.") . '</dd>';
       $output .= '<dt>' . t('Translating help topics') . '</dt>';
       $output .= '<dd>' . t('The title and body text of help topics provided by contributed modules and themes are translatable using the <a href=":locale_help">Interface Translation module</a>. Topics provided by custom modules and themes are also translatable if they have been viewed at least once in a non-English language, which triggers putting their translatable text into the translation database.', [':locale_help' => $locale_help]) . '</dd>';
+      $output .= '<dt>' . t('Configuring help search') . '</dt>';
+      $output .= '<dd>' . t('To search help, you will need to install the core Search module, configure a search page, and add a search block to the Help page or another administrative page. (A search page is provided automatically, and if you use the core Seven administrative theme, a help search block is shown on the main Help page.) Then users with search permissions, and permission to view help, will be able to search help. See the <a href=":search_help">Search module help page</a> for more information.', [':search_help' => $search_help]) . '</dd>';
       $output .= '</dl>';
       return ['#markup' => $output];
 
@@ -51,3 +55,19 @@ function help_topics_theme() {
     ],
   ];
 }
+
+/**
+ * Implements hook_rebuild().
+ */
+function help_topics_rebuild() {
+  // If the Search module is also enabled, we need to trigger a search reindex
+  // when a module or theme is installed, uninstalled, or updated; and when
+  // languages are added, translations are changed, or string overrides are
+  // changed. These situations can cause an indexed help item to have different
+  // text, or for help items to be added or removed. So, we use a state
+  // variable to keep track of the need to reindex.
+
+  if (\Drupal::moduleHandler()->moduleExists('search')) {
+    \Drupal::state()->set('help_search_reindex_needed', TRUE);
+  }
+}
diff --git a/core/modules/help_topics/help_topics/help_topics.help_topic_search.html.twig b/core/modules/help_topics/help_topics/help_topics.help_topic_search.html.twig
new file mode 100644
index 0000000..394a74f
--- /dev/null
+++ b/core/modules/help_topics/help_topics/help_topics.help_topic_search.html.twig
@@ -0,0 +1,17 @@
+<meta name="help_topic:label" content="Configuring help search"/>
+<meta name="help_topic:top_level"/>
+<h2>{% trans %}Goal{% endtrans %}</h2>
+<p>{% trans %}Set up your site so that users can search for help.{% endtrans %}</p>
+<h2>{% trans %}Steps{% endtrans %}</h2>
+<ol>
+  <li>{% trans %}From the <em>Extend</em> administrative page (<em>admin/modules</em>), verify that the Search, Help, Help Topics, and Block modules are installed (or install them if they are not already installed).{% endtrans %}</li>
+  <li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Configuration</em> &gt; <em>Search and metadata</em> &gt; <em>Search pages</em> (<em>admin/config/search/pages</em>).{% endtrans %}</li>
+  <li>{% trans %}Verify that a Help search page is listed in the <em>Search pages</em> section. If not, add a new page of type <em>Help</em>.{% endtrans %}</li>
+  <li>{% trans %}Check the indexing status of the Help search page. If it is not fully indexed, run Cron until indexing is complete.{% endtrans %}</li>
+  <li>{% trans %}In the  <em>Manage</em> administrative menu, navigate to <em>Structure</em> &gt; <em>Block layout</em> (<em>admin/structure/block</em>).{% endtrans %}</li>
+  <li>{% trans %}Click the link for your administrative theme (such as the core Seven theme), near the top of the page.{% endtrans %}</li>
+  <li>{% trans %}See if there is already a help search block placed in the Help region of your administrative theme.{% endtrans %}</li>
+  <li>{% trans %}If there is not already a help search block placed, click <em>Place block</em>, and then click <em>Place block</em> in the row next to <em>Search form</em> in the dialog. Choose <em>Help</em> as the search page for the block, and in the <em>Visibility</em> &gt; <em>Pages</em> field, enter <em>/admin/help</em> to make the search form only visible on the main <em>Help</em> page.{% endtrans %}</li>
+  <li>{% trans %}Fill in the rest of the form and save the block.{% endtrans %}</li>
+  <li>{% trans %}Visit the main <em>Help</em> page (<em>admin/help</em>) to verify that the search block is visible, and try a search.{% endtrans %}</li>
+</ol>
diff --git a/core/modules/help_topics/src/Plugin/HelpSection/HelpTopicSection.php b/core/modules/help_topics/src/Plugin/HelpSection/HelpTopicSection.php
index c68f5ff..5c0c160 100644
--- a/core/modules/help_topics/src/Plugin/HelpSection/HelpTopicSection.php
+++ b/core/modules/help_topics/src/Plugin/HelpSection/HelpTopicSection.php
@@ -3,10 +3,16 @@
 namespace Drupal\help_topics\Plugin\HelpSection;
 
 use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\help_topics\SearchableHelpInterface;
 use Drupal\help_topics\HelpTopicPluginInterface;
 use Drupal\help_topics\HelpTopicPluginManagerInterface;
+use Drupal\Core\Language\LanguageDefault;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\help\Plugin\HelpSection\HelpSectionPluginBase;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Render\RenderContext;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -25,7 +31,7 @@
  *   experimental modules and development releases of contributed modules.
  *   See https://www.drupal.org/core/experimental for more information.
  */
-class HelpTopicSection extends HelpSectionPluginBase implements ContainerFactoryPluginInterface {
+class HelpTopicSection extends HelpSectionPluginBase implements ContainerFactoryPluginInterface, SearchableHelpInterface {
 
   /**
    * The plugin manager.
@@ -47,6 +53,27 @@ class HelpTopicSection extends HelpSectionPluginBase implements ContainerFactory
   protected $cacheableMetadata;
 
   /**
+   * The Renderer service to format the username and node.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The default language object.
+   *
+   * @var \Drupal\Core\Language\LanguageDefault
+   */
+  protected $defaultLanguage;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
    * Constructs a HelpTopicSection object.
    *
    * @param array $configuration
@@ -57,10 +84,19 @@ class HelpTopicSection extends HelpSectionPluginBase implements ContainerFactory
    *   The plugin implementation definition.
    * @param \Drupal\help_topics\HelpTopicPluginManagerInterface $plugin_manager
    *   The help topic plugin manager service.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   * @param \Drupal\Core\Language\LanguageDefault $default_language
+   *   The default language object.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, HelpTopicPluginManagerInterface $plugin_manager) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, HelpTopicPluginManagerInterface $plugin_manager, RendererInterface $renderer, LanguageDefault $default_language, LanguageManagerInterface $language_manager) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->pluginManager = $plugin_manager;
+    $this->renderer = $renderer;
+    $this->defaultLanguage = $default_language;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -71,7 +107,10 @@ public static function create(ContainerInterface $container, array $configuratio
       $configuration,
       $plugin_id,
       $plugin_definition,
-      $container->get('plugin.manager.help_topic')
+      $container->get('plugin.manager.help_topic'),
+      $container->get('renderer'),
+      $container->get('language.default'),
+      $container->get('language_manager')
     );
   }
 
@@ -110,7 +149,7 @@ public function listTopics() {
    * Gets the top level help topic plugins.
    *
    * @return \Drupal\help_topics\HelpTopicPluginInterface[]
-   *   The top level help topic plugins
+   *   The top level help topic plugins.
    */
   protected function getPlugins() {
     if (!isset($this->topLevelPlugins)) {
@@ -138,6 +177,71 @@ protected function getPlugins() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function listSearchableTopics() {
+    $definitions = $this->pluginManager->getDefinitions();
+    return array_column($definitions, 'id');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderTopicForSearch($id, LanguageInterface $language) {
+    $plugin = $this->pluginManager->createInstance($id);
+    if (!$plugin) {
+      return [];
+    }
+
+    // We are rendering this topic for search indexing or search results,
+    // possibly in a different language than the current language. The topic
+    // title and body come from translatable things in the Twig template, so we
+    // need to set the default language to the desired language, render them,
+    // then reset the default language so we do not affect other cron
+    // processes. Also, just in case there is an exception, wrap the whole
+    // thing in a try/finally block, and reset the language in the finally part.
+    $old_language = $this->defaultLanguage->get();
+    try {
+      $this->defaultLanguage->set($language);
+      $topic = [];
+
+      // Render the title in this language.
+      $title_build = [
+        'title' => [
+          '#type' => '#markup',
+          '#markup' => $plugin->getLabel(),
+        ],
+      ];
+      $topic['title'] = $this->renderer->renderPlain($title_build);
+      $cacheable_metadata = CacheableMetadata::createFromRenderArray($title_build);
+
+      // Render the body in this language. For this, we need to set up a render
+      // context, because the Twig plugins that provide the body assume one
+      // is present.
+      $context = new RenderContext();
+      $build = [
+        'body' => $this->renderer->executeInRenderContext($context, [$plugin, 'getBody']),
+      ];
+      $topic['text'] = $this->renderer->renderPlain($build);
+      $cacheable_metadata->addCacheableDependency(CacheableMetadata::createFromRenderArray($build));
+      $cacheable_metadata->addCacheableDependency($plugin);
+      if (!$context->isEmpty()) {
+        $cacheable_metadata->addCacheableDependency($context->pop());
+      }
+
+      // Add the other information.
+      $topic['url'] = $plugin->toUrl();
+      $topic['cacheable_metadata'] = $cacheable_metadata;
+    }
+    finally {
+      // Reset the language.
+      $this->defaultLanguage->set($old_language);
+    }
+
+    return $topic;
+  }
+
+  /**
    * Gets the merged CacheableMetadata for all the top level help topic plugins.
    *
    * @return \Drupal\Core\Cache\CacheableMetadata
diff --git a/core/modules/help_topics/src/Plugin/Search/HelpSearch.php b/core/modules/help_topics/src/Plugin/Search/HelpSearch.php
new file mode 100644
index 0000000..3a848d1
--- /dev/null
+++ b/core/modules/help_topics/src/Plugin/Search/HelpSearch.php
@@ -0,0 +1,503 @@
+<?php
+
+namespace Drupal\help_topics\Plugin\Search;
+
+use Drupal\Core\Access\AccessibleInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Query\Condition;
+use Drupal\Core\Database\Query\PagerSelectExtender;
+use Drupal\Core\Database\StatementInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\State\StateInterface;
+use Drupal\help\HelpSectionManager;
+use Drupal\help_topics\SearchableHelpInterface;
+use Drupal\search\Plugin\SearchIndexingInterface;
+use Drupal\search\Plugin\SearchPluginBase;
+use Drupal\search\SearchQuery;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Handles searching for help using the Search module index.
+ *
+ * Help items are indexed if their HelpSection plugin implements
+ * \Drupal\help\HelpSearchInterface.
+ *
+ * @see \Drupal\help\HelpSearchInterface
+ * @see \Drupal\help\HelpSectionPluginInterface
+ *
+ * @SearchPlugin(
+ *   id = "help_search",
+ *   title = @Translation("Help")
+ * )
+ *
+ * @internal
+ *   Help Topic is currently experimental and should only be leveraged by
+ *   experimental modules and development releases of contributed modules.
+ *   See https://www.drupal.org/core/experimental for more information.
+ */
+class HelpSearch extends SearchPluginBase implements AccessibleInterface, SearchIndexingInterface {
+
+  /**
+   * The current database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * A config object for 'search.settings'.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $searchSettings;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The Drupal account to use for checking for access to search.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * The messenger.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * The state object.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * The help section plugin manager.
+   *
+   * @var \Drupal\help\HelpSectionManager
+   */
+  protected $helpManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('database'),
+      $container->get('config.factory')->get('search.settings'),
+      $container->get('language_manager'),
+      $container->get('messenger'),
+      $container->get('current_user'),
+      $container->get('state'),
+      $container->get('plugin.manager.help_section')
+    );
+  }
+
+  /**
+   * Constructs a \Drupal\help_search\Plugin\Search\HelpSearch object.
+   *
+   * @param array $configuration
+   *   Configuration for the plugin.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The current database connection.
+   * @param \Drupal\Core\Config\Config $search_settings
+   *   A config object for 'search.settings'.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *   The messenger.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The $account object to use for checking for access to view help.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state object.
+   * @param \Drupal\help\HelpSectionManager $help_manager
+   *   The help section manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, Config $search_settings, LanguageManagerInterface $language_manager, MessengerInterface $messenger, AccountInterface $account = NULL, StateInterface $state, HelpSectionManager $help_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->database = $database;
+    $this->searchSettings = $search_settings;
+    $this->languageManager = $language_manager;
+    $this->messenger = $messenger;
+    $this->account = $account;
+    $this->state = $state;
+    $this->helpManager = $help_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
+    $result = AccessResult::allowedIfHasPermission($account, 'access administration pages');
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getType() {
+    return $this->getPluginId();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute() {
+    if ($this->isSearchExecutable()) {
+      $results = $this->findResults();
+
+      if ($results) {
+        return $this->prepareResults($results);
+      }
+    }
+
+    return [];
+  }
+
+  /**
+   * Finds the search results.
+   *
+   * @return \Drupal\Core\Database\StatementInterface|null
+   *   Results from search query execute() method, or NULL if the search
+   *   failed.
+   */
+  protected function findResults() {
+    // We need to check access for the current user to see the topics that
+    // could be returned by search. Each entry in the help_search_items
+    // database has an optional permission that comes from the HelpSection
+    // plugin, in addition to the generic 'access administration pages'
+    // permission. In order to enforce these permissions so only topics that
+    // the current user has permission to view are selected by the query, make
+    // a list of the permission strings and pre-check those permissions.
+    $this->addCacheContexts(['user.permissions']);
+    if (!$this->account->hasPermission('access administration pages')) {
+      return NULL;
+    }
+    $permissions = array_filter($this->database
+      ->select('help_search_items', 'hsi')
+      ->fields('hsi', ['permission'])
+      ->groupBy('hsi.permission')
+      ->execute()
+      ->fetchCol());
+    $denied_permissions = [];
+    foreach ($permissions as $permission) {
+      if (!$this->account->hasPermission($permission)) {
+        $denied_permissions[] = $permission;
+      }
+    }
+
+    $keys = $this->getKeywords();
+    $query = $this->database
+      ->select('search_index', 'i')
+      // Restrict the search to the current interface language.
+      ->condition('i.langcode', $this->languageManager->getCurrentLanguage()->getId())
+      ->extend(SearchQuery::class)
+      ->extend(PagerSelectExtender::class);
+    $query->join('help_search_items', 'hsi', 'i.sid = hsi.sid AND i.type = :type', [':type' => $this->getPluginId()]);
+    if (count($denied_permissions)) {
+      $query->condition('hsi.permission', $denied_permissions, 'NOT IN');
+    }
+    $query->searchExpression($keys, $this->getPluginId());
+
+    $find = $query
+      ->fields('i', ['langcode'])
+      // Since SearchQuery makes these into GROUP BY queries, if we add
+      // a field, for PostgreSQL we also need to make it an aggregate or a
+      // GROUP BY. In this case, we want GROUP BY.
+      ->groupBy('i.langcode')
+      ->limit(10)
+      ->execute();
+
+    // Check query status and set messages if needed.
+    $status = $query->getStatus();
+
+    if ($status & SearchQuery::EXPRESSIONS_IGNORED) {
+      $this->messenger->addWarning($this->t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', ['@count' => $this->searchSettings->get('and_or_limit')]));
+    }
+
+    if ($status & SearchQuery::LOWER_CASE_OR) {
+      $this->messenger->addWarning($this->t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'));
+    }
+
+    if ($status & SearchQuery::NO_POSITIVE_KEYWORDS) {
+      $this->messenger->addWarning($this->formatPlural($this->searchSettings->get('index.minimum_word_size'), 'You must include at least one keyword to match in the content, and punctuation is ignored.', 'You must include at least one keyword to match in the content. Keywords must be at least @count characters, and punctuation is ignored.'));
+    }
+
+    return $find;
+  }
+
+  /**
+   * Prepares search results for display.
+   *
+   * @param \Drupal\Core\Database\StatementInterface $found
+   *   Results found from a successful search query execute() method.
+   *
+   * @return array
+   *   List of search result render arrays, with links, snippets, etc.
+   */
+  protected function prepareResults(StatementInterface $found) {
+    $results = [];
+    $plugins = [];
+    $languages = [];
+    $keys = $this->getKeywords();
+    foreach ($found as $item) {
+      $record = $this->database->select('help_search_items', 'hsi')
+        ->condition('sid', $item->sid)
+        ->fields('hsi', ['plugin_type', 'id'])
+        ->execute()->fetchObject();
+      $type = $record->plugin_type;
+      if (!isset($plugins[$type])) {
+        $plugins[$type] = $this->getSectionPlugin($type);
+      }
+      if ($plugins[$type]) {
+        $langcode = $item->langcode;
+        if (!isset($languages[$langcode])) {
+          $languages[$langcode] = $this->languageManager->getLanguage($item->langcode);
+        }
+        $topic = $plugins[$type]->renderTopicForSearch($record->id, $languages[$langcode]);
+        if ($topic) {
+          if (isset($topic['cacheable_metadata'])) {
+            $this->addCacheableDependency($topic['cacheable_metadata']);
+          }
+          $results[] = [
+            'title' => $topic['title'],
+            'link' => $topic['url']->toString(),
+            'snippet' => search_excerpt($keys, $topic['title'] . ' ' . $topic['text'], $item->langcode),
+            'langcode' => $item->langcode,
+          ];
+        }
+      }
+    }
+
+    return $results;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateIndex() {
+    // See if we need to update the list of items to be indexed.
+    if ($this->state->get('help_search_reindex_needed', TRUE)) {
+      $this->markForReindex();
+    }
+
+    // Find some items that need to be updated. Start with ones that have
+    // never been indexed.
+    $limit = (int) $this->searchSettings->get('index.cron_limit');
+
+    $query = $this->database->select('help_search_items', 'hsi');
+    $query->fields('hsi', ['sid', 'plugin_type', 'id']);
+    $query->leftJoin('search_dataset', 'sd', 'sd.sid = hsi.sid AND sd.type = :type', [':type' => $this->getPluginId()]);
+    $query->where('sd.sid IS NULL');
+    $query->groupBy('hsi.sid')
+      ->groupBy('hsi.plugin_type')
+      ->groupBy('hsi.id')
+      ->range(0, $limit);
+    $items = $query->execute()->fetchAll();
+
+    // If there is still space in the indexing limit, index items that have
+    // been indexed before, but are currently marked as needing a re-index.
+    if (count($items) < $limit) {
+      $query = $this->database->select('help_search_items', 'hsi');
+      $query->fields('hsi', ['sid', 'plugin_type', 'id']);
+      $query->leftJoin('search_dataset', 'sd', 'sd.sid = hsi.sid AND sd.type = :type', [':type' => $this->getPluginId()]);
+      $query->condition('sd.reindex', 0, '<>');
+      $query->groupBy('hsi.sid')
+        ->groupBy('hsi.plugin_type')
+        ->groupBy('hsi.id')
+        ->range(0, $limit - count($items));
+      $items = $items + $query->execute()->fetchAll();
+    }
+
+    // Index the items we have chosen, in all available languages.
+    $language_list = $this->languageManager->getLanguages(LanguageInterface::STATE_CONFIGURABLE);
+    $plugins = [];
+
+    foreach ($items as $item) {
+      $type = $item->plugin_type;
+      if (!isset($plugins[$type])) {
+        $plugins[$type] = $this->getSectionPlugin($type);
+      }
+
+      if (!$plugins[$type]) {
+        $this->removeItemsFromIndex($item->sid);
+        continue;
+      }
+
+      $plugin = $plugins[$type];
+      search_index_clear($this->getPluginId(), $item->sid);
+      foreach ($language_list as $langcode => $language) {
+        $topic = $plugin->renderTopicForSearch($item->id, $language);
+        if ($topic) {
+          // Index the title plus body text.
+          $text = '<h1>' . $topic['title'] . '</h1>' . "\n" . $topic['text'];
+          search_index($this->getPluginId(), $item->sid, $langcode, $text);
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function indexClear() {
+    search_index_clear($this->getPluginId());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function markForReindex() {
+    // Update the list of help items. Start by fetching the existing list,
+    // so we can remove items not found at the end.
+    $old_list = $this->database->select('help_search_items', 'hsi')
+      ->fields('hsi', ['sid', 'id', 'plugin_type', 'permission'])
+      ->execute()
+      ->fetchAll();
+    $old_list_ordered = [];
+    $sids_to_remove = [];
+    foreach ($old_list as $item) {
+      $old_list_ordered[$item->plugin_type][$item->id] = $item;
+      $sids_to_remove[$item->sid] = $item->sid;
+    }
+
+    $plugins = $this->helpManager->getDefinitions();
+    foreach ($plugins as $plugin_id => $plugin_definition) {
+      $plugin = $this->getSectionPlugin($plugin_id);
+      if (!$plugin) {
+        continue;
+      }
+      $permission = $plugin_definition['permission'] ?? '';
+      $items = $plugin->listSearchableTopics();
+      if (!count($items)) {
+        continue;
+      }
+      foreach ($items as $id) {
+        if (isset($old_list_ordered[$plugin_id][$id])) {
+          $old_item = $old_list_ordered[$plugin_id][$id];
+          if ($old_item->permission == $permission) {
+            // Record has not changed.
+            unset($sids_to_remove[$old_item->sid]);
+            continue;
+          }
+
+          // Permission has changed, update record.
+          $this->database->update('help_search_items')
+            ->condition('sid', $old_item->sid)
+            ->fields(['permission' => $permission])
+            ->execute();
+          unset($sids_to_remove[$old_item->sid]);
+          continue;
+        }
+
+        // New record, create it.
+        $this->database->insert('help_search_items')
+          ->fields([
+            'plugin_type' => $plugin_id,
+            'permission' => $permission,
+            'id' => $id,
+          ])
+          ->execute();
+      }
+    }
+
+    // Remove remaining items from the index.
+    $this->removeItemsFromIndex($sids_to_remove);
+
+    // Mark all items currently in the search index database as needing
+    // reindex.
+    search_mark_for_reindex($this->getPluginId());
+    $this->state->set('help_search_reindex_needed', FALSE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function indexStatus() {
+    if ($this->state->get('help_search_reindex_needed', TRUE)) {
+      $this->markForReindex();
+    }
+
+    $total = $this->database->select('help_search_items', 'hsi')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+
+    $query = $this->database->select('help_search_items', 'hsi');
+    $query->addExpression('COUNT(DISTINCT(hsi.sid))');
+    $query->leftJoin('search_dataset', 'sd', 'hsi.sid = sd.sid AND sd.type = :type', [':type' => $this->getPluginId()]);
+    $condition = new Condition('OR');
+    $condition->condition('sd.reindex', 0, '<>')
+      ->isNull('sd.sid');
+    $query->condition($condition);
+    $remaining = $query->execute()->fetchField();
+
+    return [
+      'remaining' => $remaining,
+      'total' => $total,
+    ];
+  }
+
+  /**
+   * Removes an item or items from the search index.
+   *
+   * @param int|int[] $sids
+   *   Search ID (sid) of item or items to remove.
+   */
+  protected function removeItemsFromIndex($sids) {
+    $sids = (array) $sids;
+
+    // Remove items from our table in batches of 100, to avoid problems
+    // with having too many placeholders in database queries.
+    foreach (array_chunk($sids, 100) as $this_list) {
+      $this->database->delete('help_search_items')
+        ->condition('sid', $this_list, 'IN')
+        ->execute();
+    }
+    // Remove items from the search tables individually, as there is no bulk
+    // function to delete items from the search index.
+    foreach ($sids as $sid) {
+      search_index_clear($this->getPluginId(), $sid);
+    }
+  }
+
+  /**
+   * Instantiates a help section plugin and verifies it is searchable.
+   *
+   * @param string $plugin_type
+   *   Type of plugin to instantiate.
+   *
+   * @return \Drupal\help_topics\SearchableHelpInterface|false
+   *   Plugin object, or FALSE if it is not a searchable type.
+   */
+  protected function getSectionPlugin($plugin_type) {
+    /** @var \Drupal\help\HelpSectionPluginInterface $plugin */
+    $plugin = $this->helpManager->createInstance($plugin_type);
+    if ($plugin && $plugin instanceof SearchableHelpInterface) {
+      return $plugin;
+    }
+    // Intentionally return boolean to allow caching of results.
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/help_topics/src/SearchableHelpInterface.php b/core/modules/help_topics/src/SearchableHelpInterface.php
new file mode 100644
index 0000000..1cac8de
--- /dev/null
+++ b/core/modules/help_topics/src/SearchableHelpInterface.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\help_topics;
+
+use Drupal\Core\Language\LanguageInterface;
+
+/**
+ * Provides an interface for a HelpSection plugin that also supports search.
+ *
+ * @see \Drupal\help\HelpSectionPluginInterface
+ *
+ * @internal
+ *   Help Topic is currently experimental and should only be leveraged by
+ *   experimental modules and development releases of contributed modules.
+ *   See https://www.drupal.org/core/experimental for more information.
+ */
+interface SearchableHelpInterface {
+
+  /**
+   * Returns the IDs of topics that should be indexed for searching.
+   *
+   * @return string[]
+   *   An array of topic IDs that should be searchable. IDs need to be
+   *   unique within this HelpSection plugin type.
+   */
+  public function listSearchableTopics();
+
+  /**
+   * Renders one topic for search indexing or search results.
+   *
+   * @param string $id
+   *   The ID of the topic to be indexed.
+   * @param \Drupal\Core\Language\LanguageInterface $language
+   *   The language to render the topic in.
+   *
+   * @return array
+   *   An array of information about the topic, with elements:
+   *   - title: The title of the topic in this language.
+   *   - text: The text of the topic in this language.
+   *   - url: The URL of the topic as a \Drupal\Core\Url object.
+   *   - cacheable_metadata: (optional) An object to add as a cache dependency
+   *     if this topic is shown in search results.
+   */
+  public function renderTopicForSearch($id, LanguageInterface $language);
+
+}
diff --git a/core/modules/help_topics/tests/modules/help_topics_test/help_topics_test.permissions.yml b/core/modules/help_topics/tests/modules/help_topics_test/help_topics_test.permissions.yml
new file mode 100644
index 0000000..4147e30
--- /dev/null
+++ b/core/modules/help_topics/tests/modules/help_topics_test/help_topics_test.permissions.yml
@@ -0,0 +1,2 @@
+access test help:
+  title: 'Access the test help section'
diff --git a/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpSection/TestHelpSection.php b/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpSection/TestHelpSection.php
new file mode 100644
index 0000000..8772f39
--- /dev/null
+++ b/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpSection/TestHelpSection.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\help_topics_test\Plugin\HelpSection;
+
+use Drupal\help_topics\SearchableHelpInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Link;
+use Drupal\help\Plugin\HelpSection\HelpSectionPluginBase;
+
+/**
+ * Provides a searchable help section for testing.
+ *
+ * @HelpSection(
+ *   id = "help_topics_test",
+ *   title = @Translation("Test section"),
+ *   weight = 100,
+ *   description = @Translation("For testing search"),
+ *   permission = "access test help"
+ * )
+ */
+class TestHelpSection extends HelpSectionPluginBase implements SearchableHelpInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listTopics() {
+    return [
+      Link::fromTextAndUrl('Foo', Url::fromUri('https://foo.com')),
+      Link::fromTextAndUrl('Bar', Url::fromUri('https://bar.com')),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listSearchableTopics() {
+    return ['foo', 'bar'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderTopicForSearch($id, LanguageInterface $language) {
+    switch ($id) {
+      case 'foo':
+        if ($language->getId() == 'en') {
+          return [
+            'title' => 'Foo in English title',
+            'text' => 'Something about foo body',
+            'url' => Url::fromUri('https://foo.com'),
+          ];
+        }
+        return [
+          'title' => 'Foomm Foreign heading',
+          'text' => 'Fake foreign foomm text',
+          'url' => Url::fromUri('https://mm.foo.com'),
+        ];
+
+      case 'bar':
+        if ($language->getId() == 'en') {
+          return [
+            'title' => 'Bar in English',
+            'text' => 'Something about bar',
+            'url' => Url::fromUri('https://bar.com'),
+          ];
+        }
+        return [
+          'title' => 'Barmm Foreign',
+          'text' => 'Fake foreign barmm',
+          'url' => Url::fromUri('https://mm.bar.com'),
+        ];
+
+      default:
+        throw new \InvalidArgumentException('Unexpected ID encountered');
+    }
+  }
+
+}
diff --git a/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpTopic/TestHelpTopicPlugin.php b/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpTopic/TestHelpTopicPlugin.php
index 4c9717f..567a972 100644
--- a/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpTopic/TestHelpTopicPlugin.php
+++ b/core/modules/help_topics/tests/modules/help_topics_test/src/Plugin/HelpTopic/TestHelpTopicPlugin.php
@@ -14,7 +14,10 @@ class TestHelpTopicPlugin extends HelpTopicPluginBase {
    * {@inheritdoc}
    */
   public function getBody() {
-    return $this->pluginDefinition['body'];
+    return [
+      '#type' => 'markup',
+      '#markup' => $this->pluginDefinition['body'],
+    ];
   }
 
   /**
diff --git a/core/modules/help_topics/tests/src/Functional/HelpTopicSearchTest.php b/core/modules/help_topics/tests/src/Functional/HelpTopicSearchTest.php
new file mode 100644
index 0000000..ed63d19
--- /dev/null
+++ b/core/modules/help_topics/tests/src/Functional/HelpTopicSearchTest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Drupal\Tests\help_topics\Functional;
+
+use Drupal\Tests\Traits\Core\CronRunTrait;
+use Drupal\help_topics\Plugin\Search\HelpSearch;
+
+/**
+ * Verifies help topic search.
+ *
+ * @group help_topics
+ */
+class HelpTopicSearchTest extends HelpTopicTranslatedTestBase {
+
+  use CronRunTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'search',
+    'locale',
+    'language',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Log in.
+    $this->drupalLogin($this->createUser([
+      'access administration pages',
+      'administer site configuration',
+      'view the administration theme',
+      'administer permissions',
+      'administer languages',
+      'administer search',
+      'access test help',
+      'search content',
+    ]));
+
+    // Add English language and set to default.
+    $this->drupalPostForm('admin/config/regional/language/add', [
+      'predefined_langcode' => 'en',
+    ], 'Add language');
+    $this->drupalPostForm('admin/config/regional/language', [
+      'site_default_language' => 'en',
+    ], 'Save configuration');
+
+    // Clear the cache after changing the default language. Without this, or
+    // if this line is replaced by $this->resetAll(), visiting URLs like
+    // 'de/search/help' result in 404 errors.
+    $this->drupalPostForm('admin/config/development/performance', [], 'Clear all caches');
+
+    // Run cron until the topics are fully indexed, with a limit of 100 runs
+    // to avoid infinite loops.
+    $num_runs = 0;
+    $indexed = FALSE;
+    $plugin = HelpSearch::create($this->container, [], 'foo', []);
+    while (($num_runs < 100) && !$indexed) {
+      $this->cronRun();
+      $status = $plugin->indexStatus();
+      $indexed = ($status['remaining'] == $status['total']);
+    }
+
+    // Visit the Search settings page and verify it says 100% indexed.
+    $this->drupalGet('admin/config/search/pages');
+    $this->assertText('100% of the site has been indexed');
+  }
+
+  /**
+   * Tests help topic search.
+   */
+  public function testHelpSearch() {
+    $session = $this->assertSession();
+
+    // Verify that we can search for the test help items, in English and German.
+    $this->drupalPostForm('search/help', ['keys' => 'foo'], 'Search');
+    $session->addressMatches('|search/help|');
+    // Make sure there is not a German topic shown, and exactly one result
+    // for this topic.
+    $this->assertLinkExistsOnce('Foo in English title');
+    $session->linkByHrefNotExists('/de/');
+
+    $this->drupalPostForm('de/search/help', ['keys' => 'foomm'], 'Search');
+    $session->addressMatches('|search/help|');
+    $this->assertLinkExistsOnce('Foomm Foreign heading');
+    $session->linkByHrefNotExists('/en/');
+
+    // Verify we can search for help topics in English and (fake) German
+    // from the admin/help page.
+    $this->drupalPostForm('search/help', ['keys' => 'ABC'], 'Search');
+    $session->addressMatches('|search/help|');
+    $this->assertLinkExistsOnce('ABC Help Test module');
+
+    // Click the link and verify we ended up on the topic page.
+    $this->clickLink('ABC Help Test module');
+    $session->pageTextContains('This is a test');
+
+    $this->drupalPostForm('de/search/help', ['keys' => 'ABC'], 'Search');
+    $session->addressMatches('|de/search/help|');
+    $this->assertLinkExistsOnce('ABC-Hilfetestmodul');
+
+    // Verify we can also search for body text (other searches used text
+    // that was part of the title).
+    $this->drupalPostForm('search/help', ['keys' => 'writing'], 'Search');
+    $session->addressMatches('|search/help|');
+    $this->assertLinkExistsOnce('ABC Help Test module');
+
+    // Verify the cache tags and contexts.
+    $session->responseHeaderContains('X-Drupal-Cache-Tags', 'config:search.page.help_search');
+    $session->responseHeaderContains('X-Drupal-Cache-Tags', 'search_index:help_search');
+    $session->responseHeaderContains('X-Drupal-Cache-Contexts', 'user.permissions');
+    $session->responseHeaderContains('X-Drupal-Cache-Contexts', 'languages:language_interface');
+
+    // Verify that search works from the help page.
+    $this->drupalGet('admin/help');
+    $session->pageTextContains('Search help');
+    $this->drupalPostForm(NULL, ['keys' => 'ABC'], 'Search');
+    $session->addressMatches('|search/help|');
+    $this->assertLinkExistsOnce('ABC Help Test module');
+
+    // Log in as a user that does not have permission to see test help items,
+    // and verify they can still search for help topics but not the test items.
+    $this->drupalLogin($this->createUser([
+      'access administration pages',
+      'administer site configuration',
+      'view the administration theme',
+      'administer permissions',
+      'administer languages',
+      'administer search',
+      'search content',
+    ]));
+
+    $this->drupalGet('admin/help');
+    $session->pageTextContains('Search help');
+
+    $this->drupalPostForm('search/help', ['keys' => 'writing'], 'Search');
+    $session->addressMatches('|search/help|');
+    $this->assertLinkExistsOnce('ABC Help Test module');
+
+    $this->drupalPostForm('search/help', ['keys' => 'foo'], 'Search');
+    $session->addressMatches('|search/help|');
+    $session->linkNotExists('Foo in English title');
+    $session->pageTextContains('no results');
+  }
+
+  /**
+   * Asserts that a given link exists exactly once on the page.
+   *
+   * @param string $label
+   *   Label for the link to verify.
+   */
+  protected function assertLinkExistsOnce($label) {
+    $links = $this->xpath('//a[contains(text(), :label)]',
+      [':label' => $label]);
+    $this->assertEqual(count($links), 1, 'Exactly 1 link matching ' . $label . ' was found');
+  }
+
+}
diff --git a/core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php b/core/modules/help_topics/tests/src/Functional/HelpTopicTranslatedTestBase.php
similarity index 67%
copy from core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php
copy to core/modules/help_topics/tests/src/Functional/HelpTopicTranslatedTestBase.php
index d99e0b6..91952a3 100644
--- a/core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php
+++ b/core/modules/help_topics/tests/src/Functional/HelpTopicTranslatedTestBase.php
@@ -5,16 +5,15 @@
 use Drupal\Tests\BrowserTestBase;
 
 /**
- * Verifies help topic display and user access to help based on permissions.
+ * Provides a base class for functional help topic tests that use translation.
  *
- * @group help_topics
+ * Installs in German, with a small PO file, and sets up the task, help, and
+ * page title blocks.
  */
-class HelpTopicTranslationTest extends BrowserTestBase {
+abstract class HelpTopicTranslatedTestBase extends BrowserTestBase {
 
   /**
-   * Modules to enable.
-   *
-   * @var array
+   * {@inheritdoc}
    */
   public static $modules = [
     'help_topics_test',
@@ -42,28 +41,6 @@ protected function setUp() {
     $this->placeBlock('local_tasks_block', $settings);
     $this->placeBlock('local_actions_block', $settings);
     $this->placeBlock('page_title_block', $settings);
-
-    // Create user.
-    $this->drupalLogin($this->createUser([
-      'access administration pages',
-      'view the administration theme',
-      'administer permissions',
-    ]));
-  }
-
-  /**
-   * Tests help topic translations.
-   */
-  public function testHelpTopicTranslations() {
-    $session = $this->assertSession();
-
-    // Verify that help topic link is translated on admin/help.
-    $this->drupalGet('admin/help');
-    $session->linkExists('ABC-Hilfetestmodul');
-    // Verify that help topic is translated.
-    $this->drupalGet('admin/help/topic/help_topics_test.test');
-    $session->pageTextContains('ABC-Hilfetestmodul');
-    $session->pageTextContains('Übersetzung testen.');
   }
 
   /**
diff --git a/core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php b/core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php
index d99e0b6..dff2208 100644
--- a/core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php
+++ b/core/modules/help_topics/tests/src/Functional/HelpTopicTranslationTest.php
@@ -2,26 +2,12 @@
 
 namespace Drupal\Tests\help_topics\Functional;
 
-use Drupal\Tests\BrowserTestBase;
-
 /**
- * Verifies help topic display and user access to help based on permissions.
+ * Verifies help topic translations.
  *
  * @group help_topics
  */
-class HelpTopicTranslationTest extends BrowserTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = [
-    'help_topics_test',
-    'help',
-    'help_topics',
-    'block',
-  ];
+class HelpTopicTranslationTest extends HelpTopicTranslatedTestBase {
 
   /**
    * {@inheritdoc}
@@ -29,21 +15,7 @@ class HelpTopicTranslationTest extends BrowserTestBase {
   protected function setUp() {
     parent::setUp();
 
-    // These tests rely on some markup from the 'Seven' theme.
-    \Drupal::service('theme_installer')->install(['seven']);
-    \Drupal::service('config.factory')->getEditable('system.theme')->set('admin', 'seven')->save();
-
-    // Place various blocks.
-    $settings = [
-      'theme' => 'seven',
-      'region' => 'help',
-    ];
-    $this->placeBlock('help_block', $settings);
-    $this->placeBlock('local_tasks_block', $settings);
-    $this->placeBlock('local_actions_block', $settings);
-    $this->placeBlock('page_title_block', $settings);
-
-    // Create user.
+    // Create user and log in.
     $this->drupalLogin($this->createUser([
       'access administration pages',
       'view the administration theme',
@@ -66,32 +38,4 @@ public function testHelpTopicTranslations() {
     $session->pageTextContains('Übersetzung testen.');
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function installParameters() {
-    $parameters = parent::installParameters();
-    // Install in German. This will ensure the language and locale modules are
-    // installed.
-    $parameters['parameters']['langcode'] = 'de';
-    // Create a po file so we don't attempt to download one from
-    // localize.drupal.org and to have a test translation that will not change.
-    \Drupal::service('file_system')->mkdir($this->publicFilesDirectory . '/translations', NULL, TRUE);
-    $contents = <<<ENDPO
-msgid ""
-msgstr ""
-
-msgid "ABC Help Test module"
-msgstr "ABC-Hilfetestmodul"
-
-msgid "Test translation."
-msgstr "Übersetzung testen."
-
-ENDPO;
-    include_once $this->root . '/core/includes/install.core.inc';
-    $version = _install_get_version_info(\Drupal::VERSION)['major'] . '.0.0';
-    file_put_contents($this->publicFilesDirectory . "/translations/drupal-{$version}.de.po", $contents);
-    return $parameters;
-  }
-
 }
