diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index 735a434..906af7f 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -15,6 +15,7 @@
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\SafeStringInterface;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Render\RenderableInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
@@ -134,6 +135,7 @@ public function getFilters() {
       // be used in "trans" tags.
       // @see TwigNodeTrans::compileString()
       new \Twig_SimpleFilter('passthrough', 'twig_raw_filter', array('is_safe' => array('html'))),
+      new \Twig_SimpleFilter('url_filter', array($this, 'urlFilter'), array('is_safe' => array('html'), 'needs_environment' => TRUE)),
       new \Twig_SimpleFilter('placeholder', [$this, 'escapePlaceholder'], array('is_safe' => array('html'), 'needs_environment' => TRUE)),
 
       // Replace twig's escape filter with our own.
@@ -346,6 +348,21 @@ public function escapePlaceholder($env, $string) {
   }
 
   /**
+   * Escapes URLs to HTML and strips them of dangerous protocols.
+   *
+   * @param \Twig_Environment $env
+   *   A Twig_Environment instance.
+   * @param mixed $string
+   *   The URL.
+   *
+   * @return null|string
+   *   The sanitized, rendered output, or NULL if there is no valid output.
+   */
+  public function urlFilter($env, $string) {
+    return Html::escape($env, UrlHelper::stripDangerousProtocols($string));
+  }
+
+  /**
    * Overrides twig_escape_filter().
    *
    * Replacement function for Twig's escape filter.
diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
index a7b723a..4f80ec2 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
@@ -142,6 +142,9 @@ protected function compileString(\Twig_NodeInterface $body) {
               case 'placeholder':
                 $argPrefix = '%';
                 break;
+              case 'url_filter':
+                $argPrefix = ':';
+                break;
             }
             $args = $args->getNode('node');
           }
diff --git a/core/modules/system/src/Tests/Theme/TwigTransTest.php b/core/modules/system/src/Tests/Theme/TwigTransTest.php
index 21aef18..2ddb6aa 100644
--- a/core/modules/system/src/Tests/Theme/TwigTransTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigTransTest.php
@@ -172,6 +172,12 @@ protected function assertTwigTransTags() {
       'O HAI NU TXTZZZZ.',
       '{% trans with {"context": "Lolspeak", "langcode": "zz"} %} was successfully translated with context in specified language.'
     );
+
+    $this->assertText(
+      "Kittens be full 'o awe: alert(0)",
+      "{% trans %} Kittens are awesome: {{ 'javascript:alert(0)'|url_filter }} {% endtrans %} was succesfully escaped and translated"
+    );
+
     // Makes sure https://www.drupal.org/node/2489024 doesn't happen without
     // twig debug.
     $this->assertNoText(pi(), 'Running php code inside a Twig trans is not possible.');
@@ -256,6 +262,9 @@ protected function poFileContents($langcode) {
 msgid "Pass-through: !string"
 msgstr "PAS-THRU: !string"
 
+msgid "Kittens are awesome: :url"
+msgstr "Kittens be full 'o awe: :url"
+
 msgid "Placeholder: %string"
 msgstr "PLAYSHOLDR: %string"
 
diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig
index 9623ba5..56e9d8f 100644
--- a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig
+++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig
@@ -101,3 +101,11 @@
     Number I never remember: ' . print(pi()) . '
   {% endtrans %}
 </div>
+
+{# Tests URL escaping. #}
+<div>
+  {% set url = 'javascript:alert(0)' %}
+  {% trans %}
+    Kittens are awesome: {{ url|url_filter }}
+  {% endtrans %}
+</div>
diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
index 3e78e91..2376dc7 100644
--- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
+++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Render\RenderableInterface;
 use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Template\Loader\StringLoader;
 use Drupal\Core\Template\TwigEnvironment;
 use Drupal\Core\Template\TwigExtension;
 use Drupal\Tests\UnitTestCase;
@@ -129,6 +130,22 @@ public function testSafeStringEscaping() {
   }
 
   /**
+   * Tests the escaping of objects implementing SafeStringInterface.
+   *
+   * @covers ::urlFilter
+   */
+  public function testUrlEscaping() {
+    $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface');
+    $twig_extension = new TwigExtension($renderer);
+
+    $loader = new StringLoader();
+    $twig = new \Twig_Environment($loader);
+    $twig->addExtension($twig_extension);
+    $result = $twig->render("{{ 'javascript:alert(0)'|url_filter }}");
+    $this->assertEquals('alert(0)', $result);
+  }
+
+  /**
    * @covers ::safeJoin
    */
   public function testSafeJoin() {
