diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 0fce7ec..c2ebc4c 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -163,19 +163,13 @@ function comment_field_extra_fields() {
  */
 function comment_theme() {
   return array(
-    'comment_preview' => array(
-      'variables' => array('comment' => NULL),
-    ),
     'comment' => array(
-      'template' => 'comment',
       'render element' => 'elements',
-    ),
-    'comment_post_forbidden' => array(
-      'variables' => array('commented_entity' => NULL, 'field_name' => 'comment'),
+      'template' => 'comment',
     ),
     'comment_wrapper' => array(
-      'template' => 'comment-wrapper',
       'render element' => 'content',
+      'template' => 'comment-wrapper',
     ),
   );
 }
@@ -514,13 +508,8 @@ function comment_entity_view(EntityInterface $entity, EntityViewDisplayInterface
             }
           }
           else {
-            $comment_post_forbidden = array(
-              '#theme' => 'comment_post_forbidden',
-              '#commented_entity' => $entity,
-              '#field_name' => $field_name,
-            );
             $links['comment-forbidden'] = array(
-              'title' => drupal_render($comment_post_forbidden),
+              'title' => \Drupal::service('comment.manager')->forbiddenMessage($entity, $field_name),
               'html' => TRUE,
             );
           }
@@ -549,13 +538,8 @@ function comment_entity_view(EntityInterface $entity, EntityViewDisplayInterface
             }
           }
           else {
-            $comment_post_forbidden = array(
-              '#theme' => 'comment_post_forbidden',
-              '#commented_entity' => $entity,
-              '#field_name' => $field_name,
-            );
             $links['comment-forbidden'] = array(
-              'title' => drupal_render($comment_post_forbidden),
+              'title' => \Drupal::service('comment.manager')->forbiddenMessage($entity, $field_name),
               'html' => TRUE,
             );
           }
@@ -1495,56 +1479,6 @@ function template_preprocess_comment(&$variables) {
 }
 
 /**
- * Returns HTML for a "you can't post comments" notice.
- *
- * @param $variables
- *   An associative array containing:
- *   - commented_entity: The entity to which comments are attached to.
- *   - field_name: The comment field.
- *
- * @ingroup themeable
- */
-function theme_comment_post_forbidden($variables) {
-  $entity = $variables['commented_entity'];
-  $field_name = $variables['field_name'];
-
-  // Since this is expensive to compute, we cache it so that a page with many
-  // comments only has to query the database once for all the links.
-  $authenticated_post_comments = &drupal_static(__FUNCTION__, NULL);
-
-  if (\Drupal::currentUser()->isAnonymous()) {
-    if (!isset($authenticated_post_comments)) {
-      // We only output a link if we are certain that users will get permission
-      // to post comments by logging in.
-      $comment_roles = user_roles(TRUE, 'post comments');
-      $authenticated_post_comments = isset($comment_roles[DRUPAL_AUTHENTICATED_RID]);
-    }
-
-    if ($authenticated_post_comments) {
-      $instance = \Drupal::service('field.info')->getInstance($entity->entityType(), $entity->bundle(), $field_name);
-      // We cannot use drupal_get_destination() because these links
-      // sometimes appear on /node and taxonomy listing pages.
-      if ($instance->getSetting('form_location') == COMMENT_FORM_SEPARATE_PAGE) {
-        $destination = array('destination' => 'comment/reply/' . $entity->entityType() . '/' . $entity->id() . '/' . $field_name . '#comment-form');
-      }
-      else {
-        $uri = $entity->uri();
-        $destination = array('destination' => $uri['path'] . '#comment-form');
-      }
-
-      if (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
-        // Users can register themselves.
-        return t('<a href="@login">Log in</a> or <a href="@register">register</a> to post comments', array('@login' => url('user/login', array('query' => $destination)), '@register' => url('user/register', array('query' => $destination))));
-      }
-      else {
-        // Only admins can add new users, no public registration.
-        return t('<a href="@login">Log in</a> to post comments', array('@login' => url('user/login', array('query' => $destination))));
-      }
-    }
-  }
-}
-
-/**
  * Prepares variables for comment wrapper templates.
  *
  * Default template: comment-wrapper.html.twig.
diff --git a/core/modules/comment/comment.services.yml b/core/modules/comment/comment.services.yml
index 084f9b8..bcfb009 100644
--- a/core/modules/comment/comment.services.yml
+++ b/core/modules/comment/comment.services.yml
@@ -7,7 +7,7 @@ services:
 
   comment.manager:
     class: Drupal\comment\CommentManager
-    arguments: ['@field.info', '@entity.manager']
+    arguments: ['@field.info', '@entity.manager', '@current_user', '@config.factory', '@string_translation', '@url_generator']
 
   comment.route_enhancer:
       class: Drupal\comment\Routing\CommentBundleEnhancer
diff --git a/core/modules/comment/lib/Drupal/comment/CommentManager.php b/core/modules/comment/lib/Drupal/comment/CommentManager.php
index 9bca3c5..e1075b3 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentManager.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentManager.php
@@ -8,7 +8,12 @@
 namespace Drupal\comment;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Routing\UrlGeneratorInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\field\FieldInfo;
 
 /**
@@ -31,16 +36,63 @@ class CommentManager implements CommentManagerInterface {
   protected $entityManager;
 
   /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface $current_user
+   */
+  protected $currentUser;
+
+  /**
+   * Whether the DRUPAL_AUTHENTICATED_RID can post comments.
+   *
+   * @var bool
+   */
+  protected $authenticatedCanPostComments;
+
+  /**
+   * The user settings config object.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $userConfig;
+
+  /**
+   * The string translation service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface
+   */
+  protected $translationManager;
+
+  /**
+   * The url generator service.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
    * Construct the CommentManager object.
    *
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
+   *   The string translation service.
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
+   *   The url generator service.
    */
