diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index e2b09071cc879d1a43971944aa396863a3983756..fde8921de45ecfa573fef9ff963c23dbaff73889 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
@@ -1060,7 +1061,7 @@ function _filter_url_escape_comments($match, $escape = NULL) {
 }
 
 /**
- * Shortens long URLs to http://www.example.com/long/url...
+ * Shortens long URLs to http://www.example.com/long/url…
  */
 function _filter_url_trim($text, $length = NULL) {
   static $_length;
@@ -1068,9 +1069,8 @@ function _filter_url_trim($text, $length = NULL) {
     $_length = $length;
   }
 
-  // Use +3 for '...' string length.
-  if ($_length && strlen($text) > $_length + 3) {
-    $text = substr($text, 0, $_length) . '...';
+  if (isset($_length)) {
+    $text = Unicode::truncate($text, $_length, FALSE, TRUE);
   }
 
   return $text;
diff --git a/core/modules/filter/src/Tests/FilterUnitTest.php b/core/modules/filter/src/Tests/FilterUnitTest.php
index 832257dca2d31ddc0757ace574a0d1e051279177..b04a717dd2aa49998eda2f498690790df830047e 100644
--- a/core/modules/filter/src/Tests/FilterUnitTest.php
+++ b/core/modules/filter/src/Tests/FilterUnitTest.php
@@ -682,7 +682,7 @@ function testUrlFilter() {
     ));
     $tests = array(
       'www.trimmed.com/d/ff.ext?a=1&b=2#a1' => array(
-        '<a href="http://www.trimmed.com/d/ff.ext?a=1&amp;b=2#a1">www.trimmed.com/d/ff...</a>' => TRUE,
+        '<a href="http://www.trimmed.com/d/ff.ext?a=1&amp;b=2#a1">www.trimmed.com/d/f…</a>' => TRUE,
       ),
     );
     $this->assertFilteredString($filter, $tests);
@@ -715,15 +715,17 @@ function assertFilteredString($filter, $tests) {
       foreach ($tasks as $value => $is_expected) {
         // Not using assertIdentical, since combination with strpos() is hard to grok.
         if ($is_expected) {
-          $success = $this->assertTrue(strpos($result, $value) !== FALSE, format_string('@source: @value found.', array(
+          $success = $this->assertTrue(strpos($result, $value) !== FALSE, format_string('@source: @value found. Filtered result: @result.', array(
             '@source' => var_export($source, TRUE),
             '@value' => var_export($value, TRUE),
+            '@result' => var_export($result, TRUE),
           )));
         }
         else {
-          $success = $this->assertTrue(strpos($result, $value) === FALSE, format_string('@source: @value not found.', array(
+          $success = $this->assertTrue(strpos($result, $value) === FALSE, format_string('@source: @value not found. Filtered result: @result.', array(
             '@source' => var_export($source, TRUE),
             '@value' => var_export($value, TRUE),
+            '@result' => var_export($result, TRUE),
           )));
         }
         if (!$success) {