diff --git a/core/lib/Drupal/Core/Link.php b/core/lib/Drupal/Core/Link.php
index e435d4427910..c4fbd0dfc358 100644
--- a/core/lib/Drupal/Core/Link.php
+++ b/core/lib/Drupal/Core/Link.php
@@ -3,7 +3,7 @@
 namespace Drupal\Core;
 
 use Drupal\Core\Render\RenderableInterface;
-use Drupal\Core\Routing\LinkGeneratorTrait;
+use Drupal\Core\Utility\LinkGeneratorInterface;
 
 /**
  * Defines an object that holds information about a link.
@@ -11,9 +11,11 @@ use Drupal\Core\Routing\LinkGeneratorTrait;
 class Link implements RenderableInterface {
 
   /**
-   * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
+   * The link generator.
+   *
+   * @var \Drupal\Core\Utility\LinkGeneratorInterface
    */
-  use LinkGeneratorTrait;
+  protected $linkGenerator;
 
   /**
    * The text of the link.
@@ -147,4 +149,31 @@ class Link implements RenderableInterface {
     ];
   }
 
+  /**
+   * Returns the link generator.
+   *
+   * @return \Drupal\Core\Utility\LinkGeneratorInterface
+   *   The link generator
+   */
+  protected function getLinkGenerator() {
+    if (!isset($this->linkGenerator)) {
+      $this->linkGenerator = \Drupal::service('link_generator');
+    }
+    return $this->linkGenerator;
+  }
+
+  /**
+   * Sets the link generator service.
+   *
+   * @param \Drupal\Core\Utility\LinkGeneratorInterface $generator
+   *   The link generator service.
+   *
+   * @return $this
+   */
+  public function setLinkGenerator(LinkGeneratorInterface $generator) {
+    $this->linkGenerator = $generator;
+
+    return $this;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Logger/LoggerChannelInterface.php b/core/lib/Drupal/Core/Logger/LoggerChannelInterface.php
index b7d350183b7f..8764535f71b9 100644
--- a/core/lib/Drupal/Core/Logger/LoggerChannelInterface.php
+++ b/core/lib/Drupal/Core/Logger/LoggerChannelInterface.php
@@ -29,7 +29,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
  * @see \Psr\Log\LoggerInterface
  * @see \Drupal\Core\Logger\\LoggerChannelFactoryInterface
  * @see \Drupal\Core\Utility\LinkGeneratorInterface
- * @see \Drupal\Core\Routing\LinkGeneratorTrait::l()
+ * @see \Drupal\Core\Link::fromTextAndUrl()
  * @see \Drupal\Core\Entity\EntityInterface::link()
  */
 interface LoggerChannelInterface extends LoggerInterface {
diff --git a/core/lib/Drupal/Core/Routing/LinkGeneratorTrait.php b/core/lib/Drupal/Core/Routing/LinkGeneratorTrait.php
index 2cda6df9856c..4604058095d2 100644
--- a/core/lib/Drupal/Core/Routing/LinkGeneratorTrait.php
+++ b/core/lib/Drupal/Core/Routing/LinkGeneratorTrait.php
@@ -37,13 +37,14 @@ trait LinkGeneratorTrait {
    *   A GeneratedLink object containing a link to the given route and
    *   parameters and bubbleable metadata.
    *
-   * @deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0.
-   *   Use \Drupal\Core\Link instead.
+   * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+   *   \Drupal\Core\Link::fromTextAndUrl() instead.
    *
    * @see https://www.drupal.org/node/2614344
    * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
    */
   protected function l($text, Url $url) {
+    @trigger_error(__NAMESPACE__ . "\LinkGeneratorTrait::l() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Link::fromTextAndUrl() instead. See https://www.drupal.org/node/2614344", E_USER_DEPRECATED);
     return $this->getLinkGenerator()->generate($text, $url);
   }
 
@@ -52,8 +53,14 @@ trait LinkGeneratorTrait {
    *
    * @return \Drupal\Core\Utility\LinkGeneratorInterface
    *   The link generator
+   *
+   * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Inject the
+   *   'link_generator' service or use \Drupal\Core\Link instead
+   *
+   * @see https://www.drupal.org/node/2614344
    */
   protected function getLinkGenerator() {
+    @trigger_error(__NAMESPACE__ . "\LinkGeneratorTrait::getLinkGenerator() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Inject the 'link_generator' service or use \Drupal\Core\Link instead. See https://www.drupal.org/node/2614344", E_USER_DEPRECATED);
     if (!isset($this->linkGenerator)) {
       $this->linkGenerator = \Drupal::service('link_generator');
     }
@@ -67,8 +74,14 @@ trait LinkGeneratorTrait {
    *   The link generator service.
    *
    * @return $this
+   *
+   * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Inject the
+   *   'link_generator' service or use \Drupal\Core\Link instead
+   *
+   * @see https://www.drupal.org/node/2614344
    */
   public function setLinkGenerator(LinkGeneratorInterface $generator) {
+    @trigger_error(__NAMESPACE__ . "\LinkGeneratorTrait::setLinkGenerator() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Inject the 'link_generator' service or use \Drupal\Core\Link instead. See https://www.drupal.org/node/2614344", E_USER_DEPRECATED);
     $this->linkGenerator = $generator;
 
     return $this;
diff --git a/core/modules/aggregator/src/FeedForm.php b/core/modules/aggregator/src/FeedForm.php
index 5077e7d8d53d..86978e346a08 100644
--- a/core/modules/aggregator/src/FeedForm.php
+++ b/core/modules/aggregator/src/FeedForm.php
@@ -4,6 +4,7 @@ namespace Drupal\aggregator;
 
 use Drupal\Core\Entity\ContentEntityForm;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Link;
 use Drupal\Core\Url;
 
 /**
@@ -26,7 +27,7 @@ class FeedForm extends ContentEntityForm {
       $form_state->setRedirectUrl($feed->toUrl('canonical'));
     }
     else {
-      $this->logger('aggregator')->notice('Feed %feed added.', ['%feed' => $feed->label(), 'link' => $this->l($this->t('View'), new Url('aggregator.admin_overview'))]);
+      $this->logger('aggregator')->notice('Feed %feed added.', ['%feed' => $feed->label(), 'link' => Link::fromTextAndUrl($this->t('View'), new Url('aggregator.admin_overview'))->toString()]);
       $this->messenger()->addStatus($this->t('The feed %feed has been added.', ['%feed' => $view_link]));
     }
   }
diff --git a/core/modules/book/src/Controller/BookController.php b/core/modules/book/src/Controller/BookController.php
index 998fa16c5b41..bb09ae971966 100644
--- a/core/modules/book/src/Controller/BookController.php
+++ b/core/modules/book/src/Controller/BookController.php
@@ -5,6 +5,7 @@ namespace Drupal\book\Controller;
 use Drupal\book\BookExport;
 use Drupal\book\BookManagerInterface;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Link;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Url;
 use Drupal\node\NodeInterface;
@@ -84,7 +85,7 @@ class BookController extends ControllerBase {
         $url->setOptions($book['options']);
       }
       $row = [
-        $this->l($book['title'], $url),
+        Link::fromTextAndUrl($book['title'], $url),
       ];
       $links = [];
       $links['edit'] = [
@@ -116,7 +117,7 @@ class BookController extends ControllerBase {
   public function bookRender() {
     $book_list = [];
     foreach ($this->bookManager->getAllBooks() as $book) {
-      $book_list[] = $this->l($book['title'], $book['url']);
+      $book_list[] = Link::fromTextAndUrl($book['title'], $book['url']);
     }
     return [
       '#theme' => 'item_list',
diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php
index 952c9d139b9d..f70bcff77ae8 100644
--- a/core/modules/comment/src/CommentForm.php
+++ b/core/modules/comment/src/CommentForm.php
@@ -13,6 +13,7 @@ use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Link;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -379,7 +380,7 @@ class CommentForm extends ContentEntityForm {
       // Add a log entry.
       $logger->notice('Comment posted: %subject.', [
           '%subject' => $comment->getSubject(),
-          'link' => $this->l(t('View'), $comment->toUrl()->setOption('fragment', 'comment-' . $comment->id())),
+          'link' => Link::fromTextAndUrl(t('View'), $comment->toUrl()->setOption('fragment', 'comment-' . $comment->id()))->toString(),
         ]);
 
       // Explain the approval queue if necessary.
diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php
index 22c5a4098212..d7bdff90a95a 100644
--- a/core/modules/content_translation/src/Controller/ContentTranslationController.php
+++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php
@@ -9,6 +9,7 @@ use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Link;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -189,7 +190,7 @@ class ContentTranslationController extends ControllerBase {
           $link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : ['url' => $entity->toUrl()];
           if (!empty($link['url'])) {
             $link['url']->setOption('language', $language);
-            $row_title = $this->l($label, $link['url']);
+            $row_title = Link::fromTextAndUrl($label, $link['url'])->toString();
           }
 
           if (empty($link['url'])) {
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 54bc7ea6cabf..5b1ec80e478b 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -188,14 +188,14 @@ class DbLogController extends ControllerBase {
         $log_text = Unicode::truncate($title, 56, TRUE, TRUE);
         // The link generator will escape any unsafe HTML entities in the final
         // text.
-        $message = $this->l($log_text, new Url('dblog.event', ['event_id' => $dblog->wid], [
+        $message = Link::fromTextAndUrl($log_text, new Url('dblog.event', ['event_id' => $dblog->wid], [
           'attributes' => [
             // Provide a title for the link for useful hover hints. The
             // Attribute object will escape any unsafe HTML entities in the
             // final text.
             'title' => $title,
           ],
-        ]));
+        ]))->toString();
       }
       $username = [
         '#theme' => 'username',
diff --git a/core/modules/forum/src/Form/ForumForm.php b/core/modules/forum/src/Form/ForumForm.php
index 73f5e8f1bfb3..75264cc4d578 100644
--- a/core/modules/forum/src/Form/ForumForm.php
+++ b/core/modules/forum/src/Form/ForumForm.php
@@ -3,6 +3,7 @@
 namespace Drupal\forum\Form;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Link;
 use Drupal\Core\Url;
 use Drupal\taxonomy\TermForm;
 
@@ -78,7 +79,7 @@ class ForumForm extends TermForm {
 
     $route_name = $this->urlStub == 'container' ? 'entity.taxonomy_term.forum_edit_container_form' : 'entity.taxonomy_term.forum_edit_form';
     $route_parameters = ['taxonomy_term' => $term->id()];
-    $link = $this->l($this->t('Edit'), new Url($route_name, $route_parameters));
+    $link = Link::fromTextAndUrl($this->t('Edit'), new Url($route_name, $route_parameters))->toString();
     $view_link = $term->toLink($term->getName())->toString();
     switch ($status) {
       case SAVED_NEW:
diff --git a/core/modules/node/src/Controller/NodeController.php b/core/modules/node/src/Controller/NodeController.php
index 58712f37261e..e355d5e53ec6 100644
--- a/core/modules/node/src/Controller/NodeController.php
+++ b/core/modules/node/src/Controller/NodeController.php
@@ -7,6 +7,7 @@ use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Datetime\DateFormatterInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityRepositoryInterface;
+use Drupal\Core\Link;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Url;
 use Drupal\node\NodeStorageInterface;
@@ -211,7 +212,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
         // this case.
         $is_current_revision = $vid == $default_revision || (!$current_revision_displayed && $revision->wasDefaultRevision());
         if (!$is_current_revision) {
-          $link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
+          $link = Link::fromTextAndUrl($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]))->toString();
         }
         else {
           $link = $node->toLink($date)->toString();
diff --git a/core/modules/path/src/Controller/PathController.php b/core/modules/path/src/Controller/PathController.php
index 26fe851710c9..3577bc826909 100644
--- a/core/modules/path/src/Controller/PathController.php
+++ b/core/modules/path/src/Controller/PathController.php
@@ -4,6 +4,7 @@ namespace Drupal\path\Controller;
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Link;
 use Drupal\Core\Path\AliasStorageInterface;
 use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Core\Url;
@@ -84,13 +85,13 @@ class PathController extends ControllerBase {
       $row = [];
       // @todo Should Path module store leading slashes? See
       //   https://www.drupal.org/node/2430593.
-      $row['data']['alias'] = $this->l(Unicode::truncate($data->alias, 50, FALSE, TRUE), Url::fromUserInput($data->source, [
+      $row['data']['alias'] = Link::fromTextAndUrl(Unicode::truncate($data->alias, 50, FALSE, TRUE), Url::fromUserInput($data->source, [
         'attributes' => ['title' => $data->alias],
-      ]));
-      $row['data']['source'] = $this->l(Unicode::truncate($data->source, 50, FALSE, TRUE), Url::fromUserInput($data->source, [
+      ]))->toString();
+      $row['data']['source'] = Link::fromTextAndUrl(Unicode::truncate($data->source, 50, FALSE, TRUE), Url::fromUserInput($data->source, [
         'alias' => TRUE,
         'attributes' => ['title' => $data->source],
-      ]));
+      ]))->toString();
       if ($multilanguage) {
         $row['data']['language_name'] = $this->languageManager()->getLanguageName($data->langcode);
       }
diff --git a/core/modules/update/src/Form/UpdateManagerUpdate.php b/core/modules/update/src/Form/UpdateManagerUpdate.php
index e17d214bb7d7..c42928e8d240 100644
--- a/core/modules/update/src/Form/UpdateManagerUpdate.php
+++ b/core/modules/update/src/Form/UpdateManagerUpdate.php
@@ -5,6 +5,7 @@ namespace Drupal\update\Form;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Link;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -108,7 +109,7 @@ class UpdateManagerUpdate extends FormBase {
       // The project name to display can vary based on the info we have.
       if (!empty($project['title'])) {
         if (!empty($project['link'])) {
-          $project_name = $this->l($project['title'], Url::fromUri($project['link']));
+          $project_name = Link::fromTextAndUrl($project['title'], Url::fromUri($project['link']))->toString();
         }
         else {
           $project_name = $project['title'];
diff --git a/core/modules/views_ui/src/Controller/ViewsUIController.php b/core/modules/views_ui/src/Controller/ViewsUIController.php
index fe3d1595bc67..92a258558ba2 100644
--- a/core/modules/views_ui/src/Controller/ViewsUIController.php
+++ b/core/modules/views_ui/src/Controller/ViewsUIController.php
@@ -3,6 +3,7 @@
 namespace Drupal\views_ui\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Link;
 use Drupal\Core\Url;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewEntityInterface;
@@ -86,7 +87,7 @@ class ViewsUIController extends ControllerBase {
     foreach ($fields as $field_name => $views) {
       $rows[$field_name]['data'][0]['data']['#plain_text'] = $field_name;
       foreach ($views as $view) {
-        $rows[$field_name]['data'][1][] = $this->l($view, new Url('entity.view.edit_form', ['view' => $view]));
+        $rows[$field_name]['data'][1][] = Link::fromTextAndUrl($view, new Url('entity.view.edit_form', ['view' => $view]))->toString();
       }
       $item_list = [
         '#theme' => 'item_list',
@@ -120,7 +121,7 @@ class ViewsUIController extends ControllerBase {
       $views = [];
       // Link each view name to the view itself.
       foreach ($row['views'] as $row_name => $view) {
-        $views[] = $this->l($view, new Url('entity.view.edit_form', ['view' => $view]));
+        $views[] = Link::fromTextAndUrl($view, new Url('entity.view.edit_form', ['view' => $view]))->toString();
       }
       unset($row['views']);
       $row['views']['data'] = [
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 1d4434fe0d96..bc9a7caed082 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -9,6 +9,7 @@ use Drupal\Core\Ajax\HtmlCommand;
 use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\Datetime\DateFormatterInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Link;
 use Drupal\Core\Render\ElementInfoManagerInterface;
 use Drupal\Core\Url;
 use Drupal\Core\TempStore\SharedTempStoreFactory;
@@ -1062,13 +1063,13 @@ class ViewEditForm extends ViewFormBase {
       if ($handler->broken()) {
         $build['fields'][$id]['#class'][] = 'broken';
         $field_name = $handler->adminLabel();
-        $build['fields'][$id]['#link'] = $this->l($field_name, new Url('views_ui.form_handler', [
+        $build['fields'][$id]['#link'] = Link::fromTextAndUrl($field_name, new Url('views_ui.form_handler', [
           'js' => 'nojs',
           'view' => $view->id(),
           'display_id' => $display['id'],
           'type' => $type,
           'id' => $id,
-        ], ['attributes' => ['class' => ['views-ajax-link']]]));
+        ], ['attributes' => ['class' => ['views-ajax-link']]]))->toString();
         continue;
       }
 
@@ -1085,33 +1086,33 @@ class ViewEditForm extends ViewFormBase {
         // Add a [hidden] marker, if the field is excluded.
         $link_text .= ' [' . $this->t('hidden') . ']';
       }
-      $build['fields'][$id]['#link'] = $this->l($link_text, new Url('views_ui.form_handler', [
+      $build['fields'][$id]['#link'] = Link::fromTextAndUrl($link_text, new Url('views_ui.form_handler', [
         'js' => 'nojs',
         'view' => $view->id(),
         'display_id' => $display['id'],
         'type' => $type,
         'id' => $id,
-      ], ['attributes' => $link_attributes]));
+      ], ['attributes' => $link_attributes]))->toString();
       $build['fields'][$id]['#class'][] = Html::cleanCssIdentifier($display['id'] . '-' . $type . '-' . $id);
 
       if ($executable->display_handler->useGroupBy() && $handler->usesGroupBy()) {
-        $build['fields'][$id]['#settings_links'][] = $this->l(new FormattableMarkup('@text', ['@text' => $this->t('Aggregation settings')]), new Url('views_ui.form_handler_group', [
+        $build['fields'][$id]['#settings_links'][] = Link::fromTextAndUrl(new FormattableMarkup('@text', ['@text' => $this->t('Aggregation settings')]), new Url('views_ui.form_handler_group', [
           'js' => 'nojs',
           'view' => $view->id(),
           'display_id' => $display['id'],
           'type' => $type,
           'id' => $id,
-        ], ['attributes' => ['class' => ['views-button-configure', 'views-ajax-link'], 'title' => $this->t('Aggregation settings')]]));
+        ], ['attributes' => ['class' => ['views-button-configure', 'views-ajax-link'], 'title' => $this->t('Aggregation settings')]]))->toString();
       }
 
       if ($handler->hasExtraOptions()) {
-        $build['fields'][$id]['#settings_links'][] = $this->l(new FormattableMarkup('@text', ['@text' => $this->t('Settings')]), new Url('views_ui.form_handler_extra', [
+        $build['fields'][$id]['#settings_links'][] = Link::fromTextAndUrl(new FormattableMarkup('@text', ['@text' => $this->t('Settings')]), new Url('views_ui.form_handler_extra', [
           'js' => 'nojs',
           'view' => $view->id(),
           'display_id' => $display['id'],
           'type' => $type,
           'id' => $id,
-        ], ['attributes' => ['class' => ['views-button-configure', 'views-ajax-link'], 'title' => $this->t('Settings')]]));
+        ], ['attributes' => ['class' => ['views-button-configure', 'views-ajax-link'], 'title' => $this->t('Settings')]]))->toString();
       }
 
       if ($grouping) {