-  public function __construct(FieldInfo $field_info, EntityManagerInterface $entity_manager) {
+  public function __construct(FieldInfo $field_info, EntityManagerInterface $entity_manager, AccountInterface $current_user, ConfigFactory $config_factory, TranslationInterface $translation_manager, UrlGeneratorInterface $url_generator) {
     $this->fieldInfo = $field_info;
     $this->entityManager = $entity_manager;
+    $this->currentUser = $current_user;
+    $this->userConfig = $config_factory->get('user.settings');
+    $this->translationManager = $translation_manager;
+    $this->urlGenerator = $url_generator;
   }
 
   /**
@@ -200,4 +252,56 @@ public function getFieldUIPageTitle($commented_entity_type, $field_name) {
     return String::checkPlain($sample_instance->label);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function forbiddenMessage(EntityInterface $entity, $field_name) {
+    if ($this->currentUser->isAnonymous()) {
+      if (!isset($this->authenticatedCanPostComments)) {
+        // We only output a link if we are certain that users will get the
+        // permission to post comments by logging in.
+        $this->authenticatedCanPostComments = $this->entityManager
+          ->getStorageController('user_role')
+          ->load(DRUPAL_AUTHENTICATED_RID)
+          ->hasPermission('post comments');
+      }
+
+      if ($this->authenticatedCanPostComments) {
+        // We cannot use drupal_get_destination() because these links
+        // sometimes appear on /node and taxonomy listing pages.
+        if ($entity->get($field_name)->getFieldDefinition()->getSetting('form_location') == COMMENT_FORM_SEPARATE_PAGE) {
+          $destination = array('destination' => 'comment/reply/' . $entity->entityType() . '/' . $entity->id() . '/' . $field_name . '#comment-form');
+        }
+        else {
+          $uri = $entity->uri();
+          $destination = array('destination' => $uri['path'] . '#comment-form');
+        }
+
+        if ($this->userConfig->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
+          // Users can register themselves.
+          return $this->t('<a href="@login">Log in</a> or <a href="@register">register</a> to post comments', array(
+            '@login' => $this->urlGenerator->generateFromRoute('user.login', array(), array('query' => $destination)),
+            '@register' => $this->urlGenerator->generateFromRoute('user.register', array(), array('query' => $destination)),
+          ));
+        }
+        else {
+          // Only admins can add new users, no public registration.
+          return $this->t('<a href="@login">Log in</a> to post comments', array(
+            '@login' => $this->urlGenerator->generateFromRoute('user.login', array(), array('query' => $destination)),
+          ));
+        }
+      }
+    }
+    return '';
+  }
+
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return $this->translationManager->translate($string, $args, $options);
+  }
+
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentManagerInterface.php b/core/modules/comment/lib/Drupal/comment/CommentManagerInterface.php
index b1747f2..92f1a7b 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentManagerInterface.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentManagerInterface.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\comment;
+use Drupal\Core\Entity\EntityInterface;
 
 /**
  * Comment manager contains common functions to manage comment fields.
@@ -86,4 +87,21 @@ public function addBodyField($entity_type, $field_name);
    */
   public function getFieldUIPageTitle($commented_entity_type, $field_name);
 
+  /**
+   * Provides a message if posting comments is forbidden.
+   *
+   * If authenticated users can post comments, a message is returned that
+   * prompts the anonymous user to log in (or register, if applicable) that
+   * redirects to entity comment form. Otherwise, no message is returned.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to which comments are attached to.
+   * @param string $field_name
+   *   The field name on the entity to which comments are attached to.
+   *
+   * @return string
+   *   HTML for a "you can't post comments" notice.
+   */
+  public function forbiddenMessage(EntityInterface $entity, $field_name);
+
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
index 7dd6517..d118ea1 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
@@ -238,12 +238,7 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
         );
       }
       if (empty($links)) {
-        $comment_post_forbidden = array(
-          '#theme' => 'comment_post_forbidden',
-          '#commented_entity' => $commented_entity,
-          '#field_name' => $entity->field_name->value,
-        );
-        $links['comment-forbidden']['title'] = drupal_render($comment_post_forbidden);
+        $links['comment-forbidden']['title'] = \Drupal::service('comment.manager')->forbiddenMessage($commented_entity, $entity->field_name->value);
         $links['comment-forbidden']['html'] = TRUE;
       }
     }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
index 6eabd64..78f81f9 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
@@ -260,7 +260,7 @@ function assertCommentLinks(array $info) {
 
         // Anonymous users should see a note to log in or register in case
         // authenticated users are allowed to post comments.
-        // @see theme_comment_post_forbidden()
+        // @see \Drupal\comment\CommentManagerInterface::forbiddenMessage()
         if (!$this->loggedInUser) {
           if (user_access('post comments', $this->web_user)) {
             // The note depends on whether users are actually able to register.
