diff --git a/core/includes/common.inc b/core/includes/common.inc
index 88d6fd3..515a2fc 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -11,6 +11,7 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\SystemListingInfo;
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * @file
@@ -1548,11 +1549,7 @@ function filter_xss_bad_protocol($string, $decode = TRUE) {
   // @todo Remove the $decode parameter in Drupal 8, and always assume an HTML
   //   string that needs decoding.
   if ($decode) {
-    if (!function_exists('decode_entities')) {
-      require_once DRUPAL_ROOT . '/core/includes/unicode.inc';
-    }
-
-    $string = decode_entities($string);
+    $string = Unicode::decodeEntities($string);
   }
   return check_plain(drupal_strip_dangerous_protocols($string));
 }
@@ -1582,7 +1579,7 @@ function format_rss_channel($title, $link, $description, $items, $langcode = NUL
   // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
   // We strip all HTML tags, but need to prevent double encoding from properly
   // escaped source data (such as &amp becoming &amp;amp;).
-  $output .= ' <description>' . check_plain(decode_entities(strip_tags($description))) . "</description>\n";
+  $output .= ' <description>' . check_plain(Unicode::decodeEntities(strip_tags($description))) . "</description>\n";
   $output .= ' <language>' . check_plain($langcode) . "</language>\n";
   $output .= format_xml_elements($args);
   $output .= $items;
@@ -3414,7 +3411,7 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
  *   The cleaned class name.
  */
 function drupal_html_class($class) {
-  return drupal_clean_css_identifier(drupal_strtolower($class));
+  return drupal_clean_css_identifier(Unicode::strtolower($class));
 }
 
 /**
@@ -3488,7 +3485,7 @@ function drupal_html_id($id) {
   }
   $seen_ids = &drupal_static(__FUNCTION__, $seen_ids_init);
 
-  $id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
+  $id = strtr(Unicode::strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
 
   // As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
   // only contain letters, digits ([0-9]), hyphens ("-"), underscores ("_"),
@@ -4866,7 +4863,6 @@ function _drupal_bootstrap_code() {
   require_once DRUPAL_ROOT . '/' . settings()->get('menu_inc', 'core/includes/menu.inc');
   require_once DRUPAL_ROOT . '/core/includes/tablesort.inc';
   require_once DRUPAL_ROOT . '/core/includes/file.inc';
-  require_once DRUPAL_ROOT . '/core/includes/unicode.inc';
   require_once DRUPAL_ROOT . '/core/includes/image.inc';
   require_once DRUPAL_ROOT . '/core/includes/form.inc';
   require_once DRUPAL_ROOT . '/core/includes/mail.inc';
diff --git a/core/includes/file.inc b/core/includes/file.inc
index 5f54190..ad9c062 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\StreamWrapper\LocalStream;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -445,7 +446,7 @@ function file_create_url($uri) {
     //   HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
     // Both types of relative URIs are characterized by a leading slash, hence
     // we can use a single check.
-    if (drupal_substr($uri, 0, 1) == '/') {
+    if (Unicode::substr($uri, 0, 1) == '/') {
       return $uri;
     }
     else {
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 3ebaca1..69ef376 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Template\Attribute;
@@ -1366,8 +1367,8 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
     // The following errors are always shown.
     if (isset($elements['#needs_validation'])) {
       // Verify that the value is not longer than #maxlength.
-      if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
-        form_error($elements, $t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
+      if (isset($elements['#maxlength']) && Unicode::strlen($elements['#value']) > $elements['#maxlength']) {
+        form_error($elements, $t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => Unicode::strlen($elements['#value']))));
       }
 
       if (isset($elements['#options']) && isset($elements['#value'])) {
@@ -1447,7 +1448,7 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
       // An unchecked checkbox has a #value of integer 0, different than string
       // '0', which could be a valid value.
       $is_empty_multiple = (!count($elements['#value']));
-      $is_empty_string = (is_string($elements['#value']) && drupal_strlen(trim($elements['#value'])) == 0);
+      $is_empty_string = (is_string($elements['#value']) && Unicode::strlen(trim($elements['#value'])) == 0);
       $is_empty_value = ($elements['#value'] === 0);
       if ($is_empty_multiple || $is_empty_string || $is_empty_value) {
         // Flag this element as #required_but_empty to allow #element_validate
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 088413a..63a3010 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -5,6 +5,7 @@
  * API functions for installing modules and themes.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
 use Drupal\locale\Gettext;
@@ -388,7 +389,7 @@ function drupal_verify_profile($install_state) {
   if (count($missing_modules)) {
     $modules = array();
     foreach ($missing_modules as $module) {
-      $modules[] = '<span class="admin-missing">' . drupal_ucfirst($module) . '</span>';
+      $modules[] = '<span class="admin-missing">' . Unicode::ucfirst($module) . '</span>';
     }
     $requirements['required_modules'] = array(
       'title'       => st('Required modules'),
diff --git a/core/includes/mail.inc b/core/includes/mail.inc
index c9ff601..1147625 100644
--- a/core/includes/mail.inc
+++ b/core/includes/mail.inc
@@ -5,6 +5,8 @@
  * API functions for processing and sending e-mail.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Auto-detect appropriate line endings for e-mails.
  *
@@ -463,11 +465,11 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
         // Fancy headers
         case 'h1':
           $indent[] = '======== ';
-          $casing = 'drupal_strtoupper';
+          $casing = 'Unicode::strtoupper';
           break;
         case 'h2':
           $indent[] = '-------- ';
-          $casing = 'drupal_strtoupper';
+          $casing = 'Unicode::strtoupper';
           break;
         case '/h1':
         case '/h2':
@@ -496,8 +498,8 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
     else {
       // Convert inline HTML text to plain text; not removing line-breaks or
       // white-space, since that breaks newlines when sanitizing plain-text.
-      $value = trim(decode_entities($value));
-      if (drupal_strlen($value)) {
+      $value = trim(Unicode::decodeEntities($value));
+      if (Unicode::strlen($value)) {
         $chunk = $value;
       }
     }
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index d719571..13fd258 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -8,6 +8,7 @@
 use Symfony\Component\HttpFoundation\Request;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
@@ -2141,11 +2142,11 @@ function menu_contextual_links($module, $parent_path, $args) {
       ->execute()
       ->fetchAllAssoc('path', PDO::FETCH_ASSOC);
   }
-  $parent_length = drupal_strlen($root_path) + 1;
+  $parent_length = Unicode::strlen($root_path) + 1;
   $map = $router_item['original_map'];
   foreach ($data[$root_path] as $item) {
     // Extract the actual "task" string from the path argument.
-    $key = drupal_substr($item['path'], $parent_length);
+    $key = Unicode::substr($item['path'], $parent_length);
 
     // Denormalize and translate the contextual link.
     _menu_translate($item, $map, TRUE);
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 37775e6..a9b7226 100644
--- a/core/includes/theme.maintenance.inc
+++ b/core/includes/theme.maintenance.inc
@@ -25,7 +25,6 @@ function _drupal_maintenance_theme() {
   require_once DRUPAL_ROOT . '/' . settings()->get('path_inc', 'core/includes/path.inc');
   require_once DRUPAL_ROOT . '/core/includes/theme.inc';
   require_once DRUPAL_ROOT . '/core/includes/common.inc';
-  require_once DRUPAL_ROOT . '/core/includes/unicode.inc';
   require_once DRUPAL_ROOT . '/core/includes/file.inc';
   require_once DRUPAL_ROOT . '/core/includes/module.inc';
   unicode_check();
diff --git a/core/includes/unicode.inc b/core/includes/unicode.inc
deleted file mode 100644
index 70a8fde..0000000
--- a/core/includes/unicode.inc
+++ /dev/null
@@ -1,631 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides Unicode-related conversions and operations.
- */
-
-/**
- * Matches Unicode characters that are word boundaries.
- *
- * Characters with the following General_category (gc) property values are used
- * as word boundaries. While this does not fully conform to the Word Boundaries
- * algorithm described in http://unicode.org/reports/tr29, as PCRE does not
- * contain the Word_Break property table, this simpler algorithm has to do.
- * - Cc, Cf, Cn, Co, Cs: Other.
- * - Pc, Pd, Pe, Pf, Pi, Po, Ps: Punctuation.
- * - Sc, Sk, Sm, So: Symbols.
- * - Zl, Zp, Zs: Separators.
- *
- * Non-boundary characters include the following General_category (gc) property
- * values:
- * - Ll, Lm, Lo, Lt, Lu: Letters.
- * - Mc, Me, Mn: Combining Marks.
- * - Nd, Nl, No: Numbers.
- *
- * Note that the PCRE property matcher is not used because we wanted to be
- * compatible with Unicode 5.2.0 regardless of the PCRE version used (and any
- * bugs in PCRE property tables).
- *
- * @see http://unicode.org/glossary
- */
-define('PREG_CLASS_UNICODE_WORD_BOUNDARY',
-  '\x{0}-\x{2F}\x{3A}-\x{40}\x{5B}-\x{60}\x{7B}-\x{A9}\x{AB}-\x{B1}\x{B4}' .
-  '\x{B6}-\x{B8}\x{BB}\x{BF}\x{D7}\x{F7}\x{2C2}-\x{2C5}\x{2D2}-\x{2DF}' .
-  '\x{2E5}-\x{2EB}\x{2ED}\x{2EF}-\x{2FF}\x{375}\x{37E}-\x{385}\x{387}\x{3F6}' .
-  '\x{482}\x{55A}-\x{55F}\x{589}-\x{58A}\x{5BE}\x{5C0}\x{5C3}\x{5C6}' .
-  '\x{5F3}-\x{60F}\x{61B}-\x{61F}\x{66A}-\x{66D}\x{6D4}\x{6DD}\x{6E9}' .
-  '\x{6FD}-\x{6FE}\x{700}-\x{70F}\x{7F6}-\x{7F9}\x{830}-\x{83E}' .
-  '\x{964}-\x{965}\x{970}\x{9F2}-\x{9F3}\x{9FA}-\x{9FB}\x{AF1}\x{B70}' .
-  '\x{BF3}-\x{BFA}\x{C7F}\x{CF1}-\x{CF2}\x{D79}\x{DF4}\x{E3F}\x{E4F}' .
-  '\x{E5A}-\x{E5B}\x{F01}-\x{F17}\x{F1A}-\x{F1F}\x{F34}\x{F36}\x{F38}' .
-  '\x{F3A}-\x{F3D}\x{F85}\x{FBE}-\x{FC5}\x{FC7}-\x{FD8}\x{104A}-\x{104F}' .
-  '\x{109E}-\x{109F}\x{10FB}\x{1360}-\x{1368}\x{1390}-\x{1399}\x{1400}' .
-  '\x{166D}-\x{166E}\x{1680}\x{169B}-\x{169C}\x{16EB}-\x{16ED}' .
-  '\x{1735}-\x{1736}\x{17B4}-\x{17B5}\x{17D4}-\x{17D6}\x{17D8}-\x{17DB}' .
-  '\x{1800}-\x{180A}\x{180E}\x{1940}-\x{1945}\x{19DE}-\x{19FF}' .
-  '\x{1A1E}-\x{1A1F}\x{1AA0}-\x{1AA6}\x{1AA8}-\x{1AAD}\x{1B5A}-\x{1B6A}' .
-  '\x{1B74}-\x{1B7C}\x{1C3B}-\x{1C3F}\x{1C7E}-\x{1C7F}\x{1CD3}\x{1FBD}' .
-  '\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}' .
-  '\x{1FFD}-\x{206F}\x{207A}-\x{207E}\x{208A}-\x{208E}\x{20A0}-\x{20B8}' .
-  '\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}' .
-  '\x{2116}-\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}' .
-  '\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}-\x{214D}\x{214F}' .
-  '\x{2190}-\x{244A}\x{249C}-\x{24E9}\x{2500}-\x{2775}\x{2794}-\x{2B59}' .
-  '\x{2CE5}-\x{2CEA}\x{2CF9}-\x{2CFC}\x{2CFE}-\x{2CFF}\x{2E00}-\x{2E2E}' .
-  '\x{2E30}-\x{3004}\x{3008}-\x{3020}\x{3030}\x{3036}-\x{3037}' .
-  '\x{303D}-\x{303F}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{3190}-\x{3191}' .
-  '\x{3196}-\x{319F}\x{31C0}-\x{31E3}\x{3200}-\x{321E}\x{322A}-\x{3250}' .
-  '\x{3260}-\x{327F}\x{328A}-\x{32B0}\x{32C0}-\x{33FF}\x{4DC0}-\x{4DFF}' .
-  '\x{A490}-\x{A4C6}\x{A4FE}-\x{A4FF}\x{A60D}-\x{A60F}\x{A673}\x{A67E}' .
-  '\x{A6F2}-\x{A716}\x{A720}-\x{A721}\x{A789}-\x{A78A}\x{A828}-\x{A82B}' .
-  '\x{A836}-\x{A839}\x{A874}-\x{A877}\x{A8CE}-\x{A8CF}\x{A8F8}-\x{A8FA}' .
-  '\x{A92E}-\x{A92F}\x{A95F}\x{A9C1}-\x{A9CD}\x{A9DE}-\x{A9DF}' .
-  '\x{AA5C}-\x{AA5F}\x{AA77}-\x{AA79}\x{AADE}-\x{AADF}\x{ABEB}' .
-  '\x{E000}-\x{F8FF}\x{FB29}\x{FD3E}-\x{FD3F}\x{FDFC}-\x{FDFD}' .
-  '\x{FE10}-\x{FE19}\x{FE30}-\x{FE6B}\x{FEFF}-\x{FF0F}\x{FF1A}-\x{FF20}' .
-  '\x{FF3B}-\x{FF40}\x{FF5B}-\x{FF65}\x{FFE0}-\x{FFFD}');
-
-/**
- * Returns Unicode library status and errors.
- */
-function unicode_requirements() {
-  // Ensure translations don't break during installation.
-  $t = get_t();
-
-  $libraries = array(
-    UNICODE_SINGLEBYTE => $t('Standard PHP'),
-    UNICODE_MULTIBYTE => $t('PHP Mbstring Extension'),
-    UNICODE_ERROR => $t('Error'),
-  );
-  $severities = array(
-    UNICODE_SINGLEBYTE => REQUIREMENT_WARNING,
-    UNICODE_MULTIBYTE => NULL,
-    UNICODE_ERROR => REQUIREMENT_ERROR,
-  );
-  $failed_check = unicode_check();
-  $library = $GLOBALS['multibyte'];
-
-  $requirements['unicode'] = array(
-    'title' => $t('Unicode library'),
-    'value' => $libraries[$library],
-    'severity' => $severities[$library],
-  );
-  $t_args = array('@url' => 'http://www.php.net/mbstring');
-  switch ($failed_check) {
-    case 'mb_strlen':
-      $requirements['unicode']['description'] = $t('Operations on Unicode strings are emulated on a best-effort basis. Install the <a href="@url">PHP mbstring extension</a> for improved Unicode support.', $t_args);
-      break;
-
-    case 'mbstring.func_overload':
-      $requirements['unicode']['description'] = $t('Multibyte string function overloading in PHP is active and must be disabled. Check the php.ini <em>mbstring.func_overload</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
-      break;
-
-    case 'mbstring.encoding_translation':
-      $requirements['unicode']['description'] = $t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.encoding_translation</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
-      break;
-
-    case 'mbstring.http_input':
-      $requirements['unicode']['description'] = $t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_input</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
-      break;
-
-    case 'mbstring.http_output':
-      $requirements['unicode']['description'] = $t('Multibyte string output conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_output</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
-      break;
-  }
-
-  return $requirements;
-}
-
-/**
- * Prepares a new XML parser.
- *
- * This is a wrapper around xml_parser_create() which extracts the encoding
- * from the XML data first and sets the output encoding to UTF-8. This function
- * should be used instead of xml_parser_create(), because PHP 4's XML parser
- * doesn't check the input encoding itself. "Starting from PHP 5, the input
- * encoding is automatically detected, so that the encoding parameter specifies
- * only the output encoding."
- *
- * This is also where unsupported encodings will be converted. Callers should
- * take this into account: $data might have been changed after the call.
- *
- * @param $data
- *   The XML data which will be parsed later.
- *
- * @return
- *   An XML parser object or FALSE on error.
- *
- * @ingroup php_wrappers
- */
-function drupal_xml_parser_create(&$data) {
-  // Default XML encoding is UTF-8
-  $encoding = 'utf-8';
-  $bom = FALSE;
-
-  // Check for UTF-8 byte order mark (PHP5's XML parser doesn't handle it).
-  if (!strncmp($data, "\xEF\xBB\xBF", 3)) {
-    $bom = TRUE;
-    $data = substr($data, 3);
-  }
-
-  // Check for an encoding declaration in the XML prolog if no BOM was found.
-  if (!$bom && preg_match('/^<\?xml[^>]+encoding="(.+?)"/', $data, $match)) {
-    $encoding = $match[1];
-  }
-
-  // Unsupported encodings are converted here into UTF-8.
-  $php_supported = array('utf-8', 'iso-8859-1', 'us-ascii');
-  if (!in_array(strtolower($encoding), $php_supported)) {
-    $out = drupal_convert_to_utf8($data, $encoding);
-    if ($out !== FALSE) {
-      $encoding = 'utf-8';
-      $data = preg_replace('/^(<\?xml[^>]+encoding)="(.+?)"/', '\\1="utf-8"', $out);
-    }
-    else {
-      watchdog('php', 'Could not convert XML encoding %s to UTF-8.', array('%s' => $encoding), WATCHDOG_WARNING);
-      return FALSE;
-    }
-  }
-
-  $xml_parser = xml_parser_create($encoding);
-  xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8');
-  return $xml_parser;
-}
-
-/**
- * Converts data to UTF-8.
- *
- * Requires the iconv, GNU recode or mbstring PHP extension.
- *
- * @param $data
- *   The data to be converted.
- * @param $encoding
- *   The encoding that the data is in.
- *
- * @return
- *   Converted data or FALSE.
- */
-function drupal_convert_to_utf8($data, $encoding) {
-  if (function_exists('iconv')) {
-    $out = @iconv($encoding, 'utf-8', $data);
-  }
-  elseif (function_exists('mb_convert_encoding')) {
-    $out = @mb_convert_encoding($data, 'utf-8', $encoding);
-  }
-  elseif (function_exists('recode_string')) {
-    $out = @recode_string($encoding . '..utf-8', $data);
-  }
-  else {
-    watchdog('php', 'Unsupported encoding %s. Please install iconv, GNU recode or mbstring for PHP.', array('%s' => $encoding), WATCHDOG_ERROR);
-    return FALSE;
-  }
-
-  return $out;
-}
-
-/**
- * Truncates a UTF-8-encoded string safely to a number of bytes.
- *
- * If the end position is in the middle of a UTF-8 sequence, it scans backwards
- * until the beginning of the byte sequence.
- *
- * Use this function whenever you want to chop off a string at an unsure
- * location. On the other hand, if you're sure that you're splitting on a
- * character boundary (e.g. after using strpos() or similar), you can safely
- * use substr() instead.
- *
- * @param $string
- *   The string to truncate.
- * @param $len
- *   An upper limit on the returned string length.
- *
- * @return
- *   The truncated string.
- */
-function drupal_truncate_bytes($string, $len) {
-  if (strlen($string) <= $len) {
-    return $string;
-  }
-  if ((ord($string[$len]) < 0x80) || (ord($string[$len]) >= 0xC0)) {
-    return substr($string, 0, $len);
-  }
-  // Scan backwards to beginning of the byte sequence.
-  while (--$len >= 0 && ord($string[$len]) >= 0x80 && ord($string[$len]) < 0xC0);
-
-  return substr($string, 0, $len);
-}
-
-/**
- * Truncates a UTF-8-encoded string safely to a number of characters.
- *
- * @param $string
- *   The string to truncate.
- * @param $max_length
- *   An upper limit on the returned string length, including trailing ellipsis
- *   if $add_ellipsis is TRUE.
- * @param $wordsafe
- *   If TRUE, attempt to truncate on a word boundary. Word boundaries are
- *   spaces, punctuation, and Unicode characters used as word boundaries in
- *   non-Latin languages; see PREG_CLASS_UNICODE_WORD_BOUNDARY for more
- *   information. If a word boundary cannot be found that would make the length
- *   of the returned string fall within length guidelines (see parameters
- *   $max_length and $min_wordsafe_length), word boundaries are ignored.
- * @param $add_ellipsis
- *   If TRUE, add t('...') to the end of the truncated string (defaults to
- *   FALSE). The string length will still fall within $max_length.
- * @param $min_wordsafe_length
- *   If $wordsafe is TRUE, the minimum acceptable length for truncation (before
- *   adding an ellipsis, if $add_ellipsis is TRUE). Has no effect if $wordsafe
- *   is FALSE. This can be used to prevent having a very short resulting string
- *   that will not be understandable. For instance, if you are truncating the
- *   string "See myverylongurlexample.com for more information" to a word-safe
- *   return length of 20, the only available word boundary within 20 characters
- *   is after the word "See", which wouldn't leave a very informative string. If
- *   you had set $min_wordsafe_length to 10, though, the function would realise
- *   that "See" alone is too short, and would then just truncate ignoring word
- *   boundaries, giving you "See myverylongurl..." (assuming you had set
- *   $add_ellipses to TRUE).
- *
- * @return string
- *   The truncated string.
- */
-function truncate_utf8($string, $max_length, $wordsafe = FALSE, $add_ellipsis = FALSE, $min_wordsafe_length = 1) {
-  $ellipsis = '';
-  $max_length = max($max_length, 0);
-  $min_wordsafe_length = max($min_wordsafe_length, 0);
-
-  if (drupal_strlen($string) <= $max_length) {
-    // No truncation needed, so don't add ellipsis, just return.
-    return $string;
-  }
-
-  if ($add_ellipsis) {
-    // Truncate ellipsis in case $max_length is small.
-    $ellipsis = drupal_substr(t('…'), 0, $max_length);
-    $max_length -= drupal_strlen($ellipsis);
-    $max_length = max($max_length, 0);
-  }
-
-  if ($max_length <= $min_wordsafe_length) {
-    // Do not attempt word-safe if lengths are bad.
-    $wordsafe = FALSE;
-  }
-
-  if ($wordsafe) {
-    $matches = array();
-    // Find the last word boundary, if there is one within $min_wordsafe_length
-    // to $max_length characters. preg_match() is always greedy, so it will
-    // find the longest string possible.
-    $found = preg_match('/^(.{' . $min_wordsafe_length . ',' . $max_length . '})[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']/u', $string, $matches);
-    if ($found) {
-      $string = $matches[1];
-    }
-    else {
-      $string = drupal_substr($string, 0, $max_length);
-    }
-  }
-  else {
-    $string = drupal_substr($string, 0, $max_length);
-  }
-
-  if ($add_ellipsis) {
-    // If we're adding an ellipsis, remove any trailing periods.
-    $string = rtrim($string, '.');
-
-    $string .= $ellipsis;
-  }
-
-  return $string;
-}
-
-/**
- * Encodes MIME/HTTP header values that contain incorrectly encoded characters.
- *
- * For example, mime_header_encode('tést.txt') returns "=?UTF-8?B?dMOpc3QudHh0?=".
- *
- * See http://www.rfc-editor.org/rfc/rfc2047.txt for more information.
- *
- * Notes:
- * - Only encode strings that contain non-ASCII characters.
- * - We progressively cut-off a chunk with truncate_utf8(). This is to ensure
- *   each chunk starts and ends on a character boundary.
- * - Using \n as the chunk separator may cause problems on some systems and may
- *   have to be changed to \r\n or \r.
- *
- * @param $string
- *   The header to encode.
- *
- * @return string
- *   The mime-encoded header.
- *
- * @see mime_header_decode()
- */
-function mime_header_encode($string) {
-  if (preg_match('/[^\x20-\x7E]/', $string)) {
-    $chunk_size = 47; // floor((75 - strlen("=?UTF-8?B??=")) * 0.75);
-    $len = strlen($string);
-    $output = '';
-    while ($len > 0) {
-      $chunk = drupal_truncate_bytes($string, $chunk_size);
-      $output .= ' =?UTF-8?B?' . base64_encode($chunk) . "?=\n";
-      $c = strlen($chunk);
-      $string = substr($string, $c);
-      $len -= $c;
-    }
-    return trim($output);
-  }
-  return $string;
-}
-
-/**
- * Decodes MIME/HTTP encoded header values.
- *
- * @param $header
- *   The header to decode.
- *
- * @return string
- *   The mime-decoded header.
- *
- * @see mime_header_encode()
- */
-function mime_header_decode($header) {
-  // First step: encoded chunks followed by other encoded chunks (need to collapse whitespace)
-  $header = preg_replace_callback('/=\?([^?]+)\?(Q|B)\?([^?]+|\?(?!=))\?=\s+(?==\?)/', '_mime_header_decode', $header);
-  // Second step: remaining chunks (do not collapse whitespace)
-  return preg_replace_callback('/=\?([^?]+)\?(Q|B)\?([^?]+|\?(?!=))\?=/', '_mime_header_decode', $header);
-}
-
-/**
- * Decodes encoded header data passed from mime_header_decode().
- *
- * Callback for preg_replace_callback() within mime_header_decode().
- *
- * @param $matches
- *   The array of matches from preg_replace_callback().
- *
- * @return string
- *   The mime-decoded string.
- *
- * @see mime_header_decode()
- */
-function _mime_header_decode($matches) {
-  // Regexp groups:
-  // 1: Character set name
-  // 2: Escaping method (Q or B)
-  // 3: Encoded data
-  $data = ($matches[2] == 'B') ? base64_decode($matches[3]) : str_replace('_', ' ', quoted_printable_decode($matches[3]));
-  if (strtolower($matches[1]) != 'utf-8') {
-    $data = drupal_convert_to_utf8($data, $matches[1]);
-  }
-  return $data;
-}
-
-/**
- * Decodes all HTML entities (including numerical ones) to regular UTF-8 bytes.
- *
- * Double-escaped entities will only be decoded once ("&amp;lt;" becomes "&lt;"
- * , not "<"). Be careful when using this function, as decode_entities can
- * revert previous sanitization efforts (&lt;script&gt; will become <script>).
- *
- * @param $text
- *   The text to decode entities in.
- *
- * @return
- *   The input $text, with all HTML entities decoded once.
- */
-function decode_entities($text) {
-  return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
-}
-
-/**
- * Counts the number of characters in a UTF-8 string.
- *
- * This is less than or equal to the byte count.
- *
- * @param $text
- *   The string to run the operation on.
- *
- * @return integer
- *   The length of the string.
- *
- * @ingroup php_wrappers
- */
-function drupal_strlen($text) {
-  global $multibyte;
-  if ($multibyte == UNICODE_MULTIBYTE) {
-    return mb_strlen($text);
-  }
-  else {
-    // Do not count UTF-8 continuation bytes.
-    return strlen(preg_replace("/[\x80-\xBF]/", '', $text));
-  }
-}
-
-/**
- * Uppercase a UTF-8 string.
- *
- * @param $text
- *   The string to run the operation on.
- *
- * @return string
- *   The string in uppercase.
- *
- * @ingroup php_wrappers
- */
-function drupal_strtoupper($text) {
-  global $multibyte;
-  if ($multibyte == UNICODE_MULTIBYTE) {
-    return mb_strtoupper($text);
-  }
-  else {
-    // Use C-locale for ASCII-only uppercase
-    $text = strtoupper($text);
-    // Case flip Latin-1 accented letters
-    $text = preg_replace_callback('/\xC3[\xA0-\xB6\xB8-\xBE]/', '_unicode_caseflip', $text);
-    return $text;
-  }
-}
-
-/**
- * Lowercase a UTF-8 string.
- *
- * @param $text
- *   The string to run the operation on.
- *
- * @return string
- *   The string in lowercase.
- *
- * @ingroup php_wrappers
- */
-function drupal_strtolower($text) {
-  global $multibyte;
-  if ($multibyte == UNICODE_MULTIBYTE) {
-    return mb_strtolower($text);
-  }
-  else {
-    // Use C-locale for ASCII-only lowercase
-    $text = strtolower($text);
-    // Case flip Latin-1 accented letters
-    $text = preg_replace_callback('/\xC3[\x80-\x96\x98-\x9E]/', '_unicode_caseflip', $text);
-    return $text;
-  }
-}
-
-/**
- * Flips U+C0-U+DE to U+E0-U+FD and back.
- *
- * @param $matches
- *   An array of matches.
- *
- * @return array
- *   The Latin-1 version of the array of matches.
- *
- * @see drupal_strtolower()
- */
-function _unicode_caseflip($matches) {
-  return $matches[0][0] . chr(ord($matches[0][1]) ^ 32);
-}
-
-/**
- * Capitalizes the first letter of a UTF-8 string.
- *
- * @param $text
- *   The string to convert.
- *
- * @return
- *   The string with the first letter as uppercase.
- *
- * @ingroup php_wrappers
- */
-function drupal_ucfirst($text) {
-  // Note: no mbstring equivalent!
-  return drupal_strtoupper(drupal_substr($text, 0, 1)) . drupal_substr($text, 1);
-}
-
-/**
- * Cuts off a piece of a string based on character indices and counts.
- *
- * Follows the same behavior as PHP's own substr() function. Note that for
- * cutting off a string at a known character/substring location, the usage of
- * PHP's normal strpos/substr is safe and much faster.
- *
- * @param $text
- *   The input string.
- * @param $start
- *   The position at which to start reading.
- * @param $length
- *   The number of characters to read.
- *
- * @return
- *   The shortened string.
- *
- * @ingroup php_wrappers
- */
-function drupal_substr($text, $start, $length = NULL) {
-  global $multibyte;
-  if ($multibyte == UNICODE_MULTIBYTE) {
-    return $length === NULL ? mb_substr($text, $start) : mb_substr($text, $start, $length);
-  }
-  else {
-    $strlen = strlen($text);
-    // Find the starting byte offset.
-    $bytes = 0;
-    if ($start > 0) {
-      // Count all the continuation bytes from the start until we have found
-      // $start characters or the end of the string.
-      $bytes = -1; $chars = -1;
-      while ($bytes < $strlen - 1 && $chars < $start) {
-        $bytes++;
-        $c = ord($text[$bytes]);
-        if ($c < 0x80 || $c >= 0xC0) {
-          $chars++;
-        }
-      }
-    }
-    elseif ($start < 0) {
-      // Count all the continuation bytes from the end until we have found
-      // abs($start) characters.
-      $start = abs($start);
-      $bytes = $strlen; $chars = 0;
-      while ($bytes > 0 && $chars < $start) {
-        $bytes--;
-        $c = ord($text[$bytes]);
-        if ($c < 0x80 || $c >= 0xC0) {
-          $chars++;
-        }
-      }
-    }
-    $istart = $bytes;
-
-    // Find the ending byte offset.
-    if ($length === NULL) {
-      $iend = $strlen;
-    }
-    elseif ($length > 0) {
-      // Count all the continuation bytes from the starting index until we have
-      // found $length characters or reached the end of the string, then
-      // backtrace one byte.
-      $iend = $istart - 1;
-      $chars = -1;
-      $last_real = FALSE;
-      while ($iend < $strlen - 1 && $chars < $length) {
-        $iend++;
-        $c = ord($text[$iend]);
-        $last_real = FALSE;
-        if ($c < 0x80 || $c >= 0xC0) {
-          $chars++;
-          $last_real = TRUE;
-        }
-      }
-      // Backtrace one byte if the last character we found was a real character
-      // and we don't need it.
-      if ($last_real && $chars >= $length) {
-        $iend--;
-      }
-    }
-    elseif ($length < 0) {
-      // Count all the continuation bytes from the end until we have found
-      // abs($start) characters, then backtrace one byte.
-      $length = abs($length);
-      $iend = $strlen; $chars = 0;
-      while ($iend > 0 && $chars < $length) {
-        $iend--;
-        $c = ord($text[$iend]);
-        if ($c < 0x80 || $c >= 0xC0) {
-          $chars++;
-        }
-      }
-      // Backtrace one byte if we are not at the beginning of the string.
-      if ($iend > 0) {
-        $iend--;
-      }
-    }
-    else {
-      // $length == 0, return an empty string.
-      return '';
-    }
-
-    return substr($text, $istart, max(0, $iend - $istart + 1));
-  }
-}
diff --git a/core/lib/Drupal/Component/Diff/DiffEngine.php b/core/lib/Drupal/Component/Diff/DiffEngine.php
index 1236610..9b15b19 100644
--- a/core/lib/Drupal/Component/Diff/DiffEngine.php
+++ b/core/lib/Drupal/Component/Diff/DiffEngine.php
@@ -8,6 +8,8 @@
  * You may copy this code freely under the conditions of the GPL.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 define('USE_ASSERTS', FALSE);
 
 /**
@@ -238,7 +240,7 @@ function diff($from_lines, $to_lines) {
    * Returns the whole line if it's small enough, or the MD5 hash otherwise.
    */
   function _line_hash($line) {
-    if (drupal_strlen($line) > $this->MAX_XREF_LENGTH()) {
+    if (Unicode::strlen($line) > $this->MAX_XREF_LENGTH()) {
       return md5($line);
     }
     else {
@@ -993,7 +995,7 @@ function addWords($words, $tag = '') {
       }
       if ($word[0] == "\n") {
         $this->_flushLine($tag);
-        $word = drupal_substr($word, 1);
+        $word = Unicode::substr($word, 1);
       }
       assert(!strstr($word, "\n"));
       $this->_group .= $word;
@@ -1037,7 +1039,7 @@ function _split($lines) {
         $words[] = "\n";
         $stripped[] = "\n";
       }
-      if ( drupal_strlen( $line ) > $this->MAX_LINE_LENGTH() ) {
+      if ( Unicode::strlen( $line ) > $this->MAX_LINE_LENGTH() ) {
         $words[] = $line;
         $stripped[] = $line;
       }
@@ -1256,7 +1258,7 @@ function render() {
           break;
         case 'delete':
           foreach ($chunk->orig as $i => $piece) {
-            if (strpos($piece, '<') === 0 && drupal_substr($piece, drupal_strlen($piece) - 1) === '>') {
+            if (strpos($piece, '<') === 0 && Unicode::substr($piece, Unicode::strlen($piece) - 1) === '>') {
               $output .= $piece;
             }
             else {
@@ -1267,7 +1269,7 @@ function render() {
         default:
           $chunk->closing = $this->process_chunk($chunk->closing);
           foreach ($chunk->closing as $i => $piece) {
-            if ($piece === ' ' || (strpos($piece, '<') === 0 && drupal_substr($piece, drupal_strlen($piece) - 1) === '>' && drupal_strtolower(drupal_substr($piece, 1, 3)) != 'img')) {
+            if ($piece === ' ' || (strpos($piece, '<') === 0 && Unicode::substr($piece, Unicode::strlen($piece) - 1) === '>' && Unicode::strtolower(Unicode::substr($piece, 1, 3)) != 'img')) {
               $output .= $piece;
             }
             else {
@@ -1291,11 +1293,11 @@ function process_chunk($chunk) {
       if (!isset($processed[$j])) {
         $processed[$j] = '';
       }
-      if (strpos($piece, '<') === 0 && drupal_substr($piece, drupal_strlen($piece) - 1) === '>') {
+      if (strpos($piece, '<') === 0 && Unicode::substr($piece, Unicode::strlen($piece) - 1) === '>') {
         $processed[$j] = $piece;
         $j++;
       }
-      elseif (isset($next) && strpos($next, '<') === 0 && drupal_substr($next, drupal_strlen($next) - 1) === '>') {
+      elseif (isset($next) && strpos($next, '<') === 0 && Unicode::substr($next, Unicode::strlen($next) - 1) === '>') {
         $processed[$j] .= $piece;
         $j++;
       }
diff --git a/core/lib/Drupal/Component/Utility/Unicode.php b/core/lib/Drupal/Component/Utility/Unicode.php
new file mode 100644
index 0000000..3e241ad
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/Unicode.php
@@ -0,0 +1,644 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Component\Utility\Unicode.
+ */
+
+namespace Drupal\Component\Utility;
+
+/**
+ * Provides Unicode-related conversions and operations.
+ */
+class Unicode {
+
+  /**
+   * Matches Unicode characters that are word boundaries.
+   *
+   * Characters with the following General_category (gc) property values are used
+   * as word boundaries. While this does not fully conform to the Word Boundaries
+   * algorithm described in http://unicode.org/reports/tr29, as PCRE does not
+   * contain the Word_Break property table, this simpler algorithm has to do.
+   * - Cc, Cf, Cn, Co, Cs: Other.
+   * - Pc, Pd, Pe, Pf, Pi, Po, Ps: Punctuation.
+   * - Sc, Sk, Sm, So: Symbols.
+   * - Zl, Zp, Zs: Separators.
+   *
+   * Non-boundary characters include the following General_category (gc) property
+   * values:
+   * - Ll, Lm, Lo, Lt, Lu: Letters.
+   * - Mc, Me, Mn: Combining Marks.
+   * - Nd, Nl, No: Numbers.
+   *
+   * Note that the PCRE property matcher is not used because we wanted to be
+   * compatible with Unicode 5.2.0 regardless of the PCRE version used (and any
+   * bugs in PCRE property tables).
+   *
+   * @see http://unicode.org/glossary
+   */
+  const PREG_CLASS_UNICODE_WORD_BOUNDARY = <<< 'EOD'
+\x{0}-\x{2F}\x{3A}-\x{40}\x{5B}-\x{60}\x{7B}-\x{A9}\x{AB}-\x{B1}\x{B4}
+\x{B6}-\x{B8}\x{BB}\x{BF}\x{D7}\x{F7}\x{2C2}-\x{2C5}\x{2D2}-\x{2DF}
+\x{2E5}-\x{2EB}\x{2ED}\x{2EF}-\x{2FF}\x{375}\x{37E}-\x{385}\x{387}\x{3F6}
+\x{482}\x{55A}-\x{55F}\x{589}-\x{58A}\x{5BE}\x{5C0}\x{5C3}\x{5C6}
+\x{5F3}-\x{60F}\x{61B}-\x{61F}\x{66A}-\x{66D}\x{6D4}\x{6DD}\x{6E9}
+\x{6FD}-\x{6FE}\x{700}-\x{70F}\x{7F6}-\x{7F9}\x{830}-\x{83E}
+\x{964}-\x{965}\x{970}\x{9F2}-\x{9F3}\x{9FA}-\x{9FB}\x{AF1}\x{B70}
+\x{BF3}-\x{BFA}\x{C7F}\x{CF1}-\x{CF2}\x{D79}\x{DF4}\x{E3F}\x{E4F}
+\x{E5A}-\x{E5B}\x{F01}-\x{F17}\x{F1A}-\x{F1F}\x{F34}\x{F36}\x{F38}
+\x{F3A}-\x{F3D}\x{F85}\x{FBE}-\x{FC5}\x{FC7}-\x{FD8}\x{104A}-\x{104F}
+\x{109E}-\x{109F}\x{10FB}\x{1360}-\x{1368}\x{1390}-\x{1399}\x{1400}
+\x{166D}-\x{166E}\x{1680}\x{169B}-\x{169C}\x{16EB}-\x{16ED}
+\x{1735}-\x{1736}\x{17B4}-\x{17B5}\x{17D4}-\x{17D6}\x{17D8}-\x{17DB}
+\x{1800}-\x{180A}\x{180E}\x{1940}-\x{1945}\x{19DE}-\x{19FF}
+\x{1A1E}-\x{1A1F}\x{1AA0}-\x{1AA6}\x{1AA8}-\x{1AAD}\x{1B5A}-\x{1B6A}
+\x{1B74}-\x{1B7C}\x{1C3B}-\x{1C3F}\x{1C7E}-\x{1C7F}\x{1CD3}\x{1FBD}
+\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}
+\x{1FFD}-\x{206F}\x{207A}-\x{207E}\x{208A}-\x{208E}\x{20A0}-\x{20B8}
+\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}
+\x{2116}-\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}
+\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}-\x{214D}\x{214F}
+\x{2190}-\x{244A}\x{249C}-\x{24E9}\x{2500}-\x{2775}\x{2794}-\x{2B59}
+\x{2CE5}-\x{2CEA}\x{2CF9}-\x{2CFC}\x{2CFE}-\x{2CFF}\x{2E00}-\x{2E2E}
+\x{2E30}-\x{3004}\x{3008}-\x{3020}\x{3030}\x{3036}-\x{3037}
+\x{303D}-\x{303F}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{3190}-\x{3191}
+\x{3196}-\x{319F}\x{31C0}-\x{31E3}\x{3200}-\x{321E}\x{322A}-\x{3250}
+\x{3260}-\x{327F}\x{328A}-\x{32B0}\x{32C0}-\x{33FF}\x{4DC0}-\x{4DFF}
+\x{A490}-\x{A4C6}\x{A4FE}-\x{A4FF}\x{A60D}-\x{A60F}\x{A673}\x{A67E}
+\x{A6F2}-\x{A716}\x{A720}-\x{A721}\x{A789}-\x{A78A}\x{A828}-\x{A82B}
+\x{A836}-\x{A839}\x{A874}-\x{A877}\x{A8CE}-\x{A8CF}\x{A8F8}-\x{A8FA}
+\x{A92E}-\x{A92F}\x{A95F}\x{A9C1}-\x{A9CD}\x{A9DE}-\x{A9DF}
+\x{AA5C}-\x{AA5F}\x{AA77}-\x{AA79}\x{AADE}-\x{AADF}\x{ABEB}
+\x{E000}-\x{F8FF}\x{FB29}\x{FD3E}-\x{FD3F}\x{FDFC}-\x{FDFD}
+\x{FE10}-\x{FE19}\x{FE30}-\x{FE6B}\x{FEFF}-\x{FF0F}\x{FF1A}-\x{FF20}
+\x{FF3B}-\x{FF40}\x{FF5B}-\x{FF65}\x{FFE0}-\x{FFFD}
+EOD;
+
+  /**
+   * Returns Unicode library status and errors.
+   *
+   * @return array
+   *   The structure is identical of a hook_requirements() return value.
+   */
+  public static function requirements() {
+    // Ensure translations don't break during installation.
+    $t = get_t();
+
+    $libraries = array(
+      UNICODE_SINGLEBYTE => $t('Standard PHP'),
+      UNICODE_MULTIBYTE => $t('PHP Mbstring Extension'),
+      UNICODE_ERROR => $t('Error'),
+    );
+    $severities = array(
+      UNICODE_SINGLEBYTE => REQUIREMENT_WARNING,
+      UNICODE_MULTIBYTE => NULL,
+      UNICODE_ERROR => REQUIREMENT_ERROR,
+    );
+    $failed_check = unicode_check();
+    $library = $GLOBALS['multibyte'];
+
+    $requirements['unicode'] = array(
+      'title' => $t('Unicode library'),
+      'value' => $libraries[$library],
+      'severity' => $severities[$library],
+    );
+    $t_args = array('@url' => 'http://www.php.net/mbstring');
+    switch ($failed_check) {
+      case 'mb_strlen':
+        $requirements['unicode']['description'] = $t('Operations on Unicode strings are emulated on a best-effort basis. Install the <a href="@url">PHP mbstring extension</a> for improved Unicode support.', $t_args);
+        break;
+
+      case 'mbstring.func_overload':
+        $requirements['unicode']['description'] = $t('Multibyte string function overloading in PHP is active and must be disabled. Check the php.ini <em>mbstring.func_overload</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
+        break;
+
+      case 'mbstring.encoding_translation':
+        $requirements['unicode']['description'] = $t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.encoding_translation</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
+        break;
+
+      case 'mbstring.http_input':
+        $requirements['unicode']['description'] = $t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_input</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
+        break;
+
+      case 'mbstring.http_output':
+        $requirements['unicode']['description'] = $t('Multibyte string output conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_output</em> setting. Please refer to the <a href="@url">PHP mbstring documentation</a> for more information.', $t_args);
+        break;
+    }
+
+    return $requirements;
+  }
+
+  /**
+   * Prepares a new XML parser.
+   *
+   * This is a wrapper around xml_parser_create() which extracts the encoding
+   * from the XML data first and sets the output encoding to UTF-8. This function
+   * should be used instead of xml_parser_create(), because PHP 4's XML parser
+   * doesn't check the input encoding itself. "Starting from PHP 5, the input
+   * encoding is automatically detected, so that the encoding parameter specifies
+   * only the output encoding."
+   *
+   * This is also where unsupported encodings will be converted. Callers should
+   * take this into account: $data might have been changed after the call.
+   *
+   * @param string $data
+   *   The XML data which will be parsed later.
+   *
+   * @return resource
+   *   An XML parser object or FALSE on error.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function createXMLParser(&$data) {
+    // Default XML encoding is UTF-8
+    $encoding = 'utf-8';
+    $bom = FALSE;
+
+    // Check for UTF-8 byte order mark (PHP5's XML parser doesn't handle it).
+    if (!strncmp($data, "\xEF\xBB\xBF", 3)) {
+      $bom = TRUE;
+      $data = substr($data, 3);
+    }
+
+    // Check for an encoding declaration in the XML prolog if no BOM was found.
+    if (!$bom && preg_match('/^<\?xml[^>]+encoding="(.+?)"/', $data, $match)) {
+      $encoding = $match[1];
+    }
+
+    // Unsupported encodings are converted here into UTF-8.
+    $php_supported = array('utf-8', 'iso-8859-1', 'us-ascii');
+    if (!in_array(strtolower($encoding), $php_supported)) {
+      $out = self::convertToUTF8($data, $encoding);
+      if ($out !== FALSE) {
+        $encoding = 'utf-8';
+        $data = preg_replace('/^(<\?xml[^>]+encoding)="(.+?)"/', '\\1="utf-8"', $out);
+      }
+      else {
+        watchdog('php', 'Could not convert XML encoding %s to UTF-8.', array('%s' => $encoding), WATCHDOG_WARNING);
+        return FALSE;
+      }
+    }
+
+    $xml_parser = xml_parser_create($encoding);
+    xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8');
+    return $xml_parser;
+  }
+
+  /**
+   * Converts data to UTF-8.
+   *
+   * Requires the iconv, GNU recode or mbstring PHP extension.
+   *
+   * @param string $data
+   *   The data to be converted.
+   * @param string $encoding
+   *   The encoding that the data is in.
+   *
+   * @return bool|string
+   *   Converted data or FALSE.
+   */
+  public static function convertToUTF8($data, $encoding) {
+    if (function_exists('iconv')) {
+      $out = @iconv($encoding, 'utf-8', $data);
+    }
+    elseif (function_exists('mb_convert_encoding')) {
+      $out = @mb_convert_encoding($data, 'utf-8', $encoding);
+    }
+    elseif (function_exists('recode_string')) {
+      $out = @recode_string($encoding . '..utf-8', $data);
+    }
+    else {
+      watchdog('php', 'Unsupported encoding %s. Please install iconv, GNU recode or mbstring for PHP.', array('%s' => $encoding), WATCHDOG_ERROR);
+      return FALSE;
+    }
+
+    return $out;
+  }
+
+  /**
+   * Truncates a UTF-8-encoded string safely to a number of bytes.
+   *
+   * If the end position is in the middle of a UTF-8 sequence, it scans backwards
+   * until the beginning of the byte sequence.
+   *
+   * Use this function whenever you want to chop off a string at an unsure
+   * location. On the other hand, if you're sure that you're splitting on a
+   * character boundary (e.g. after using strpos() or similar), you can safely
+   * use substr() instead.
+   *
+   * @param $string
+   *   The string to truncate.
+   * @param $len
+   *   An upper limit on the returned string length.
+   *
+   * @return
+   *   The truncated string.
+   */
+  public static function truncateBytes($string, $len) {
+    if (strlen($string) <= $len) {
+      return $string;
+    }
+    if ((ord($string[$len]) < 0x80) || (ord($string[$len]) >= 0xC0)) {
+      return substr($string, 0, $len);
+    }
+    // Scan backwards to beginning of the byte sequence.
+    while (--$len >= 0 && ord($string[$len]) >= 0x80 && ord($string[$len]) < 0xC0);
+
+    return substr($string, 0, $len);
+  }
+
+  /**
+   * Truncates a UTF-8-encoded string safely to a number of characters.
+   *
+   * @param string $string
+   *   The string to truncate.
+   * @param integer $max_length
+   *   An upper limit on the returned string length, including trailing ellipsis
+   *   if $add_ellipsis is TRUE.
+   * @param bool $wordsafe
+   *   If TRUE, attempt to truncate on a word boundary. Word boundaries are
+   *   spaces, punctuation, and Unicode characters used as word boundaries in
+   *   non-Latin languages; see self::PREG_CLASS_UNICODE_WORD_BOUNDARY for more
+   *   information. If a word boundary cannot be found that would make the length
+   *   of the returned string fall within length guidelines (see parameters
+   *   $max_length and $min_wordsafe_length), word boundaries are ignored.
+   * @param bool $add_ellipsis
+   *   If TRUE, add t('...') to the end of the truncated string (defaults to
+   *   FALSE). The string length will still fall within $max_length.
+   * @param integer $min_wordsafe_length
+   *   If $wordsafe is TRUE, the minimum acceptable length for truncation (before
+   *   adding an ellipsis, if $add_ellipsis is TRUE). Has no effect if $wordsafe
+   *   is FALSE. This can be used to prevent having a very short resulting string
+   *   that will not be understandable. For instance, if you are truncating the
+   *   string "See myverylongurlexample.com for more information" to a word-safe
+   *   return length of 20, the only available word boundary within 20 characters
+   *   is after the word "See", which wouldn't leave a very informative string. If
+   *   you had set $min_wordsafe_length to 10, though, the function would realise
+   *   that "See" alone is too short, and would then just truncate ignoring word
+   *   boundaries, giving you "See myverylongurl..." (assuming you had set
+   *   $add_ellipses to TRUE).
+   *
+   * @return string
+   *   The truncated string.
+   */
+  public static function truncateUTF8($string, $max_length, $wordsafe = FALSE, $add_ellipsis = FALSE, $min_wordsafe_length = 1) {
+    $ellipsis = '';
+    $max_length = max($max_length, 0);
+    $min_wordsafe_length = max($min_wordsafe_length, 0);
+
+    if (Unicode::strlen($string) <= $max_length) {
+      // No truncation needed, so don't add ellipsis, just return.
+      return $string;
+    }
+
+    if ($add_ellipsis) {
+      // Truncate ellipsis in case $max_length is small.
+      $ellipsis = self::substr(t('…'), 0, $max_length);
+      $max_length -= Unicode::strlen($ellipsis);
+      $max_length = max($max_length, 0);
+    }
+
+    if ($max_length <= $min_wordsafe_length) {
+      // Do not attempt word-safe if lengths are bad.
+      $wordsafe = FALSE;
+    }
+
+    if ($wordsafe) {
+      $matches = array();
+      // Find the last word boundary, if there is one within $min_wordsafe_length
+      // to $max_length characters. preg_match() is always greedy, so it will
+      // find the longest string possible.
+      $found = preg_match('/^(.{' . $min_wordsafe_length . ',' . $max_length . '})[' . self::PREG_CLASS_UNICODE_WORD_BOUNDARY . ']/u', $string, $matches);
+      if ($found) {
+        $string = $matches[1];
+      }
+      else {
+        $string = self::substr($string, 0, $max_length);
+      }
+    }
+    else {
+      $string = self::substr($string, 0, $max_length);
+    }
+
+    if ($add_ellipsis) {
+      // If we're adding an ellipsis, remove any trailing periods.
+      $string = rtrim($string, '.');
+
+      $string .= $ellipsis;
+    }
+
+    return $string;
+  }
+
+  /**
+   * Encodes MIME/HTTP header values that contain incorrectly encoded characters.
+   *
+   * For example, self::MIMEHeaderEncode('tést.txt') returns "=?UTF-8?B?dMOpc3QudHh0?=".
+   *
+   * See http://www.rfc-editor.org/rfc/rfc2047.txt for more information.
+   *
+   * Notes:
+   * - Only encode strings that contain non-ASCII characters.
+   * - We progressively cut-off a chunk with self::truncateBytes(). This is to
+   *   ensure each chunk starts and ends on a character boundary.
+   * - Using \n as the chunk separator may cause problems on some systems and may
+   *   have to be changed to \r\n or \r.
+   *
+   * @param string $string
+   *   The header to encode.
+   *
+   * @return string
+   *   The mime-encoded header.
+   *
+   * @see self::MIMEHeaderDecode()
+   */
+  public static function MIMEHeaderEncode($string) {
+    if (preg_match('/[^\x20-\x7E]/', $string)) {
+      $chunk_size = 47; // floor((75 - strlen("=?UTF-8?B??=")) * 0.75);
+      $len = strlen($string);
+      $output = '';
+      while ($len > 0) {
+        $chunk = self::truncateBytes($string, $chunk_size);
+        $output .= ' =?UTF-8?B?' . base64_encode($chunk) . "?=\n";
+        $c = strlen($chunk);
+        $string = substr($string, $c);
+        $len -= $c;
+      }
+      return trim($output);
+    }
+    return $string;
+  }
+
+  /**
+   * Decodes MIME/HTTP encoded header values.
+   *
+   * @param string $header
+   *   The header to decode.
+   *
+   * @return string
+   *   The mime-decoded header.
+   *
+   * @see self::MIMEHeaderEncode()
+   */
+  public static function MIMEHeaderDecode($header) {
+    // First step: encoded chunks followed by other encoded chunks (need to collapse whitespace)
+    $header = preg_replace_callback('/=\?([^?]+)\?(Q|B)\?([^?]+|\?(?!=))\?=\s+(?==\?)/', 'self::MIMEHeaderDecodeCallback', $header);
+    // Second step: remaining chunks (do not collapse whitespace)
+    return preg_replace_callback('/=\?([^?]+)\?(Q|B)\?([^?]+|\?(?!=))\?=/', 'self::MIMEHeaderDecodeCallback', $header);
+  }
+
+  /**
+   * Decodes encoded header data passed from self::MIMEHeaderDecode().
+   *
+   * Callback for preg_replace_callback() within self::MIMEHeaderDecode().
+   *
+   * @param array $matches
+   *   The array of matches from preg_replace_callback().
+   *
+   * @return string
+   *   The mime-decoded string.
+   *
+   * @see self::MIMEHeaderDecode()
+   */
+  protected static function MIMEHeaderDecodeCallback(array $matches) {
+    // Regexp groups:
+    // 1: Character set name
+    // 2: Escaping method (Q or B)
+    // 3: Encoded data
+    $data = ($matches[2] == 'B') ? base64_decode($matches[3]) : str_replace('_', ' ', quoted_printable_decode($matches[3]));
+    if (strtolower($matches[1]) != 'utf-8') {
+      $data = self::convertToUTF8($data, $matches[1]);
+    }
+    return $data;
+  }
+
+  /**
+   * Decodes all HTML entities (including numerical ones) to regular UTF-8 bytes.
+   *
+   * Double-escaped entities will only be decoded once ("&amp;lt;" becomes "&lt;"
+   * , not "<"). Be careful when using this function, as self::decodeEntities()
+   * can revert previous sanitization efforts (&lt;script&gt; will become
+   * <script>).
+   *
+   * @param string $text
+   *   The text to decode entities in.
+   *
+   * @return string
+   *   The input $text, with all HTML entities decoded once.
+   */
+  public static function decodeEntities($text) {
+    return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
+  }
+
+  /**
+   * Counts the number of characters in a UTF-8 string.
+   *
+   * This is less than or equal to the byte count.
+   *
+   * @param string $text
+   *   The string to run the operation on.
+   *
+   * @return integer
+   *   The length of the string.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function strlen($text) {
+    global $multibyte;
+    if ($multibyte == UNICODE_MULTIBYTE) {
+      return mb_strlen($text);
+    }
+    else {
+      // Do not count UTF-8 continuation bytes.
+      return strlen(preg_replace("/[\x80-\xBF]/", '', $text));
+    }
+  }
+
+  /**
+   * Uppercase a UTF-8 string.
+   *
+   * @param string $text
+   *   The string to run the operation on.
+   *
+   * @return string string
+   *   The string in uppercase.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function strtoupper($text) {
+    global $multibyte;
+    if ($multibyte == UNICODE_MULTIBYTE) {
+      return mb_strtoupper($text);
+    }
+    else {
+      // Use C-locale for ASCII-only uppercase
+      $text = strtoupper($text);
+      // Case flip Latin-1 accented letters
+      $text = preg_replace_callback('/\xC3[\xA0-\xB6\xB8-\xBE]/', 'self::caseflip', $text);
+      return $text;
+    }
+  }
+
+  /**
+   * Lowercase a UTF-8 string.
+   *
+   * @param string $text
+   *   The string to run the operation on.
+   *
+   * @return string
+   *   The string in lowercase.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function strtolower($text) {
+    global $multibyte;
+    if ($multibyte == UNICODE_MULTIBYTE) {
+      return mb_strtolower($text);
+    }
+    else {
+      // Use C-locale for ASCII-only lowercase
+      $text = strtolower($text);
+      // Case flip Latin-1 accented letters
+      $text = preg_replace_callback('/\xC3[\x80-\x96\x98-\x9E]/', 'self::caseflip', $text);
+      return $text;
+    }
+  }
+
+  /**
+   * Flips U+C0-U+DE to U+E0-U+FD and back.
+   *
+   * @param array $matches
+   *   An array of matches.
+   *
+   * @return array
+   *   The Latin-1 version of the array of matches.
+   *
+   * @see self::strtolower()
+   */
+  protected static function caseflip($matches) {
+    return $matches[0][0] . chr(ord($matches[0][1]) ^ 32);
+  }
+
+  /**
+   * Capitalizes the first letter of a UTF-8 string.
+   *
+   * @param string $text
+   *   The string to convert.
+   *
+   * @return string
+   *   The string with the first letter as uppercase.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function ucfirst($text) {
+    // Note: no mbstring equivalent!
+    return Unicode::strtoupper(self::substr($text, 0, 1)) . self::substr($text, 1);
+  }
+
+  /**
+   * Cuts off a piece of a string based on character indices and counts.
+   *
+   * Follows the same behavior as PHP's own substr() function. Note that for
+   * cutting off a string at a known character/substring location, the usage of
+   * PHP's normal strpos/substr is safe and much faster.
+   *
+   * @param string $text
+   *   The input string.
+   * @param integer $start
+   *   The position at which to start reading.
+   * @param integer $length
+   *   The number of characters to read.
+   *
+   * @return string
+   *   The shortened string.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function substr($text, $start, $length = NULL) {
+    global $multibyte;
+    if ($multibyte == UNICODE_MULTIBYTE) {
+      return $length === NULL ? mb_substr($text, $start) : mb_substr($text, $start, $length);
+    }
+    else {
+      $strlen = strlen($text);
+      // Find the starting byte offset.
+      $bytes = 0;
+      if ($start > 0) {
+        // Count all the continuation bytes from the start until we have found
+        // $start characters or the end of the string.
+        $bytes = -1; $chars = -1;
+        while ($bytes < $strlen - 1 && $chars < $start) {
+          $bytes++;
+          $c = ord($text[$bytes]);
+          if ($c < 0x80 || $c >= 0xC0) {
+            $chars++;
+          }
+        }
+      }
+      elseif ($start < 0) {
+        // Count all the continuation bytes from the end until we have found
+        // abs($start) characters.
+        $start = abs($start);
+        $bytes = $strlen; $chars = 0;
+        while ($bytes > 0 && $chars < $start) {
+          $bytes--;
+          $c = ord($text[$bytes]);
+          if ($c < 0x80 || $c >= 0xC0) {
+            $chars++;
+          }
+        }
+      }
+      $istart = $bytes;
+
+      // Find the ending byte offset.
+      if ($length === NULL) {
+        $iend = $strlen;
+      }
+      elseif ($length > 0) {
+        // Count all the continuation bytes from the starting index until we have
+        // found $length characters or reached the end of the string, then
+        // backtrace one byte.
+        $iend = $istart - 1;
+        $chars = -1;
+        $last_real = FALSE;
+        while ($iend < $strlen - 1 && $chars < $length) {
+          $iend++;
+          $c = ord($text[$iend]);
+          $last_real = FALSE;
+          if ($c < 0x80 || $c >= 0xC0) {
+            $chars++;
+            $last_real = TRUE;
+          }
+        }
+        // Backtrace one byte if the last character we found was a real character
+        // and we don't need it.
+        if ($last_real && $chars >= $length) {
+          $iend--;
+        }
+      }
+      elseif ($length < 0) {
+        // Count all the continuation bytes from the end until we have found
+        // abs($start) characters, then backtrace one byte.
+        $length = abs($length);
+        $iend = $strlen; $chars = 0;
+        while ($iend > 0 && $chars < $length) {
+          $iend--;
+          $c = ord($text[$iend]);
+          if ($c < 0x80 || $c >= 0xC0) {
+            $chars++;
+          }
+        }
+        // Backtrace one byte if we are not at the beginning of the string.
+        if ($iend > 0) {
+          $iend--;
+        }
+      }
+      else {
+        // $length == 0, return an empty string.
+        return '';
+      }
+
+      return substr($text, $istart, max(0, $iend - $istart + 1));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
index 4fbba3b..d07dfc8 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Database\Driver\mysql;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Query\Condition;
 use Drupal\Core\Database\SchemaObjectExistsException;
@@ -207,7 +208,7 @@ protected function processField($field) {
     // Set the correct database-engine specific datatype.
     // In case one is already provided, force it to uppercase.
     if (isset($field['mysql_type'])) {
-      $field['mysql_type'] = drupal_strtoupper($field['mysql_type']);
+      $field['mysql_type'] = Unicode::strtoupper($field['mysql_type']);
     }
     else {
       $map = $this->getFieldTypeMap();
@@ -480,7 +481,7 @@ public function prepareComment($comment, $length = NULL) {
     // Truncate comment to maximum comment length.
     if (isset($length)) {
       // Add table prefixes before truncating.
-      $comment = truncate_utf8($this->connection->prefixTables($comment), $length, TRUE, TRUE);
+      $comment = Unicode::truncateUTF8($this->connection->prefixTables($comment), $length, TRUE, TRUE);
     }
 
     return $this->connection->quote($comment);
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
index 0b65280..769cfaf 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Database\Driver\pgsql;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Query\Condition;
 use Drupal\Core\Database\SchemaObjectExistsException;
@@ -232,7 +233,7 @@ protected function processField($field) {
     // Set the correct database-engine specific datatype.
     // In case one is already provided, force it to lowercase.
     if (isset($field['pgsql_type'])) {
-      $field['pgsql_type'] = drupal_strtolower($field['pgsql_type']);
+      $field['pgsql_type'] = Unicode::strtolower($field['pgsql_type']);
     }
     else {
       $map = $this->getFieldTypeMap();
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
index 91105e7..98b8ef4 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Database\Driver\sqlite;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\SchemaObjectExistsException;
 use Drupal\Core\Database\SchemaObjectDoesNotExistException;
 use Drupal\Core\Database\Schema as DatabaseSchema;
@@ -127,7 +128,7 @@ protected function processField($field) {
     // Set the correct database-engine specific datatype.
     // In case one is already provided, force it to uppercase.
     if (isset($field['sqlite_type'])) {
-      $field['sqlite_type'] = drupal_strtoupper($field['sqlite_type']);
+      $field['sqlite_type'] = Unicode::strtoupper($field['sqlite_type']);
     }
     else {
       $map = $this->getFieldTypeMap();
diff --git a/core/lib/Drupal/Core/Database/Query/Condition.php b/core/lib/Drupal/Core/Database/Query/Condition.php
index c23ad76..b396214 100644
--- a/core/lib/Drupal/Core/Database/Query/Condition.php
+++ b/core/lib/Drupal/Core/Database/Query/Condition.php
@@ -308,7 +308,7 @@ protected function mapConditionOperator($operator) {
     }
     else {
       // We need to upper case because PHP index matches are case sensitive but
-      // do not need the more expensive drupal_strtoupper because SQL statements are ASCII.
+      // do not need the more expensive Unicode::strtoupper because SQL statements are ASCII.
       $operator = strtoupper($operator);
       $return = isset($specials[$operator]) ? $specials[$operator] : array();
     }
diff --git a/core/lib/Drupal/Core/Database/Query/TableSortExtender.php b/core/lib/Drupal/Core/Database/Query/TableSortExtender.php
index cf1a042..7b23117 100644
--- a/core/lib/Drupal/Core/Database/Query/TableSortExtender.php
+++ b/core/lib/Drupal/Core/Database/Query/TableSortExtender.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Database\Query;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Connection;
 
 /**
@@ -47,7 +48,7 @@ public function orderByHeader(array $header) {
       $field = preg_replace('/[^A-Za-z0-9_.]+/', '', $ts['sql']);
 
       // Sort order can only be ASC or DESC.
-      $sort = drupal_strtoupper($ts['sort']);
+      $sort = Unicode::strtoupper($ts['sort']);
       $sort = in_array($sort, array('ASC', 'DESC')) ? $sort : '';
       $this->orderBy($field, $sort);
     }
diff --git a/core/lib/Drupal/Core/Mail/PhpMail.php b/core/lib/Drupal/Core/Mail/PhpMail.php
index 580a824..448d76f 100644
--- a/core/lib/Drupal/Core/Mail/PhpMail.php
+++ b/core/lib/Drupal/Core/Mail/PhpMail.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\Mail;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * The default Drupal mail backend using PHP's mail function.
  */
@@ -57,11 +59,11 @@ public function mail(array $message) {
     }
     $mimeheaders = array();
     foreach ($message['headers'] as $name => $value) {
-      $mimeheaders[] = $name . ': ' . mime_header_encode($value);
+      $mimeheaders[] = $name . ': ' . Unicode::MIMEHeaderEncode($value);
     }
     $line_endings = settings()->get('mail_line_endings', MAIL_LINE_ENDINGS);
     // Prepare mail commands.
-    $mail_subject = mime_header_encode($message['subject']);
+    $mail_subject = Unicode::MIMEHeaderEncode($message['subject']);
     // Note: e-mail uses CRLF for line-endings. PHP's API requires LF
     // on Unix and CRLF on Windows. Drupal automatically guesses the
     // line-ending format appropriate for your system. If you need to
diff --git a/core/lib/Drupal/Core/TypedData/ItemList.php b/core/lib/Drupal/Core/TypedData/ItemList.php
index 3c6d01a..8b79770 100644
--- a/core/lib/Drupal/Core/TypedData/ItemList.php
+++ b/core/lib/Drupal/Core/TypedData/ItemList.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\TypedData;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * A generic list class.
  *
@@ -80,7 +82,7 @@ public function getString() {
         $strings[] = $item->getString();
       }
       // Remove any empty strings resulting from empty items.
-      return implode(', ', array_filter($strings, 'drupal_strlen'));
+      return implode(', ', array_filter($strings, 'Unicode::strlen'));
     }
   }
 
diff --git a/core/lib/Drupal/Core/TypedData/Type/Map.php b/core/lib/Drupal/Core/TypedData/Type/Map.php
index 01598d0..d6b2118 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Map.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Map.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\TypedData\ContextAwareTypedData;
 use Drupal\Core\TypedData\ComplexDataInterface;
 
@@ -79,7 +80,7 @@ public function getString() {
       $strings[] = $property->getString();
     }
     // Remove any empty strings resulting from empty items.
-    return implode(', ', array_filter($strings, 'drupal_strlen'));
+    return implode(', ', array_filter($strings, 'Unicode::strlen'));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Updater/Updater.php b/core/lib/Drupal/Core/Updater/Updater.php
index cdd4e4e..c9fc720 100644
--- a/core/lib/Drupal/Core/Updater/Updater.php
+++ b/core/lib/Drupal/Core/Updater/Updater.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Updater;
 
 use Drupal\Core\FileTransfer\FileTransferException;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Defines the base class for Updaters used in Drupal.
@@ -99,7 +100,7 @@ public static function findInfoFile($directory) {
       return FALSE;
     }
     foreach ($info_files as $info_file) {
-      if (drupal_substr($info_file->filename, 0, -5) == drupal_basename($directory)) {
+      if (Unicode::substr($info_file->filename, 0, -5) == drupal_basename($directory)) {
         // Info file Has the same name as the directory, return it.
         return $info_file->uri;
       }
diff --git a/core/lib/Drupal/Core/Utility/Color.php b/core/lib/Drupal/Core/Utility/Color.php
index 27ba440..141d136 100644
--- a/core/lib/Drupal/Core/Utility/Color.php
+++ b/core/lib/Drupal/Core/Utility/Color.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\Utility;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Performs color conversions.
  */
@@ -28,7 +30,7 @@ public static function validateHex($hex) {
     // Hash prefix is optional.
     $hex = ltrim($hex, '#');
     // Must be either RGB or RRGGBB.
-    $length = drupal_strlen($hex);
+    $length = Unicode::strlen($hex);
     $valid = $valid && ($length === 3 || $length === 6);
     // Must be a valid hex value.
     $valid = $valid && ctype_xdigit($hex);
diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc
index 632f1ec..1c71b2e 100644
--- a/core/modules/aggregator/aggregator.admin.inc
+++ b/core/modules/aggregator/aggregator.admin.inc
@@ -5,6 +5,7 @@
  * Administration page callbacks for the Aggregator module.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\aggregator\Plugin\Core\Entity\Feed;
 
@@ -286,7 +287,7 @@ function aggregator_form_opml_submit($form, &$form_state) {
  */
 function _aggregator_parse_opml($opml) {
   $feeds = array();
-  $xml_parser = drupal_xml_parser_create($opml);
+  $xml_parser = Unicode::createXMLParser($opml);
   if (xml_parse_into_struct($xml_parser, $opml, $values)) {
     foreach ($values as $entry) {
       if ($entry['tag'] == 'OUTLINE' && isset($entry['attributes'])) {
diff --git a/core/modules/aggregator/aggregator.parser.inc b/core/modules/aggregator/aggregator.parser.inc
index 3b906e8..2a2e0ab 100644
--- a/core/modules/aggregator/aggregator.parser.inc
+++ b/core/modules/aggregator/aggregator.parser.inc
@@ -5,6 +5,7 @@
  * Parser functions for the aggregator module.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\aggregator\Plugin\Core\Entity\Feed;
 
 /**
@@ -71,7 +72,7 @@ function aggregator_parse_feed(&$data, Feed $feed) {
   $channel = array();
 
   // Parse the data.
-  $xml_parser = drupal_xml_parser_create($data);
+  $xml_parser = Unicode::createXMLParser($data);
   xml_set_element_handler($xml_parser, 'aggregator_element_start', 'aggregator_element_end');
   xml_set_character_data_handler($xml_parser, 'aggregator_element_data');
 
@@ -102,7 +103,7 @@ function aggregator_parse_feed(&$data, Feed $feed) {
       $item['title'] = $item['title'];
     }
     elseif (!empty($item['description'])) {
-      $item['title'] = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($item['description'], 40));
+      $item['title'] = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", Unicode::truncateUTF8($item['description'], 40));
     }
     else {
       $item['title'] = '';
diff --git a/core/modules/aggregator/aggregator.processor.inc b/core/modules/aggregator/aggregator.processor.inc
index 10b52f1..c5bc48f 100644
--- a/core/modules/aggregator/aggregator.processor.inc
+++ b/core/modules/aggregator/aggregator.processor.inc
@@ -5,6 +5,7 @@
  * Processor functions for the aggregator module.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\aggregator\Plugin\Core\Entity\Feed;
 
 /**
@@ -56,8 +57,8 @@ function aggregator_aggregator_process($feed) {
         }
 
         // Make sure the item title and author fit in the 255 varchar column.
-        $entry->title->value = truncate_utf8($item['title'], 255, TRUE, TRUE);
-        $entry->author->value = truncate_utf8($item['author'], 255, TRUE, TRUE);
+        $entry->title->value = Unicode::truncateUTF8($item['title'], 255, TRUE, TRUE);
+        $entry->author->value = Unicode::truncateUTF8($item['author'], 255, TRUE, TRUE);
 
         $entry->fid->value = $feed->id();
         $entry->link->value = $item['link'];
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTranslationUITest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTranslationUITest.php
index 3ccac91..d6adab2 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTranslationUITest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockTranslationUITest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\custom_block\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\translation_entity\Tests\EntityTranslationUITest;
 use Drupal\custom_block\Plugin\Core\Entity\CustomBlock;
 
@@ -50,7 +51,7 @@ public static function getInfo() {
   public function setUp() {
     $this->entityType = 'custom_block';
     $this->bundle = 'basic';
-    $this->name = drupal_strtolower($this->randomName());
+    $this->name = Unicode::strtolower($this->randomName());
     $this->testLanguageSelector = FALSE;
     parent::setUp();
   }
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/PageEditTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/PageEditTest.php
index 3ba7b0b..5a17b21 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/PageEditTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/PageEditTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\custom_block\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests the block edit functionality.
  */
@@ -34,7 +36,7 @@ public function testPageEdit() {
     $body_key = "block_body[$langcode][0][value]";
     // Create block to edit.
     $edit = array();
-    $edit['info'] = drupal_strtolower($this->randomName(8));
+    $edit['info'] = Unicode::strtolower($this->randomName(8));
     $edit[$body_key] = $this->randomName(16);
     $this->drupalPost('block/add/basic', $edit, t('Save'));
 
diff --git a/core/modules/block/lib/Drupal/block/BlockBase.php b/core/modules/block/lib/Drupal/block/BlockBase.php
index 9c90a5a..615c56c 100644
--- a/core/modules/block/lib/Drupal/block/BlockBase.php
+++ b/core/modules/block/lib/Drupal/block/BlockBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\block;
 
 use Drupal\Component\Plugin\PluginBase;
+use Drupal\Component\Utility\Unicode;
 use Drupal\block\Plugin\Core\Entity\Block;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 
@@ -169,11 +170,11 @@ public function access() {
 
       // Convert path to lowercase. This allows comparison of the same path
       // with different case. Ex: /Page, /page, /PAGE.
-      $pages = drupal_strtolower($visibility['path']['pages']);
+      $pages = Unicode::strtolower($visibility['path']['pages']);
       if ($visibility['path']['visibility'] < BLOCK_VISIBILITY_PHP) {
         // Compare the lowercase path alias (if any) and internal path.
         $path = current_path();
-        $path_alias = drupal_strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path));
+        $path_alias = Unicode::strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path));
         $page_match = drupal_match_path($path_alias, $pages) || (($path != $path_alias) && drupal_match_path($path, $pages));
         // When $block->visibility has a value of 0
         // (BLOCK_VISIBILITY_NOTLISTED), the block is displayed on all pages
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index c316278..e7d4ea7 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -5,6 +5,7 @@
  * Allows users to create and organize related content in an outline.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
 use Drupal\Core\Template\Attribute;
@@ -1084,7 +1085,7 @@ function _book_toc_recurse($tree, $indent, &$toc, $exclude, $depth_limit) {
     }
 
     if (!in_array($data['link']['mlid'], $exclude)) {
-      $toc[$data['link']['mlid']] = $indent . ' ' . truncate_utf8($data['link']['title'], 30, TRUE, TRUE);
+      $toc[$data['link']['mlid']] = $indent . ' ' . Unicode::truncateUTF8($data['link']['title'], 30, TRUE, TRUE);
       if ($data['below']) {
         _book_toc_recurse($data['below'], $indent . '--', $toc, $exclude, $depth_limit);
       }
diff --git a/core/modules/book/book.pages.inc b/core/modules/book/book.pages.inc
index a971aa6..bb9cae5 100644
--- a/core/modules/book/book.pages.inc
+++ b/core/modules/book/book.pages.inc
@@ -5,6 +5,7 @@
  * User page callbacks for the book module.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\node\Plugin\Core\Entity\Node;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -51,7 +52,7 @@ function book_render() {
  * @see book_menu()
  */
 function book_export($type, Node $node) {
-  $type = drupal_strtolower($type);
+  $type = Unicode::strtolower($type);
 
   $export_function = 'book_export_' . $type;
 
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
index f65b559..ca0e735 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
@@ -6,6 +6,7 @@
 
 namespace Drupal\breakpoint\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\breakpoint\Tests\BreakpointsTestBase;
 use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 use Drupal\breakpoint\InvalidBreakpointNameException;
@@ -31,7 +32,7 @@ public static function getInfo() {
   public function testConfigName() {
     // Try an invalid sourceType.
     $breakpoint = entity_create('breakpoint', array(
-      'label' => drupal_strtolower($this->randomName()),
+      'label' => Unicode::strtolower($this->randomName()),
       'source' => 'custom_module',
       'sourceType' => 'oops',
     ));
@@ -62,7 +63,7 @@ public function testConfigName() {
     // Try an invalid name (make sure there is at least once capital letter).
     $breakpoint->id = '';
     $breakpoint->source = 'custom_module';
-    $breakpoint->name = drupal_ucfirst($this->randomName());
+    $breakpoint->name = Unicode::ucfirst($this->randomName());
 
     $exception = FALSE;
     try {
@@ -75,7 +76,7 @@ public function testConfigName() {
 
     // Try a valid breakpoint.
     $breakpoint->id = '';
-    $breakpoint->name = drupal_strtolower($this->randomName());
+    $breakpoint->name = Unicode::strtolower($this->randomName());
     $breakpoint->mediaQuery = 'all';
 
     $exception = FALSE;
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
index 37caff7..bffaf4e 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
@@ -8,6 +8,7 @@
 
 use Drupal\breakpoint\Tests\BreakpointTestBase;
 use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Tests for breakpoint CRUD operations.
@@ -28,7 +29,7 @@ public static function getInfo() {
   public function testBreakpointCRUD() {
     // Add a breakpoint with minimum data only.
     $breakpoint = entity_create('breakpoint', array(
-      'label' => drupal_strtolower($this->randomName()),
+      'label' => Unicode::strtolower($this->randomName()),
       'mediaQuery' => '(min-width: 600px)',
     ));
     $breakpoint->save();
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
index d2bc016..486c2d9 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
@@ -12,6 +12,7 @@
 use Drupal\breakpoint\InvalidBreakpointNameException;
 use Drupal\breakpoint\InvalidBreakpointSourceException;
 use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Tests for general breakpoint group API functions.
@@ -34,7 +35,7 @@ public function testConfigName() {
     $label = $this->randomName();
     $breakpoint_group = entity_create('breakpoint_group', array(
       'label' => $label,
-      'name' => drupal_strtolower($label),
+      'name' => Unicode::strtolower($label),
       'source' => 'custom_module',
       'sourceType' => 'oops',
     ));
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php
index 05eaeaf..0627a82 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php
@@ -9,6 +9,7 @@
 use Drupal\breakpoint\Tests\BreakpointGroupTestBase;
 use Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup;
 use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Tests for breakpoint group CRUD operations.
@@ -32,7 +33,7 @@ public function testBreakpointGroupCRUD() {
     for ($i = 0; $i <= 3; $i++) {
       $width = ($i + 1) * 200;
       $breakpoint = entity_create('breakpoint', array(
-        'name' => drupal_strtolower($this->randomName()),
+        'name' => Unicode::strtolower($this->randomName()),
         'weight' => $i,
         'mediaQuery' => "(min-width: {$width}px)",
       ));
@@ -44,7 +45,7 @@ public function testBreakpointGroupCRUD() {
 
     $group = entity_create('breakpoint_group', array(
       'label' => $label,
-      'name' => drupal_strtolower($label),
+      'name' => Unicode::strtolower($label),
     ));
     $group->save();
     $this->verifyBreakpointGroup($group);
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 2203133..38bd975 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -4,6 +4,8 @@
  * Allows users to change the color scheme of themes.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Implements hook_help().
  */
@@ -439,7 +441,7 @@ function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) {
   // Prepare color conversion table.
   $conversion = $palette;
   foreach ($conversion as $k => $v) {
-    $conversion[$k] = drupal_strtolower($v);
+    $conversion[$k] = Unicode::strtolower($v);
   }
   $default = color_get_palette($theme, TRUE);
 
@@ -458,7 +460,7 @@ function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) {
   // Iterate over all the parts.
   foreach ($style as $chunk) {
     if ($is_color) {
-      $chunk = drupal_strtolower($chunk);
+      $chunk = Unicode::strtolower($chunk);
       // Check if this is one of the colors in the default palette.
       if ($key = array_search($chunk, $default)) {
         $chunk = $conversion[$key];
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index 0418b1d..75d563a 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\comment\Plugin\Core\Entity\Comment;
+use Drupal\Component\Utility\Unicode;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -116,7 +117,7 @@ function comment_admin_overview($form, &$form_state, $arg) {
           '#type' => 'link',
           '#title' => $comment->subject->value,
           '#href' => 'comment/' . $comment->id(),
-          '#options' => array('attributes' => array('title' => truncate_utf8($comment->comment_body->value, 128)), 'fragment' => 'comment-' . $comment->id()),
+          '#options' => array('attributes' => array('title' => Unicode::truncateUTF8($comment->comment_body->value, 128)), 'fragment' => 'comment-' . $comment->id()),
         ),
       ),
       'author' => theme('username', array('account' => comment_prepare_author($comment))),
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index a5e5f15..1a4120e 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\comment;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityFormControllerNG;
@@ -279,7 +280,7 @@ public function submit(array $form, array &$form_state) {
       // 2) Strip out all HTML tags
       // 3) Convert entities back to plain-text.
       $comment_text = $comment->comment_body->processed;
-      $comment->subject = truncate_utf8(trim(decode_entities(strip_tags($comment_text))), 29, TRUE);
+      $comment->subject = Unicode::truncateUTF8(trim(Unicode::decodeEntities(strip_tags($comment_text))), 29, TRUE);
       // Edge cases where the comment body is populated only by HTML tags will
       // require a default subject.
       if ($comment->subject->value == '') {
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php b/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
index bdc98ef..7b2b854 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\contact\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -79,7 +80,7 @@ function testSiteWideContact() {
 
     // Create first valid category.
     $recipients = array('simpletest@example.com', 'simpletest2@example.com', 'simpletest3@example.com');
-    $this->addCategory($id = drupal_strtolower($this->randomName(16)), $label = $this->randomName(16), implode(',', array($recipients[0])), '', TRUE);
+    $this->addCategory($id = Unicode::strtolower($this->randomName(16)), $label = $this->randomName(16), implode(',', array($recipients[0])), '', TRUE);
     $this->assertRaw(t('Category %label has been added.', array('%label' => $label)));
 
     // Make sure the newly created category is included in the list of categories.
@@ -106,10 +107,10 @@ function testSiteWideContact() {
     $this->drupalLogin($admin_user);
 
     // Add more categories.
-    $this->addCategory(drupal_strtolower($this->randomName(16)), $label = $this->randomName(16), implode(',', array($recipients[0], $recipients[1])), '', FALSE);
+    $this->addCategory(Unicode::strtolower($this->randomName(16)), $label = $this->randomName(16), implode(',', array($recipients[0], $recipients[1])), '', FALSE);
     $this->assertRaw(t('Category %label has been added.', array('%label' => $label)));
 
-    $this->addCategory($name = drupal_strtolower($this->randomName(16)), $label = $this->randomName(16), implode(',', array($recipients[0], $recipients[1], $recipients[2])), '', FALSE);
+    $this->addCategory($name = Unicode::strtolower($this->randomName(16)), $label = $this->randomName(16), implode(',', array($recipients[0], $recipients[1], $recipients[2])), '', FALSE);
     $this->assertRaw(t('Category %label has been added.', array('%label' => $label)));
 
     // Try adding a category that already exists.
diff --git a/core/modules/contextual/lib/Drupal/contextual/Plugin/views/field/ContextualLinks.php b/core/modules/contextual/lib/Drupal/contextual/Plugin/views/field/ContextualLinks.php
index 5ef7df0..9401894 100644
--- a/core/modules/contextual/lib/Drupal/contextual/Plugin/views/field/ContextualLinks.php
+++ b/core/modules/contextual/lib/Drupal/contextual/Plugin/views/field/ContextualLinks.php
@@ -8,6 +8,7 @@
 namespace Drupal\contextual\Plugin\views\field;
 
 use Drupal\Component\Annotation\Plugin;
+use Drupal\Component\Utility\Unicode;
 use Drupal\views\Plugin\views\field\FieldPluginBase;
 
 /**
@@ -83,7 +84,7 @@ function render($values) {
       if (!empty($title) && !empty($path)) {
         // Make sure that tokens are replaced for this paths as well.
         $tokens = $this->get_render_tokens(array());
-        $path = strip_tags(decode_entities(strtr($path, $tokens)));
+        $path = strip_tags(Unicode::decodeEntities(strtr($path, $tokens)));
 
         $links[$field] = array(
           'href' => $path,
diff --git a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php
index 232d019..5dcaf7b 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeFieldTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\datetime\Tests;
 
 use Drupal\simpletest\WebTestBase;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Datetime\DrupalDateTime;
 
 /**
@@ -49,7 +50,7 @@ function setUp() {
 
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'datetime',
       'settings' => array('datetime_type' => 'date'),
     );
diff --git a/core/modules/dblog/dblog.admin.inc b/core/modules/dblog/dblog.admin.inc
index 5988866..abd2e55 100644
--- a/core/modules/dblog/dblog.admin.inc
+++ b/core/modules/dblog/dblog.admin.inc
@@ -5,6 +5,8 @@
  * Administrative page callbacks for the Database Logging module.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Page callback: Displays a listing of database log messages.
  *
@@ -298,7 +300,7 @@ function theme_dblog_message($variables) {
     }
     if ($variables['link'] && isset($event->wid)) {
       // Truncate message to 56 chars.
-      $output = truncate_utf8(filter_xss($output, array()), 56, TRUE, TRUE);
+      $output = Unicode::truncateUTF8(filter_xss($output, array()), 56, TRUE, TRUE);
       $output = l($output, 'admin/reports/event/' . $event->wid, array('html' => TRUE));
     }
   }
diff --git a/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php b/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php
index 1769605..897cf0e 100644
--- a/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php
+++ b/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\dblog\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 use SimpleXMLElement;
 
@@ -264,7 +265,7 @@ private function doUser() {
     $this->assertLogMessage(t('Session closed for %name.', array('%name' => $name)), 'DBLog event was recorded: [logout user]');
     // Delete user.
     $message = t('Deleted user: %name %email.', array('%name' => $name, '%email' => '<' . $user->mail . '>'));
-    $message_text = truncate_utf8(filter_xss($message, array()), 56, TRUE, TRUE);
+    $message_text = Unicode::truncateUTF8(filter_xss($message, array()), 56, TRUE, TRUE);
     // Verify that the full message displays on the details page.
     $link = FALSE;
     if ($links = $this->xpath('//a[text()="' . html_entity_decode($message_text) . '"]')) {
@@ -273,7 +274,7 @@ private function doUser() {
       foreach ($links->attributes() as $attr => $value) {
         if ($attr == 'href') {
           // Extract link to details page.
-          $link = drupal_substr($value, strpos($value, 'admin/reports/event/'));
+          $link = Unicode::substr($value, strpos($value, 'admin/reports/event/'));
           $this->drupalGet($link);
           // Check for full message text on the details page.
           $this->assertRaw($message, 'DBLog event details was found: [delete user]');
@@ -626,7 +627,7 @@ protected function asText(SimpleXMLElement $element) {
    *   The message to pass to simpletest.
    */
   protected function assertLogMessage($log_message, $message) {
-    $message_text = truncate_utf8(filter_xss($log_message, array()), 56, TRUE, TRUE);
+    $message_text = Unicode::truncateUTF8(filter_xss($log_message, array()), 56, TRUE, TRUE);
     // After filter_xss(), HTML entities should be converted to their character
     // equivalents because assertLink() uses this string in xpath() to query the
     // Document Object Model (DOM).
diff --git a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
index c719c97..1d05191 100644
--- a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
+++ b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\email\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -46,7 +47,7 @@ function setUp() {
   function testEmailField() {
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'email',
     );
     field_create_field($this->field);
diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module
index f5e968c..018d67c 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -7,6 +7,7 @@
 
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Query\AlterableInterface;
 use Drupal\Core\Entity\EntityInterface;
 
@@ -533,7 +534,7 @@ function entity_reference_autocomplete_callback($type, $field_name, $entity_type
   // Get the typed string, if exists from the URL.
   $tags_typed = drupal_container()->get('request')->query->get('q');
   $tags_typed = drupal_explode_tags($tags_typed);
-  $string = drupal_strtolower(array_pop($tags_typed));
+  $string = Unicode::strtolower(array_pop($tags_typed));
 
   // The user entered a comma-separated list of entity labels, so we generate a
   // prefix.
@@ -594,7 +595,7 @@ function entity_reference_autocomplete_callback_get_matches($field, $instance, $
         $key = "$label ($entity_id)";
         // Strip things like starting/trailing white spaces, line breaks and
         // tags.
-        $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
+        $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(Unicode::decodeEntities(strip_tags($key)))));
         // Names containing commas or quotes must be wrapped in quotes.
         if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
           $key = '"' . str_replace('"', '""', $key) . '"';
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 16b0143..4bf97a2 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\field\FieldUpdateForbiddenException;
 
 /**
@@ -393,7 +394,7 @@ function hook_field_prepare_view($entity_type, $entities, $field, $instances, $l
 function hook_field_validate(\Drupal\Core\Entity\EntityInterface $entity = NULL, $field, $instance, $langcode, $items, &$errors) {
   foreach ($items as $delta => $item) {
     if (!empty($item['value'])) {
-      if (!empty($field['settings']['max_length']) && drupal_strlen($item['value']) > $field['settings']['max_length']) {
+      if (!empty($field['settings']['max_length']) && Unicode::strlen($item['value']) > $field['settings']['max_length']) {
         $errors[$field['field_name']][$langcode][$delta][] = array(
           'error' => 'text_max_length',
           'message' => t('%name: the value may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length'])),
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index f6ee8a2..fb5980b 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -5,6 +5,7 @@
  * Field CRUD API, handling field and field instance creation and deletion.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\field\FieldException;
 
@@ -69,10 +70,10 @@ function field_create_field($field) {
     throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
   }
 
-  // Field name cannot be longer than 32 characters. We use drupal_strlen()
+  // Field name cannot be longer than 32 characters. We use Unicode::strlen()
   // because the DB layer assumes that column widths are given in characters,
   // not bytes.
-  if (drupal_strlen($field['field_name']) > 32) {
+  if (Unicode::strlen($field['field_name']) > 32) {
     throw new FieldException(t('Attempt to create a field with a name longer than 32 characters: %name',
       array('%name' => $field['field_name'])));
   }
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
index 5ae3610..c24d857 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\field\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Unit test class for storage-related field_attach_* functions.
  *
@@ -433,7 +435,7 @@ function testFieldAttachDelete() {
    */
   function testFieldAttachCreateRenameBundle() {
     // Create a new bundle.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+    $new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomName());
     field_test_create_bundle($new_bundle);
 
     // Add an instance to that bundle.
@@ -454,7 +456,7 @@ function testFieldAttachCreateRenameBundle() {
     $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
 
     // Rename the bundle.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+    $new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomName());
     field_test_rename_bundle($this->instance['bundle'], $new_bundle);
 
     // Check that the instance definition has been updated.
@@ -472,7 +474,7 @@ function testFieldAttachCreateRenameBundle() {
    */
   function testFieldAttachDeleteBundle() {
     // Create a new bundle.
-    $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
+    $new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomName());
     field_test_create_bundle($new_bundle);
 
     // Add an instance to that bundle.
@@ -480,7 +482,7 @@ function testFieldAttachDeleteBundle() {
     field_create_instance($this->instance);
 
     // Create a second field for the test bundle
-    $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field_name = Unicode::strtolower($this->randomName() . '_field_name');
     $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
     field_create_field($field);
     $instance = array(
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachTestBase.php
index 9d15ba2..f8e655e 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachTestBase.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\field\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 abstract class FieldAttachTestBase extends FieldTestBase {
 
   /**
@@ -35,7 +37,7 @@ function createFieldWithInstance($suffix = '') {
     $field_id = 'field_id' . $suffix;
     $instance = 'instance' . $suffix;
 
-    $this->$field_name = drupal_strtolower($this->randomName() . '_field_name' . $suffix);
+    $this->$field_name = Unicode::strtolower($this->randomName() . '_field_name' . $suffix);
     $this->$field = array('field_name' => $this->$field_name, 'type' => 'test_field', 'cardinality' => 4);
     $this->$field = field_create_field($this->$field);
     $this->$field_id = $this->{$field}['id'];
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 3115851..53feeb6 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\field\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 class FieldInfoTest extends FieldTestBase {
 
   /**
@@ -62,7 +64,7 @@ function testFieldInfo() {
     // Create a field, verify it shows up.
     $core_fields = field_info_fields();
     $field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'test_field',
     );
     field_create_field($field);
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
index 90fcc05..d9f64c0 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\field\FieldException;
 
 class FieldInstanceCrudTest extends FieldTestBase {
@@ -32,7 +33,7 @@ function setUp() {
     parent::setUp();
 
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'test_field',
     );
     field_create_field($this->field);
@@ -97,7 +98,7 @@ function testCreateFieldInstance() {
 
     // Create a field restricted to a specific entity type.
     $field_restricted = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'test_field',
       'entity_types' => array('test_cacheable_entity'),
     );
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
index f2ada97..df22141 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
 
 /**
@@ -35,7 +36,7 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $this->field_name = Unicode::strtolower($this->randomName() . '_field_name');
 
     $this->entity_type = 'test_entity';
 
@@ -244,7 +245,7 @@ function testTranslatableFieldSaveLoad() {
     }
 
     // Test default values.
-    $field_name_default = drupal_strtolower($this->randomName() . '_field_name');
+    $field_name_default = Unicode::strtolower($this->randomName() . '_field_name');
     $field = $this->field;
     $field['field_name'] = $field_name_default;
     $instance = $this->instance;
@@ -294,7 +295,7 @@ function testTranslatableFieldSaveLoad() {
    * Tests display language logic for translatable fields.
    */
   function testFieldDisplayLanguage() {
-    $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field_name = Unicode::strtolower($this->randomName() . '_field_name');
     $entity_type = 'test_entity';
 
     // We need an additional field here to properly test display language
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
index 925caef..11e0d3e 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field_ui;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\field_ui\OverviewBase;
 
 /**
@@ -317,7 +318,7 @@ public function buildForm(array $form, array &$form_state) {
           '@label' => $info['label'],
           '@field' => $info['field'],
         ));
-        $existing_field_options[$field_name] = truncate_utf8($text, 80, FALSE, TRUE);
+        $existing_field_options[$field_name] = Unicode::truncateUTF8($text, 80, FALSE, TRUE);
       }
       asort($existing_field_options);
       $name = '_add_existing_field';
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUiTestBase.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUiTestBase.php
index 2d3fb98..ddeedd9 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUiTestBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUiTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field_ui\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -37,7 +38,7 @@ function setUp() {
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
       'description' => $this->randomName(),
-      'vid' => drupal_strtolower($this->randomName()),
+      'vid' => Unicode::strtolower($this->randomName()),
       'langcode' => LANGUAGE_NOT_SPECIFIED,
       'help' => '',
       'nodes' => array('article' => 'article'),
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 69fcbf3..d5c62bf 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -7,6 +7,7 @@
 
 use Drupal\file\Plugin\Core\Entity\File;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Template\Attribute;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Drupal\file\FileUsage\DatabaseFileUsageBackend;
@@ -573,8 +574,8 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
  *   \Symfony\Component\HttpFoundation\StreamedResponse.
  */
 function file_get_content_headers(File $file) {
-  $name = mime_header_encode($file->filename);
-  $type = mime_header_encode($file->filemime);
+  $name = Unicode::MIMEHeaderEncode($file->filename);
+  $type = Unicode::MIMEHeaderEncode($file->filemime);
 
   return array(
     'Content-Type' => $type,
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 725d931..ccaf984 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -5,6 +5,7 @@
  * Framework for handling the filtering of content.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\filter\Plugin\Core\Entity\FilterFormat;
@@ -1572,7 +1573,7 @@ function _filter_url_parse_full_links($match) {
   // The $i:th parenthesis in the regexp contains the URL.
   $i = 1;
 
-  $match[$i] = decode_entities($match[$i]);
+  $match[$i] = Unicode::decodeEntities($match[$i]);
   $caption = check_plain(_filter_url_trim($match[$i]));
   $match[$i] = check_plain($match[$i]);
   return '<a href="' . $match[$i] . '">' . $caption . '</a>' . $match[$i + 1];
@@ -1587,7 +1588,7 @@ function _filter_url_parse_email_links($match) {
   // The $i:th parenthesis in the regexp contains the URL.
   $i = 0;
 
-  $match[$i] = decode_entities($match[$i]);
+  $match[$i] = Unicode::decodeEntities($match[$i]);
   $caption = check_plain(_filter_url_trim($match[$i]));
   $match[$i] = check_plain($match[$i]);
   return '<a href="mailto:' . $match[$i] . '">' . $caption . '</a>';
@@ -1602,7 +1603,7 @@ function _filter_url_parse_partial_links($match) {
   // The $i:th parenthesis in the regexp contains the URL.
   $i = 1;
 
-  $match[$i] = decode_entities($match[$i]);
+  $match[$i] = Unicode::decodeEntities($match[$i]);
   $caption = check_plain(_filter_url_trim($match[$i]));
   $match[$i] = check_plain($match[$i]);
   return '<a href="http://' . $match[$i] . '">' . $caption . '</a>' . $match[$i + 1];
@@ -1784,7 +1785,7 @@ function _filter_html_escape_tips($filter, $format, $long = FALSE) {
 function _filter_html_image_secure_process($text) {
   // Find the path (e.g. '/') to Drupal root.
   $base_path = base_path();
-  $base_path_length = drupal_strlen($base_path);
+  $base_path_length = Unicode::strlen($base_path);
 
   // Find the directory on the server where index.php resides.
   $local_dir = DRUPAL_ROOT . '/';
@@ -1800,11 +1801,11 @@ function _filter_html_image_secure_process($text) {
     // Verify that $src starts with $base_path.
     // This also ensures that external images cannot be referenced.
     $src = $image->getAttribute('src');
-    if (drupal_substr($src, 0, $base_path_length) === $base_path) {
+    if (Unicode::substr($src, 0, $base_path_length) === $base_path) {
       // Remove the $base_path to get the path relative to the Drupal root.
       // Ensure the path refers to an actual image by prefixing the image source
       // with the Drupal root and running getimagesize() on it.
-      $local_image_path = $local_dir . drupal_substr($src, $base_path_length);
+      $local_image_path = $local_dir . Unicode::substr($src, $base_path_length);
       if (@getimagesize($local_image_path)) {
         // The image has the right path. Erroneous images are dealt with below.
         continue;
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
index 4d28fa6..403cdb2 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -52,7 +53,7 @@ function testFormatAdmin() {
     // Add text format.
     $this->drupalGet('admin/config/content/formats');
     $this->clickLink('Add text format');
-    $format_id = drupal_strtolower($this->randomName());
+    $format_id = Unicode::strtolower($this->randomName());
     $name = $this->randomName();
     $edit = array(
       'format' => $format_id,
@@ -178,7 +179,7 @@ function testFilterAdmin() {
 
     // Add format.
     $edit = array();
-    $edit['format'] = drupal_strtolower($this->randomName());
+    $edit['format'] = Unicode::strtolower($this->randomName());
     $edit['name'] = $this->randomName();
     $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = 1;
     $edit['filters[' . $second_filter . '][status]'] = TRUE;
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
index 5df2a5e..dd8f683 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -32,7 +33,7 @@ function testDefaultTextFormats() {
     $formats = array();
     for ($i = 0; $i < 2; $i++) {
       $edit = array(
-        'format' => drupal_strtolower($this->randomName()),
+        'format' => Unicode::strtolower($this->randomName()),
         'name' => $this->randomName(),
       );
       $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php
index 969fac0..31ea2da 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -82,7 +83,7 @@ function setUp() {
     $formats = array();
     for ($i = 0; $i < 3; $i++) {
       $edit = array(
-        'format' => drupal_strtolower($this->randomName()),
+        'format' => Unicode::strtolower($this->randomName()),
         'name' => $this->randomName(),
       );
       $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterHooksTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterHooksTest.php
index 442eff4..b6fa7f5 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterHooksTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterHooksTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -47,7 +48,7 @@ function testFilterHooks() {
     // Add a text format.
     $name = $this->randomName();
     $edit = array();
-    $edit['format'] = drupal_strtolower($this->randomName());
+    $edit['format'] = Unicode::strtolower($this->randomName());
     $edit['name'] = $name;
     $edit['roles[' . DRUPAL_ANONYMOUS_RID . ']'] = 1;
     $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php
index de9f194..ddee823 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\DrupalUnitTestBase;
 use stdClass;
 
@@ -988,7 +989,7 @@ function testHtmlCorrectorFilter() {
    *   TRUE on pass, FALSE on fail.
    */
   function assertNormalized($haystack, $needle, $message = '', $group = 'Other') {
-    return $this->assertTrue(strpos(strtolower(decode_entities($haystack)), $needle) !== FALSE, $message, $group);
+    return $this->assertTrue(strpos(strtolower(Unicode::decodeEntities($haystack)), $needle) !== FALSE, $message, $group);
   }
 
   /**
@@ -1012,6 +1013,6 @@ function assertNormalized($haystack, $needle, $message = '', $group = 'Other') {
    *   TRUE on pass, FALSE on fail.
    */
   function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') {
-    return $this->assertTrue(strpos(strtolower(decode_entities($haystack)), $needle) === FALSE, $message, $group);
+    return $this->assertTrue(strpos(strtolower(Unicode::decodeEntities($haystack)), $needle) === FALSE, $message, $group);
   }
 }
diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc
index 11152ae..f9df9c1 100644
--- a/core/modules/image/image.field.inc
+++ b/core/modules/image/image.field.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -461,7 +462,7 @@ function theme_image_formatter($variables) {
   $item = $variables['item'];
 
   // Do not output an empty 'title' attribute.
-  if (isset($item['title']) && drupal_strlen($item['title']) == 0) {
+  if (isset($item['title']) && Unicode::strlen($item['title']) == 0) {
     unset($item['title']);
   }
 
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index a3500fd..2319fe3 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -5,6 +5,7 @@
  * Administration functions for language.module.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
@@ -424,7 +425,7 @@ function language_negotiation_configure_form_table(&$form, $type) {
 
       $table_form['weight'][$method_id] = array(
         '#type' => 'weight',
-        '#title' => t('Weight for !title language detection method', array('!title' => drupal_strtolower($method_name))),
+        '#title' => t('Weight for !title language detection method', array('!title' => Unicode::strtolower($method_name))),
         '#title_display' => 'invisible',
         '#default_value' => $weight,
         '#attributes' => array('class' => array("language-method-weight-$type")),
@@ -435,7 +436,7 @@ function language_negotiation_configure_form_table(&$form, $type) {
 
       $table_form['enabled'][$method_id] = array(
         '#type' => 'checkbox',
-        '#title' => t('Enable !title language detection method', array('!title' => drupal_strtolower($method_name))),
+        '#title' => t('Enable !title language detection method', array('!title' => Unicode::strtolower($method_name))),
         '#title_display' => 'invisible',
         '#default_value' => $enabled,
       );
diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
index a608873..9f0ddb5 100644
--- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
+++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\link\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -45,7 +46,7 @@ function setUp() {
   function testURLValidation() {
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'link',
     );
     field_create_field($this->field);
@@ -113,7 +114,7 @@ function testURLValidation() {
   function testLinkTitle() {
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'link',
     );
     field_create_field($this->field);
@@ -222,7 +223,7 @@ function testLinkTitle() {
   function testLinkFormatter() {
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'link',
       'cardinality' => 2,
     );
@@ -305,11 +306,11 @@ function testLinkFormatter() {
         switch ($setting) {
           case 'trim_length':
             $url = $url1;
-            $title = isset($new_value) ? truncate_utf8($title1, $new_value, FALSE, TRUE) : $title1;
+            $title = isset($new_value) ? Unicode::truncateUTF8($title1, $new_value, FALSE, TRUE) : $title1;
             $this->assertRaw('<a href="' . check_plain($url) . '">' . check_plain($title) . '</a>');
 
             $url = $url2;
-            $title = isset($new_value) ? truncate_utf8($title2, $new_value, FALSE, TRUE) : $title2;
+            $title = isset($new_value) ? Unicode::truncateUTF8($title2, $new_value, FALSE, TRUE) : $title2;
             $this->assertRaw('<a href="' . check_plain($url) . '">' . check_plain($title) . '</a>');
             break;
 
@@ -358,7 +359,7 @@ function testLinkFormatter() {
   function testLinkSeparateFormatter() {
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'link',
       'cardinality' => 2,
     );
@@ -423,15 +424,15 @@ function testLinkSeparateFormatter() {
         switch ($setting) {
           case 'trim_length':
             $url = $url1;
-            $url_title = isset($new_value) ? truncate_utf8($url, $new_value, FALSE, TRUE) : $url;
+            $url_title = isset($new_value) ? Unicode::truncateUTF8($url, $new_value, FALSE, TRUE) : $url;
             $expected = '<div class="link-item">';
             $expected .= '<div class="link-url"><a href="' . check_plain($url) . '">' . check_plain($url_title) . '</a></div>';
             $expected .= '</div>';
             $this->assertRaw($expected);
 
             $url = $url2;
-            $url_title = isset($new_value) ? truncate_utf8($url, $new_value, FALSE, TRUE) : $url;
-            $title = isset($new_value) ? truncate_utf8($title2, $new_value, FALSE, TRUE) : $title2;
+            $url_title = isset($new_value) ? Unicode::truncateUTF8($url, $new_value, FALSE, TRUE) : $url;
+            $title = isset($new_value) ? Unicode::truncateUTF8($title2, $new_value, FALSE, TRUE) : $title2;
             $expected = '<div class="link-item">';
             $expected .= '<div class="link-title">' . check_plain($title) . '</div>';
             $expected .= '<div class="link-url"><a href="' . check_plain($url) . '">' . check_plain($url_title) . '</a></div>';
diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldUITest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldUITest.php
index f8ff160..bb2f80e 100644
--- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldUITest.php
+++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldUITest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\link\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -46,7 +47,7 @@ function testFieldUI() {
 
     // Add a link field to the newly-created type.
     $label = $this->randomName();
-    $field_name = drupal_strtolower($label);
+    $field_name = Unicode::strtolower($label);
     $edit = array(
       'fields[_add_new_field][label]' => $label,
       'fields[_add_new_field][field_name]' => $field_name,
diff --git a/core/modules/link/link.module b/core/modules/link/link.module
index 567f5f0..2d84a7c 100644
--- a/core/modules/link/link.module
+++ b/core/modules/link/link.module
@@ -5,6 +5,7 @@
  * Defines simple link field types.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -358,7 +359,7 @@ function link_field_formatter_view(EntityInterface $entity, $field, $instance, $
 
     // Trim the link title to the desired length.
     if (!empty($settings['trim_length'])) {
-      $link_title = truncate_utf8($link_title, $settings['trim_length'], FALSE, TRUE);
+      $link_title = Unicode::truncateUTF8($link_title, $settings['trim_length'], FALSE, TRUE);
     }
 
     if ($display['type'] == 'link') {
@@ -387,7 +388,7 @@ function link_field_formatter_view(EntityInterface $entity, $field, $instance, $
       }
       $url_title = $item['url'];
       if (!empty($settings['trim_length'])) {
-        $url_title = truncate_utf8($item['url'], $settings['trim_length'], FALSE, TRUE);
+        $url_title = Unicode::truncateUTF8($item['url'], $settings['trim_length'], FALSE, TRUE);
       }
       $element[$delta] = array(
         '#theme' => 'link_formatter_link_separate',
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index fb72927..ef6f53f 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -10,6 +10,7 @@
  * object files are supported.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\locale\LocaleLookup;
 use Drupal\locale\LocaleConfigSubscriber;
 use Drupal\locale\SourceString;
@@ -1024,7 +1025,7 @@ function locale_translation_use_remote_source() {
  * possible attack vector (img).
  */
 function locale_string_is_safe($string) {
-  return decode_entities($string) == decode_entities(filter_xss($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
+  return Unicode::decodeEntities($string) == Unicode::decodeEntities(filter_xss($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
 }
 
 /**
diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
index 654a005..d971adf 100644
--- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
+++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\menu\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 class MenuTest extends WebTestBase {
@@ -135,7 +136,7 @@ function addCustomMenu() {
     $this->assertRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array(
       '!name' => t('Menu name'),
       '%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
-      '%length' => drupal_strlen($menu_name),
+      '%length' => Unicode::strlen($menu_name),
     )));
 
     // Change the menu_name so it no longer exceeds the maximum length.
@@ -147,7 +148,7 @@ function addCustomMenu() {
     $this->assertNoRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array(
       '!name' => t('Menu name'),
       '%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
-      '%length' => drupal_strlen($menu_name),
+      '%length' => Unicode::strlen($menu_name),
     )));
     // Verify that confirmation message displayed.
     $this->assertRaw(t('Menu %label has been added.', array('%label' => $label)));
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 46d6ead..b3fce1f 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -11,6 +11,7 @@
  * URLs to be added to the main site navigation menu.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\block\Plugin\Core\Entity\Block;
 use Drupal\system\Plugin\Core\Entity\Menu;
@@ -397,7 +398,7 @@ function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude,
       break;
     }
     if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
-      $title = $indent . ' ' . truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
+      $title = $indent . ' ' . Unicode::truncateUTF8($data['link']['title'], 30, TRUE, FALSE);
       if ($data['link']['hidden']) {
         $title .= ' (' . t('disabled') . ')';
       }
diff --git a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php
index 7ee1e85..e351e53 100644
--- a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\node\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -37,9 +38,9 @@ function testMultiStepNodeFormBasicOptions() {
     $this->drupalLogin($web_user);
 
     // Create an unlimited cardinality field.
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field = array(
-      'field_name' => drupal_strtolower($this->field_name),
+      'field_name' => Unicode::strtolower($this->field_name),
       'type' => 'text',
       'cardinality' => -1,
     );
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php
index 433c6bb..55e8e31 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\node\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests the interaction of the node access system with fields.
  */
@@ -37,7 +39,7 @@ public function setUp() {
     $this->content_admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields'));
 
     // Add a custom field to the page content type.
-    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $this->field_name = Unicode::strtolower($this->randomName() . '_field_name');
     $this->field = field_create_field(array('field_name' => $this->field_name, 'type' => 'text'));
     $instance = array(
       'field_name' => $this->field_name,
diff --git a/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php b/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php
index 925828c..90e235b 100644
--- a/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php
+++ b/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\number\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -46,7 +47,7 @@ function setUp() {
   function testNumberDecimalField() {
     // Create a field with settings to validate.
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'number_decimal',
       'settings' => array(
         'precision' => 8, 'scale' => 4, 'decimal_separator' => '.',
@@ -137,13 +138,13 @@ function testNumberIntegerField() {
 
     // Add a content type.
     $name = $this->randomName();
-    $type = drupal_strtolower($name);
+    $type = Unicode::strtolower($name);
     $edit = array('name' => $name, 'type' => $type);
     $this->drupalPost(NULL, $edit, t('Save and manage fields'));
 
     // Add an integer field to the newly-created type.
     $label = $this->randomName();
-    $field_name = drupal_strtolower($label);
+    $field_name = Unicode::strtolower($label);
     $edit = array(
       'fields[_add_new_field][label]'=> $label,
       'fields[_add_new_field][field_name]' => $field_name,
diff --git a/core/modules/options/options.module b/core/modules/options/options.module
index 793b07e..bc06347 100644
--- a/core/modules/options/options.module
+++ b/core/modules/options/options.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\field\FieldUpdateForbiddenException;
 
@@ -198,7 +199,7 @@ function options_field_settings_form_validate_allowed_values($element, &$form_st
         form_error($element, t('Allowed values list: each key must be a valid integer or decimal.'));
         break;
       }
-      elseif ($field_type == 'list_text' && drupal_strlen($key) > 255) {
+      elseif ($field_type == 'list_text' && Unicode::strlen($key) > 255) {
         form_error($element, t('Allowed values list: each key must be a string at most 255 characters long.'));
         break;
       }
diff --git a/core/modules/path/path.admin.inc b/core/modules/path/path.admin.inc
index 0535454..8576b9f 100644
--- a/core/modules/path/path.admin.inc
+++ b/core/modules/path/path.admin.inc
@@ -5,6 +5,8 @@
  * Administrative page callbacks for the path module.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Returns a listing of all defined URL aliases.
  *
@@ -44,10 +46,10 @@ function path_admin_overview($keys = NULL) {
   $destination = drupal_get_destination();
   foreach ($result as $data) {
     $row = array();
-    $row['data']['alias'] = l(truncate_utf8($data->alias, 50, FALSE, TRUE), $data->source, array(
+    $row['data']['alias'] = l(Unicode::truncateUTF8($data->alias, 50, FALSE, TRUE), $data->source, array(
       'attributes' => array('title' => $data->alias),
     ));
-    $row['data']['source'] = l(truncate_utf8($data->source, 50, FALSE, TRUE), $data->source, array(
+    $row['data']['source'] = l(Unicode::truncateUTF8($data->source, 50, FALSE, TRUE), $data->source, array(
       'alias' => TRUE,
       'attributes' => array('title' => $data->source),
     ));
diff --git a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
index c8cd4cb..43a2e56 100644
--- a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
+++ b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\picture\Tests;
 
 use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
+use Drupal\Component\Utility\Unicode;
 use Drupal\image\Tests\ImageFieldTestBase;
 
 /**
@@ -114,7 +115,7 @@ public function testPictureFieldFormattersPrivate() {
    * Test picture formatters on node display.
    */
   public function _testPictureFieldFormatters($scheme) {
-    $field_name = drupal_strtolower($this->randomName());
+    $field_name = Unicode::strtolower($this->randomName());
     $this->createImageField($field_name, 'article', array('uri_scheme' => $scheme));
     // Create a new node with an image attached.
     $test_image = current($this->drupalGetTestFiles('image'));
diff --git a/core/modules/picture/picture.module b/core/modules/picture/picture.module
index e5a66dd..73e2ad4 100644
--- a/core/modules/picture/picture.module
+++ b/core/modules/picture/picture.module
@@ -5,6 +5,7 @@
  * Picture display formatter for image fields.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\picture\Plugin\Core\Entity\PictureMapping;
 use \Drupal\Core\Template\Attribute;
 
@@ -207,7 +208,7 @@ function theme_picture_formatter($variables) {
   $item = $variables['item'];
 
   // Do not output an empty 'title' attribute.
-  if (isset($item['title']) && drupal_strlen($item['title']) == 0) {
+  if (isset($item['title']) && Unicode::strlen($item['title']) == 0) {
     unset($item['title']);
   }
 
diff --git a/core/modules/picture/picture_mapping.admin.inc b/core/modules/picture/picture_mapping.admin.inc
index a3d6ade..fb5b3e9 100644
--- a/core/modules/picture/picture_mapping.admin.inc
+++ b/core/modules/picture/picture_mapping.admin.inc
@@ -5,6 +5,7 @@
  * Administration functions to maintain picture mappings.
  */
 
+use Drupal\Component\Utility\Unicode;
 use \Drupal\picture\PictureMapping;
 
 /**
@@ -83,7 +84,7 @@ function picture_mapping_action_confirm($form, &$form_state, $picture_mapping, $
       t('Are you sure you want to @action the picture_mapping %title?', array('@action' => $action, '%title' => $picture_mapping->label())),
       'admin/config/media/picturemapping',
       $action == 'delete' ? t('This action cannot be undone.') : '',
-      t(drupal_ucfirst($action)),
+      t(Unicode::ucfirst($action)),
       t('Cancel')
     );
   }
diff --git a/core/modules/search/lib/Drupal/search/SearchQuery.php b/core/modules/search/lib/Drupal/search/SearchQuery.php
index 86887b7..e0a6fa8 100644
--- a/core/modules/search/lib/Drupal/search/SearchQuery.php
+++ b/core/modules/search/lib/Drupal/search/SearchQuery.php
@@ -9,6 +9,7 @@
 
 namespace Drupal\search;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Query\SelectExtender;
 use Drupal\Core\Database\StatementEmpty;
 
@@ -323,7 +324,7 @@ protected function parseWord($word) {
     $split = explode(' ', $word);
     foreach ($split as $s) {
       $num = is_numeric($s);
-      if ($num || drupal_strlen($s) >= config('search.settings')->get('index.minimum_word_size')) {
+      if ($num || Unicode::strlen($s) >= config('search.settings')->get('index.minimum_word_size')) {
         if (!isset($this->words[$s])) {
           $this->words[$s] = $s;
           $num_new_scores++;
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchSimplifyTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchSimplifyTest.php
index 5b1e5d4..6823f58 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchSimplifyTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchSimplifyTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\search\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Test search_simplify() on every Unicode character, and some other cases.
  */
@@ -44,8 +46,8 @@ function testSearchSimplifyUnicode() {
         // Split this into 30-character chunks, so we don't run into limits
         // of truncation in search_simplify().
         $start = 0;
-        while ($start < drupal_strlen($string)) {
-          $newstr = drupal_substr($string, $start, 30);
+        while ($start < Unicode::strlen($string)) {
+          $newstr = Unicode::substr($string, $start, 30);
           // Special case: leading zeros are removed from numeric strings,
           // and there's one string in this file that is numbers starting with
           // zero, so prepend a 1 on that string.
@@ -59,7 +61,7 @@ function testSearchSimplifyUnicode() {
     }
     foreach ($strings as $key => $string) {
       $simplified = search_simplify($string);
-      $this->assertTrue(drupal_strlen($simplified) >= drupal_strlen($string), "Nothing is removed from string $key.");
+      $this->assertTrue(Unicode::strlen($simplified) >= Unicode::strlen($string), "Nothing is removed from string $key.");
     }
 
     // Test the low-numbered ASCII control characters separately. They are not
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchTokenizerTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchTokenizerTest.php
index 8e705d1..866883a 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchTokenizerTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchTokenizerTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\search\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Test the CJK tokenizer.
  */
@@ -103,7 +105,7 @@ function testTokenizer() {
     // Merge into a string and tokenize.
     $string = implode('', $chars);
     $out = trim(search_simplify($string));
-    $expected = drupal_strtolower(implode(' ', $chars));
+    $expected = Unicode::strtolower(implode(' ', $chars));
 
     // Verify that the output matches what we expect.
     $this->assertEqual($out, $expected, 'CJK tokenizer worked on all supplied CJK characters');
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 375c43f..23bbe1b 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Matches all 'N' Unicode character classes (numbers)
@@ -418,10 +419,10 @@ function search_update_totals() {
  */
 function search_simplify($text, $langcode = NULL) {
   // Decode entities to UTF-8
-  $text = decode_entities($text);
+  $text = Unicode::decodeEntities($text);
 
   // Lowercase
-  $text = drupal_strtolower($text);
+  $text = Unicode::strtolower($text);
 
   // Call an external processor for word handling.
   search_invoke_preprocess($text, $langcode);
@@ -450,7 +451,7 @@ function search_simplify($text, $langcode = NULL) {
 
   // With the exception of the rules above, we consider all punctuation,
   // marks, spacers, etc, to be a word boundary.
-  $text = preg_replace('/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', ' ', $text);
+  $text = preg_replace('/[' . Unicode::PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', ' ', $text);
 
   // Truncate everything to 50 characters.
   $words = explode(' ', $text);
@@ -483,7 +484,7 @@ function search_simplify($text, $langcode = NULL) {
 function search_expand_cjk($matches) {
   $min = config('search.settings')->get('index.minimum_word_size');
   $str = $matches[0];
-  $length = drupal_strlen($str);
+  $length = Unicode::strlen($str);
   // If the text is shorter than the minimum word size, don't tokenize it.
   if ($length <= $min) {
     return ' ' . $str . ' ';
@@ -493,7 +494,7 @@ function search_expand_cjk($matches) {
   $chars = array();
   for ($i = 0; $i < $length; $i++) {
     // Add the next character off the beginning of the string to the queue.
-    $current = drupal_substr($str, 0, 1);
+    $current = Unicode::substr($str, 0, 1);
     $str = substr($str, strlen($current));
     $chars[] = $current;
     if ($i >= $min - 1) {
@@ -534,7 +535,7 @@ function _search_index_truncate(&$text) {
   if (is_numeric($text)) {
     $text = ltrim($text, '0');
   }
-  $text = truncate_utf8($text, 50);
+  $text = Unicode::truncateUTF8($text, 50);
 }
 
 /**
@@ -597,7 +598,7 @@ function search_index($sid, $module, $text, $langcode) {
     if ($tag) {
       // Increase or decrease score per word based on tag
       list($tagname) = explode(' ', $value, 2);
-      $tagname = drupal_strtolower($tagname);
+      $tagname = Unicode::strtolower($tagname);
       // Closing or opening tag?
       if ($tagname[0] == '/') {
         $tagname = substr($tagname, 1);
@@ -659,7 +660,7 @@ function search_index($sid, $module, $text, $langcode) {
           // Add word to accumulator
           $accum .= $word . ' ';
           // Check wordlength
-          if (is_numeric($word) || drupal_strlen($word) >= $minimum_word_size) {
+          if (is_numeric($word) || Unicode::strlen($word) >= $minimum_word_size) {
             // Links score mainly for the target.
             if ($link) {
               if (!isset($results[$linknid])) {
@@ -1105,7 +1106,7 @@ function search_data($keys, $module, $conditions = NULL) {
  */
 function search_excerpt($keys, $text, $langcode = NULL) {
   // We highlight around non-indexable or CJK characters.
-  $boundary = '(?:(?<=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . '])|(?=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . ']))';
+  $boundary = '(?:(?<=[' . Unicode::PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . '])|(?=[' . Unicode::PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . ']))';
 
   // Extract positive keywords and phrases
   preg_match_all('/ ("([^"]+)"|(?!OR)([^" ]+))/', ' ' . $keys, $matches);
@@ -1113,7 +1114,7 @@ function search_excerpt($keys, $text, $langcode = NULL) {
 
   // Prepare text by stripping HTML tags and decoding HTML entities.
   $text = strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text));
-  $text = decode_entities($text);
+  $text = Unicode::decodeEntities($text);
 
   // Slash-escape quotes in the search keyword string.
   array_walk($keys, '_search_excerpt_replace');
@@ -1190,7 +1191,7 @@ function search_excerpt($keys, $text, $langcode = NULL) {
     // We didn't find any keyword matches, so just return the first part of the
     // text. We also need to re-encode any HTML special characters that we
     // entity-decoded above.
-    return check_plain(truncate_utf8($text, 256, TRUE, TRUE));
+    return check_plain(Unicode::truncateUTF8($text, 256, TRUE, TRUE));
   }
 
   // Sort the text ranges by starting position.
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 44e29c2..2a76279 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\simpletest;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\ConnectionNotDefinedException;
@@ -1559,7 +1560,7 @@ protected function checkForMetaRefresh() {
         // Parse the content attribute of the meta tag for the format:
         // "[delay]: URL=[page_to_redirect_to]".
         if (preg_match('/\d+;\s*URL=(?P<url>.*)/i', $refresh[0]['content'], $match)) {
-          return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url'])));
+          return $this->drupalGet($this->getAbsoluteUrl(Unicode::decodeEntities($match['url'])));
         }
       }
     }
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index f6a9d48..35ff5ad 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -5,6 +5,7 @@
  * Logs and displays content statistics for a site.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
 
@@ -174,7 +175,7 @@ function statistics_get($nid) {
  */
 function _statistics_link($path, $width = 35) {
   $title = drupal_container()->get('path.alias_manager')->getPathAlias($path);
-  $title = truncate_utf8($title, $width, FALSE, TRUE);
+  $title = Unicode::truncateUTF8($title, $width, FALSE, TRUE);
   return l($title, $path);
 }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
index 3c54ad4..41390e8 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Common;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -188,7 +189,7 @@ function testHeaderSetting() {
     $endToken = '}';
     $start = strpos($javascript, $startToken) + strlen($startToken);
     $end = strrpos($javascript, $endToken);
-    $json  = drupal_substr($javascript, $start, $end - $start + 1);
+    $json  = Unicode::substr($javascript, $start, $end - $start + 1);
     $parsed_settings = drupal_json_decode($json);
 
     // Test whether the two real world cases are handled correctly.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/TableSortExtenderUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/TableSortExtenderUnitTest.php
index c7cc2a9..718f244 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/TableSortExtenderUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/TableSortExtenderUnitTest.php
@@ -10,7 +10,8 @@
 use Drupal\simpletest\UnitTestBase;
 
 /**
- * Tests unicode handling features implemented in unicode.inc.
+ * Tests unicode handling features implemented in
+ * \Drupal\Component\Utility\Unicode.
  */
 class TableSortExtenderUnitTest extends UnitTestBase {
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php
index 434311c..32efcc0 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests Entity Query API relationship functionality.
  */
@@ -75,7 +77,7 @@ public function setUp() {
     // We want a taxonomy term reference field. It needs a vocabulary, terms,
     // a field and an instance. First, create the vocabulary.
     $vocabulary = entity_create('taxonomy_vocabulary', array(
-      'vid' => drupal_strtolower($this->randomName()),
+      'vid' => Unicode::strtolower($this->randomName()),
     ));
     $vocabulary->save();
     // Second, create the field.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
index ae51625..4014cdc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests the basic Entity API.
  */
@@ -54,8 +56,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
     $this->installSchema('field_test', array('test_entity', 'test_entity_revision', 'test_entity_bundle'));
-    $figures = drupal_strtolower($this->randomName());
-    $greetings = drupal_strtolower($this->randomName());
+    $figures = Unicode::strtolower($this->randomName());
+    $greetings = Unicode::strtolower($this->randomName());
     foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) {
       $field = array(
         'field_name' => $field_name,
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
index cccdc5f..7b8d77f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\system\Tests\Entity;
 
 use InvalidArgumentException;
-
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
 
 /**
@@ -49,7 +49,7 @@ function setUp() {
     state()->set('entity_test.translation', TRUE);
 
     // Create a translatable test field.
-    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $this->field_name = Unicode::strtolower($this->randomName() . '_field_name');
     $field = array(
       'field_name' => $this->field_name,
       'type' => 'text',
diff --git a/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php b/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php
index f93b40f..bf4590a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Mail;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -54,7 +55,7 @@ protected function stringToHtml($text) {
    *   set of tags supported by drupal_html_to_text().
    */
   protected function assertHtmlToText($html, $text, $message, $allowed_tags = NULL) {
-    preg_match_all('/<([a-z0-6]+)/', drupal_strtolower($html), $matches);
+    preg_match_all('/<([a-z0-6]+)/', Unicode::strtolower($html), $matches);
     $tested_tags = implode(', ', array_unique($matches[1]));
     $message .= ' (' . $tested_tags . ')';
     $result = drupal_html_to_text($html, $allowed_tags);
@@ -246,8 +247,8 @@ public function testDrupalHtmlToTextBlockTagToNewline() {
     if (!$pass) {
       $this->verbose($this->stringToHtml($output));
     }
-    $output_upper = drupal_strtoupper($output);
-    $upper_input = drupal_strtoupper($input);
+    $output_upper = Unicode::strtoupper($output);
+    $upper_input = Unicode::strtoupper($input);
     $upper_output = drupal_html_to_text($upper_input);
     $pass = $this->assertEqual(
       $upper_output,
@@ -352,7 +353,7 @@ public function testVeryLongLineWrap() {
 
     $maximum_line_length = 0;
     foreach (explode($eol, $output) as $line) {
-      // We must use strlen() rather than drupal_strlen() in order to count
+      // We must use strlen() rather than Unicode::strlen() in order to count
       // octets rather than characters.
       $maximum_line_length = max($maximum_line_length, strlen($line . $eol));
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
index e561ab7..c121a4c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\Module;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests module dependency functionality.
  */
@@ -50,7 +52,7 @@ function testMissingModules() {
     // Test that the system_dependencies_test module is marked
     // as missing a dependency.
     $this->drupalGet('admin/modules');
-    $this->assertRaw(t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst('_missing_dependency'))), 'A module with missing dependencies is marked as such.');
+    $this->assertRaw(t('@module (<span class="admin-missing">missing</span>)', array('@module' => Unicode::ucfirst('_missing_dependency'))), 'A module with missing dependencies is marked as such.');
     $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Testing][system_dependencies_test][enable]"]');
     $this->assert(count($checkbox) == 1, 'Checkbox for the module is disabled.');
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/UnicodeUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/System/UnicodeUnitTest.php
index 58bef10..eb5a50a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/UnicodeUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/UnicodeUnitTest.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\system\Tests\System;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\UnitTestBase;
 
 /**
- * Test unicode handling features implemented in unicode.inc.
+ * Test unicode handling features implemented in
+ * \Drupal\Component\Utility\Unicode.
  */
 class UnicodeUnitTest extends UnitTestBase {
 
@@ -84,7 +86,7 @@ function helperTestStrToLower() {
     }
 
     foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_strtolower($input), $output, format_string('%input is lowercased as %output', array('%input' => $input, '%output' => $output)));
+      $this->assertEqual(Unicode::strtolower($input), $output, format_string('%input is lowercased as %output', array('%input' => $input, '%output' => $output)));
     }
   }
 
@@ -98,7 +100,7 @@ function helperTestStrToUpper() {
     }
 
     foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_strtoupper($input), $output, format_string('%input is uppercased as %output', array('%input' => $input, '%output' => $output)));
+      $this->assertEqual(Unicode::strtoupper($input), $output, format_string('%input is uppercased as %output', array('%input' => $input, '%output' => $output)));
     }
   }
 
@@ -114,7 +116,7 @@ function helperTestUcFirst() {
     }
 
     foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_ucfirst($input), $output, format_string('%input is ucfirst-ed as %output', array('%input' => $input, '%output' => $output)));
+      $this->assertEqual(Unicode::ucfirst($input), $output, format_string('%input is ucfirst-ed as %output', array('%input' => $input, '%output' => $output)));
     }
   }
 
@@ -125,7 +127,7 @@ function helperTestStrLen() {
     );
 
     foreach ($testcase as $input => $output) {
-      $this->assertEqual(drupal_strlen($input), $output, format_string('%input length is %output', array('%input' => $input, '%output' => $output)));
+      $this->assertEqual(Unicode::strlen($input), $output, format_string('%input length is %output', array('%input' => $input, '%output' => $output)));
     }
   }
 
@@ -184,13 +186,13 @@ function helperTestSubStr() {
 
     foreach ($testcase as $test) {
       list($input, $start, $length, $output) = $test;
-      $result = drupal_substr($input, $start, $length);
+      $result = Unicode::substr($input, $start, $length);
       $this->assertEqual($result, $output, format_string('%input substring at offset %offset for %length characters is %output (got %result)', array('%input' => $input, '%offset' => $start, '%length' => $length, '%output' => $output, '%result' => $result)));
     }
   }
 
   /**
-   * Test decode_entities().
+   * Test Unicode::decodeEntities().
    */
   function testDecodeEntities() {
     $testcase = array(
@@ -217,12 +219,12 @@ function testDecodeEntities() {
       '&euro;' => '€',
     );
     foreach ($testcase as $input => $output) {
-      $this->assertEqual(decode_entities($input), $output, format_string('Make sure the decoded entity of @input is @output', array('@input' => $input, '@output' => $output)));
+      $this->assertEqual(Unicode::decodeEntities($input), $output, format_string('Make sure the decoded entity of @input is @output', array('@input' => $input, '@output' => $output)));
     }
   }
 
   /**
-   * Tests truncate_utf8().
+   * Tests Unicode::truncateUTF8().
    */
   function helperTestTruncate() {
     // Each case is an array with input string, length to truncate to, and
@@ -291,7 +293,7 @@ function helperTestTruncate() {
   /**
    * Runs test cases for helperTestTruncate().
    *
-   * Runs each test case through truncate_utf8() and compares the output
+   * Runs each test case through Unicode::truncateUTF8() and compares the output
    * to the expected output.
    *
    * @param $cases
@@ -305,7 +307,7 @@ function helperTestTruncate() {
   function runTruncateTests($cases, $wordsafe, $ellipsis) {
     foreach ($cases as $case) {
       list($input, $max_length, $expected) = $case;
-      $output = truncate_utf8($input, $max_length, $wordsafe, $ellipsis);
+      $output = Unicode::truncateUTF8($input, $max_length, $wordsafe, $ellipsis);
       $this->assertEqual($output, $expected, format_string('%input truncate to %length characters with %wordsafe, %ellipsis is %expected (got %output)', array('%input' => $input, '%length' => $max_length, '%output' => $output, '%expected' => $expected, '%wordsafe' => ($wordsafe ? 'word-safe' : 'not word-safe'), '%ellipsis' => ($ellipsis ? 'ellipsis' : 'not ellipsis'))));
     }
   }
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 4a4ba0e..5eaf9cf 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -9,6 +9,7 @@
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Menu callback; Provide the administration overview page.
@@ -860,7 +861,7 @@ function system_modules($form, $form_state = array()) {
     // If this module requires other modules, add them to the array.
     foreach ($module->requires as $requires => $v) {
       if (!isset($files[$requires])) {
-        $extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
+        $extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => Unicode::ucfirst($requires)));
         $extra['disabled'] = TRUE;
       }
       // Only display visible modules.
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 882a049..e367285 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -7,6 +7,8 @@
  * Install, update and uninstall functions for the system module.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Test and report Drupal installation requirements.
  *
@@ -494,8 +496,7 @@ function system_requirements($phase) {
   }
 
   // Test Unicode library
-  include_once DRUPAL_ROOT . '/core/includes/unicode.inc';
-  $requirements = array_merge($requirements, unicode_requirements());
+  $requirements = array_merge($requirements, Unicode::requirements());
 
   if ($phase == 'runtime') {
     // Check for update status module.
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 2c51ae0..63b5a54 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -5,6 +5,7 @@
  * Configuration system that lets administrators modify the workings of the site.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Utility\ModuleInfo;
 use Drupal\Core\TypedData\Primitive;
@@ -1149,7 +1150,7 @@ function system_plugin_ui_form($form, &$form_state, $plugin, $facet = NULL) {
 function system_plugin_autocomplete($plugin_id) {
   $string_typed = drupal_container()->get('request')->query->get('q');
   $string_typed = drupal_explode_tags($string_typed);
-  $string = drupal_strtolower(array_pop($string_typed));
+  $string = Unicode::strtolower(array_pop($string_typed));
   $matches = array();
   if ($string) {
     $plugin_ui = drupal_container()->get('plugin.manager.system.plugin_ui')->getDefinition($plugin_id);
@@ -2705,7 +2706,7 @@ function system_admin_menu_block($item) {
       }
       // Prepare for sorting as in function _menu_tree_check_access().
       // The weight is offset so it is always positive, with a uniform 5-digits.
-      $key = (50000 + $link['weight']) . ' ' . drupal_strtolower($link['title']) . ' ' . $link['mlid'];
+      $key = (50000 + $link['weight']) . ' ' . Unicode::strtolower($link['title']) . ' ' . $link['mlid'];
       $content[$key] = $link;
     }
   }
diff --git a/core/modules/system/tests/modules/design_test/design_test.module b/core/modules/system/tests/modules/design_test/design_test.module
index a7ab1b5..b8a1495 100644
--- a/core/modules/system/tests/modules/design_test/design_test.module
+++ b/core/modules/system/tests/modules/design_test/design_test.module
@@ -5,6 +5,8 @@
  * Support module for design, markup, JavaScript, and CSS testing.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Implements hook_menu().
  *
@@ -56,7 +58,7 @@ function design_test_menu() {
     }
 
     $items["design_test/{$category}/{$path}"] = array(
-      'title' => drupal_ucfirst($name),
+      'title' => Unicode::ucfirst($name),
       'theme callback' => 'design_test_menu_theme_callback',
       'page callback' => $page_callback,
       'page arguments' => array($callback),
@@ -79,7 +81,7 @@ function design_test_menu() {
   // Lastly, add the category containers.
   foreach ($categories as $category) {
     $items["design_test/{$category}"] = array(
-      'title' => drupal_ucfirst($category),
+      'title' => Unicode::ucfirst($category),
       'page callback' => 'design_test_category_page',
       'page arguments' => array(1),
       'access callback' => TRUE,
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php
index f4b0ec8..94e27b8 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\Entity\Field\FieldItemInterface;
 use Drupal\field\Tests\FieldItemUnitTestBase;
@@ -38,7 +39,7 @@ public function setUp() {
 
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
-      'vid' => drupal_strtolower($this->randomName()),
+      'vid' => Unicode::strtolower($this->randomName()),
       'langcode' => LANGUAGE_NOT_SPECIFIED,
     ));
     $vocabulary->save();
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
index fab42f0..098b0cd 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -38,7 +39,7 @@ function createVocabulary() {
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
       'description' => $this->randomName(),
-      'vid' => drupal_strtolower($this->randomName()),
+      'vid' => Unicode::strtolower($this->randomName()),
       'langcode' => LANGUAGE_NOT_SPECIFIED,
       'help' => '',
       'weight' => mt_rand(0, 10),
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php
index 8c5571c..bcb2dac 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests a taxonomy term reference field that allows multiple vocabularies.
  */
@@ -40,7 +42,7 @@ function setUp() {
     $this->vocabulary2 = $this->createVocabulary();
 
     // Set up a field and instance.
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field = array(
       'field_name' => $this->field_name,
       'type' => 'taxonomy_term_reference',
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php
index d5de5fa..208970a 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\field\FieldValidationException;
 
 /**
@@ -40,7 +41,7 @@ function setUp() {
     $this->vocabulary = $this->createVocabulary();
 
     // Setup a field and instance.
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field = array(
       'field_name' => $this->field_name,
       'type' => 'taxonomy_term_reference',
@@ -157,7 +158,7 @@ function testTaxonomyTermFieldChangeMachineName() {
     );
     field_update_field($this->field);
     // Change the machine name.
-    $new_name = drupal_strtolower($this->randomName());
+    $new_name = Unicode::strtolower($this->randomName());
     $this->vocabulary->vid = $new_name;
     taxonomy_vocabulary_save($this->vocabulary);
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
index f9cba0d..054d657 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests the hook implementations that maintain the taxonomy index.
  */
@@ -30,7 +32,7 @@ function setUp() {
     // Create a vocabulary and add two term reference fields to article nodes.
     $this->vocabulary = $this->createVocabulary();
 
-    $this->field_name_1 = drupal_strtolower($this->randomName());
+    $this->field_name_1 = Unicode::strtolower($this->randomName());
     $this->field_1 = array(
       'field_name' => $this->field_name_1,
       'type' => 'taxonomy_term_reference',
@@ -60,7 +62,7 @@ function setUp() {
       ))
       ->save();
 
-    $this->field_name_2 = drupal_strtolower($this->randomName());
+    $this->field_name_2 = Unicode::strtolower($this->randomName());
     $this->field_2 = array(
       'field_name' => $this->field_name_2,
       'type' => 'taxonomy_term_reference',
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
index 93cc8d4..95d43f5 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests for taxonomy term functions.
  */
@@ -467,7 +469,7 @@ function testTaxonomyGetTermByName() {
     $this->assertFalse($terms);
 
     // Try to load the term using a substring of the name.
-    $terms = taxonomy_term_load_multiple_by_name(drupal_substr($term->name, 2));
+    $terms = taxonomy_term_load_multiple_by_name(Unicode::substr($term->name, 2));
     $this->assertFalse($terms);
 
     // Create a new term in a different vocabulary with the same name.
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php
index 89053cd..0ba4ac7 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
 
 /**
@@ -55,7 +56,7 @@ function testVocabularyLanguage() {
     $this->assertField('edit-langcode', 'The language selector field was found on the page');
 
     // Create the vocabulary.
-    $vid = drupal_strtolower($this->randomName());
+    $vid = Unicode::strtolower($this->randomName());
     $edit['name'] = $this->randomName();
     $edit['description'] = $this->randomName();
     $edit['langcode'] = 'aa';
@@ -84,7 +85,7 @@ function testVocabularyDefaultLanguageForTerms() {
     // the terms are saved.
     $edit = array(
       'name' => $this->randomName(),
-      'vid' => drupal_strtolower($this->randomName()),
+      'vid' => Unicode::strtolower($this->randomName()),
       'default_language[langcode]' => 'bb',
       'default_language[language_show]' => TRUE,
     );
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php
index 8d05f64..20e90fe 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests the taxonomy vocabulary interface.
  */
@@ -37,7 +39,7 @@ function testVocabularyInterface() {
     // Create a new vocabulary.
     $this->clickLink(t('Add vocabulary'));
     $edit = array();
-    $vid = drupal_strtolower($this->randomName());
+    $vid = Unicode::strtolower($this->randomName());
     $edit['name'] = $this->randomName();
     $edit['description'] = $this->randomName();
     $edit['vid'] = $vid;
@@ -127,7 +129,7 @@ function testTaxonomyAdminNoVocabularies() {
    */
   function testTaxonomyAdminDeletingVocabulary() {
     // Create a vocabulary.
-    $vid = drupal_strtolower($this->randomName());
+    $vid = Unicode::strtolower($this->randomName());
     $edit = array(
       'name' => $this->randomName(),
       'vid' => $vid,
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
index d73302b..71571da 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\taxonomy\Tests;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests for taxonomy vocabulary functions.
  */
@@ -155,7 +157,7 @@ function testTaxonomyVocabularyChangeMachineName() {
 
     // Change the machine name.
     $old_name = $this->vocabulary->id();
-    $new_name = drupal_strtolower($this->randomName());
+    $new_name = Unicode::strtolower($this->randomName());
     $this->vocabulary->vid = $new_name;
     taxonomy_vocabulary_save($this->vocabulary);
 
@@ -174,7 +176,7 @@ function testTaxonomyVocabularyChangeMachineName() {
   function testUninstallReinstall() {
     // Fields and field instances attached to taxonomy term bundles should be
     // removed when the module is uninstalled.
-    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $this->field_name = Unicode::strtolower($this->randomName() . '_field_name');
     $this->field = array('field_name' => $this->field_name, 'type' => 'text', 'cardinality' => 4);
     $this->field = field_create_field($this->field);
     $this->instance = array(
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index f894d6c..7ab99b3 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -9,6 +9,7 @@
 use Drupal\taxonomy\Plugin\Core\Entity\Term;
 use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Denotes that no term in the vocabulary has a parent.
@@ -72,11 +73,11 @@ function taxonomy_help($path, $arg) {
       $vocabulary = taxonomy_vocabulary_load($arg[3]);
       switch ($vocabulary->hierarchy) {
         case TAXONOMY_HIERARCHY_DISABLED:
-          return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '</p>';
+          return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '</p>';
         case TAXONOMY_HIERARCHY_SINGLE:
-          return '<p>' . t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '</p>';
+          return '<p>' . t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', array('%capital_name' => Unicode::ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '</p>';
         case TAXONOMY_HIERARCHY_MULTIPLE:
-          return '<p>' . t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) . '</p>';
+          return '<p>' . t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->name))) . '</p>';
       }
   }
 }
diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc
index b6f1329..fd0f9fd 100644
--- a/core/modules/taxonomy/taxonomy.pages.inc
+++ b/core/modules/taxonomy/taxonomy.pages.inc
@@ -5,6 +5,7 @@
  * Page callbacks for the taxonomy module.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\taxonomy\Plugin\Core\Entity\Term;
 use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary;
 use Symfony\Component\HttpFoundation\JsonResponse;
@@ -118,7 +119,7 @@ function taxonomy_autocomplete($field_name) {
 
   // The user enters a comma-separated list of tags. We only autocomplete the last tag.
   $tags_typed = drupal_explode_tags($tags_typed);
-  $tag_last = drupal_strtolower(array_pop($tags_typed));
+  $tag_last = Unicode::strtolower(array_pop($tags_typed));
 
   $matches = array();
   if ($tag_last != '') {
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 716ead7..ff7e462 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\text\Tests\Formatter;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
 use Drupal\simpletest\DrupalUnitTestBase;
@@ -55,7 +56,7 @@ function setUp() {
       $this->bundle = $this->entity_type;
     }
 
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field_type = 'text_long';
     $this->field_settings = array();
     $this->instance_settings = array(
diff --git a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php
index f8c585b..4d4b1ba 100644
--- a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\text\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\field\FieldValidationException;
 use Drupal\simpletest\WebTestBase;
 
@@ -51,7 +52,7 @@ function testTextFieldValidation() {
     // Create a field with settings to validate.
     $max_length = 3;
     $this->field = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'field_name' => Unicode::strtolower($this->randomName()),
       'type' => 'text',
       'settings' => array(
         'max_length' => $max_length,
@@ -100,7 +101,7 @@ function testTextfieldWidgets() {
   function _testTextfieldWidgets($field_type, $widget_type) {
     // Setup a field and instance
     $entity_type = 'test_entity';
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field = array('field_name' => $this->field_name, 'type' => $field_type);
     field_create_field($this->field);
     $this->instance = array(
@@ -163,7 +164,7 @@ function testTextfieldWidgetsFormatted() {
   function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
     // Setup a field and instance
     $entity_type = 'test_entity';
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field = array('field_name' => $this->field_name, 'type' => $field_type);
     field_create_field($this->field);
     $this->instance = array(
@@ -222,7 +223,7 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
     // access to it.
     $this->drupalLogin($this->admin_user);
     $edit = array(
-      'format' => drupal_strtolower($this->randomName()),
+      'format' => Unicode::strtolower($this->randomName()),
       'name' => $this->randomName(),
     );
     $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
diff --git a/core/modules/text/text.module b/core/modules/text/text.module
index 37df945..61948f7 100644
--- a/core/modules/text/text.module
+++ b/core/modules/text/text.module
@@ -5,6 +5,7 @@
  * Defines simple text field types.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -146,7 +147,7 @@ function text_field_validate(EntityInterface $entity = NULL, $field, $instance,
     //   length can be exceeded very easily.
     foreach (array('value', 'summary') as $column) {
       if (!empty($item[$column])) {
-        if (!empty($field['settings']['max_length']) && drupal_strlen($item[$column]) > $field['settings']['max_length']) {
+        if (!empty($field['settings']['max_length']) && Unicode::strlen($item[$column]) > $field['settings']['max_length']) {
           switch ($column) {
             case 'value':
               $message = t('%name: the text may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length']));
@@ -283,7 +284,7 @@ function text_summary($text, $format = NULL, $size = NULL) {
   }
 
   // If we have a short body, the entire body is the summary.
-  if (drupal_strlen($text) <= $size) {
+  if (Unicode::strlen($text) <= $size) {
     return $text;
   }
 
@@ -291,7 +292,7 @@ function text_summary($text, $format = NULL, $size = NULL) {
   // sentence boundaries.
 
   // The summary may not be longer than maximum length specified. Initial slice.
-  $summary = truncate_utf8($text, $size);
+  $summary = Unicode::truncateUTF8($text, $size);
 
   // Store the actual length of the UTF8 string -- which might not be the same
   // as $size.
diff --git a/core/modules/tour/tour.module b/core/modules/tour/tour.module
index f26b356..8e9d801 100644
--- a/core/modules/tour/tour.module
+++ b/core/modules/tour/tour.module
@@ -4,6 +4,8 @@
  * @file
  * Main functions of the module.
  */
+
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Cache\CacheBackendInterface;
 
 /**
@@ -120,7 +122,7 @@ function tour_preprocess_page(&$variables) {
     ->execute();
   $tours = entity_load_multiple('tour', $tour_ids);
 
-  $path_alias = drupal_strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path));
+  $path_alias = Unicode::strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path));
   foreach ($tours as $tour) {
     // @todo replace this with an entity_query() that does path matching when
     // http://drupal.org/node/1918768 lands.
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index b99fdcf..5c02979 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -5,6 +5,7 @@
  * Allows entities to be translated into different languages.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Entity\EntityFormControllerInterface;
 use Drupal\Core\Entity\EntityInterface;
@@ -570,7 +571,7 @@ function translation_entity_permission() {
   // bundle.
   foreach (entity_get_info() as $entity_type => $info) {
     if (!empty($info['permission_granularity'])) {
-      $t_args = array('@entity_label' => drupal_strtolower(t($info['label'])));
+      $t_args = array('@entity_label' => Unicode::strtolower(t($info['label'])));
 
       switch ($info['permission_granularity']) {
         case 'bundle':
diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc
index 607b523..da62c7d 100644
--- a/core/modules/update/update.report.inc
+++ b/core/modules/update/update.report.inc
@@ -5,6 +5,8 @@
  * Code required only when rendering the available updates report.
  */
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Page callback: Generates a page about the update status of projects.
  *
@@ -226,7 +228,7 @@ function theme_update_report($variables) {
     if (!isset($rows[$project['project_type']])) {
       $rows[$project['project_type']] = array();
     }
-    $row_key = isset($project['title']) ? drupal_strtolower($project['title']) : drupal_strtolower($project['name']);
+    $row_key = isset($project['title']) ? Unicode::strtolower($project['title']) : Unicode::strtolower($project['name']);
     $rows[$project['project_type']][$row_key] = array(
       'class' => array($class),
       'data' => array($row),
diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
index 8f2f0c5..57ab7e8 100644
--- a/core/modules/user/lib/Drupal/user/AccountFormController.php
+++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\user;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityFormController;
 
@@ -284,7 +285,7 @@ public function validate(array $form, array &$form_state) {
       $form_state['values']['signature'] = $form_state['values']['signature']['value'];
 
       $user_schema = drupal_get_schema('users');
-      if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
+      if (Unicode::strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
         form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
       }
     }
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAutocompleteTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAutocompleteTest.php
index 380990e..e4854e8 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserAutocompleteTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserAutocompleteTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\user\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -51,9 +52,9 @@ function testUserAutocomplete() {
     config('user.settings')->set('anonymous', $anonymous_name)->save();
     // Test that anonymous username is in the result when requested and escaped
     // with check_plain().
-    $users = $this->drupalGetAjax('user/autocomplete/anonymous', array('query' => array('q' => drupal_substr($anonymous_name, 0, 4))));
+    $users = $this->drupalGetAjax('user/autocomplete/anonymous', array('query' => array('q' => Unicode::substr($anonymous_name, 0, 4))));
     $this->assertTrue(in_array(check_plain($anonymous_name), $users), 'The anonymous name found in autocompletion results.');
-    $users = $this->drupalGetAjax('user/autocomplete', array('query' => array('q' => drupal_substr($anonymous_name, 0, 4))));
+    $users = $this->drupalGetAjax('user/autocomplete', array('query' => array('q' => Unicode::substr($anonymous_name, 0, 4))));
     $this->assertFalse(isset($users[$anonymous_name]), 'The anonymous name not found in autocompletion results without enabling anonymous username.');
   }
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index bdf5862..fd174b3 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -2,6 +2,7 @@
 
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Component\Utility\Unicode;
 use Drupal\comment\Plugin\Core\Entity\Comment;
 use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
 use Drupal\file\Plugin\Core\Entity\File;
@@ -387,7 +388,7 @@ function user_validate_name($name) {
                   $name)) {
     return t('The username contains an illegal character.');
   }
-  if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
+  if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
     return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
   }
 }
@@ -756,8 +757,8 @@ function template_preprocess_username(&$variables) {
   // their own shortening logic or add markup. If they do so, they must ensure
   // that $variables['name'] is safe for printing.
   $name = $variables['name_raw'] = user_format_name($account);
-  if (drupal_strlen($name) > 20) {
-    $name = drupal_substr($name, 0, 15) . '...';
+  if (Unicode::strlen($name) > 20) {
+    $name = Unicode::substr($name, 0, 15) . '...';
   }
   $variables['name'] = check_plain($name);
   $variables['profile_access'] = user_access('access user profiles');
diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc
index 65ecd3f..069e3e7 100644
--- a/core/modules/views/includes/ajax.inc
+++ b/core/modules/views/includes/ajax.inc
@@ -5,6 +5,7 @@
  * Handles the server side AJAX interactions of Views.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Drupal\views\Ajax\HighlightCommand;
 use Drupal\views\Ajax\SetFormCommand;
@@ -153,7 +154,7 @@ function views_ajax_form_wrapper($form_id, &$form_state) {
 function views_ajax_autocomplete_taxonomy($vid) {
   // The user enters a comma-separated list of tags. We only autocomplete the last tag.
   $tags_typed = drupal_explode_tags(drupal_container()->get('request')->query->get('q'));
-  $tag_last = drupal_strtolower(array_pop($tags_typed));
+  $tag_last = Unicode::strtolower(array_pop($tags_typed));
 
   $matches = array();
   if ($tag_last != '') {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
index 94b4a3b..a3b6fd0 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Plugin\views;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\PluginBase;
@@ -248,11 +249,11 @@ protected function caseTransform($string, $option) {
       default:
         return $string;
       case 'upper':
-        return drupal_strtoupper($string);
+        return Unicode::strtoupper($string);
       case 'lower':
-        return drupal_strtolower($string);
+        return Unicode::strtolower($string);
       case 'ucfirst':
-        return drupal_strtoupper(drupal_substr($string, 0, 1)) . drupal_substr($string, 1);
+        return Unicode::strtoupper(Unicode::substr($string, 0, 1)) . Unicode::substr($string, 1);
       case 'ucwords':
         if ($multibyte == UNICODE_MULTIBYTE) {
           return mb_convert_case($string, MB_CASE_TITLE);
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 3259815..9a6f8f6 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Plugin\views\display;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\views\ViewExecutable;
 use \Drupal\views\Plugin\views\PluginBase;
 
@@ -999,7 +1000,7 @@ public function getArgumentsTokens() {
       // Use strip tags as there should never be HTML in the path.
       // However, we need to preserve special characters like " that
       // were removed by check_plain().
-      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(decode_entities($this->view->args[$count - 1])) : '';
+      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(Unicode::decodeEntities($this->view->args[$count - 1])) : '';
     }
 
     return $tokens;
@@ -1064,7 +1065,7 @@ public function optionsSummary(&$categories, &$options) {
       );
     }
 
-    $display_comment = check_plain(drupal_substr($this->getOption('display_comment'), 0, 10));
+    $display_comment = check_plain(Unicode::substr($this->getOption('display_comment'), 0, 10));
     $options['display_comment'] = array(
       'category' => 'other',
       'title' => t('Comment'),
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
index 5f20f4a..c0b36a7 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Plugin\views\field;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\views\Plugin\views\HandlerBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ViewExecutable;
@@ -1256,13 +1257,13 @@ function render_text($alter) {
         $more_link_text = $this->options['alter']['more_link_text'] ? $this->options['alter']['more_link_text'] : t('more');
         $more_link_text = strtr(filter_xss_admin($more_link_text), $tokens);
         $more_link_path = $this->options['alter']['more_link_path'];
-        $more_link_path = strip_tags(decode_entities(strtr($more_link_path, $tokens)));
+        $more_link_path = strip_tags(Unicode::decodeEntities(strtr($more_link_path, $tokens)));
 
         // Take sure that paths which was runned through url() does work as well.
         $base_path = base_path();
         // Checks whether the path starts with the base_path.
         if (strpos($more_link_path, $base_path) === 0) {
-          $more_link_path = drupal_substr($more_link_path, drupal_strlen($base_path));
+          $more_link_path = Unicode::substr($more_link_path, Unicode::strlen($base_path));
         }
 
         $more_link = l($more_link_text, $more_link_path, array('attributes' => array('class' => array('views-more-link'))));
@@ -1334,7 +1335,7 @@ function render_as_link($alter, $text, $tokens) {
       // Use strip tags as there should never be HTML in the path.
       // However, we need to preserve special characters like " that
       // were removed by check_plain().
-      $path = strip_tags(decode_entities(strtr($path, $tokens)));
+      $path = strip_tags(Unicode::decodeEntities(strtr($path, $tokens)));
 
       if (!empty($alter['path_case']) && $alter['path_case'] != 'none') {
         $path = $this->caseTransform($path, $this->options['alter']['path_case']);
@@ -1396,7 +1397,7 @@ function render_as_link($alter, $text, $tokens) {
     $alt = strtr($alter['alt'], $tokens);
     // Set the title attribute of the link only if it improves accessibility
     if ($alt && $alt != $text) {
-      $options['attributes']['title'] = decode_entities($alt);
+      $options['attributes']['title'] = Unicode::decodeEntities($alt);
     }
 
     $class = strtr($alter['link_class'], $tokens);
@@ -1482,7 +1483,7 @@ function get_render_tokens($item) {
       // Use strip tags as there should never be HTML in the path.
       // However, we need to preserve special characters like " that
       // were removed by check_plain().
-      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(decode_entities($this->view->args[$count - 1])) : '';
+      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(Unicode::decodeEntities($this->view->args[$count - 1])) : '';
     }
 
     // Get flattened set of tokens for any array depth in $_GET parameters.
@@ -1564,7 +1565,7 @@ function get_token_values_recursive(array $array, array $parent_keys = array())
       else {
         // Create a token key based on array element structure.
         $token_string = !empty($parent_keys) ? implode('_', $parent_keys) . '_' . $param : $param;
-        $tokens['%' . $token_string] = strip_tags(decode_entities($val));
+        $tokens['%' . $token_string] = strip_tags(Unicode::decodeEntities($val));
       }
     }
 
@@ -1651,8 +1652,8 @@ public function adminLabel($short = FALSE) {
    *   The trimmed string.
    */
   public static function trimText($alter, $value) {
-    if (drupal_strlen($value) > $alter['max_length']) {
-      $value = drupal_substr($value, 0, $alter['max_length']);
+    if (Unicode::strlen($value) > $alter['max_length']) {
+      $value = Unicode::substr($value, 0, $alter['max_length']);
       if (!empty($alter['word_boundary'])) {
         $regex = "(.*)\b.+";
         if (function_exists('mb_ereg')) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php
index a33ac65..cebe64b 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Links.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\views\Plugin\views\field;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * A abstract handler which provides a collection of links.
  *
@@ -67,7 +69,7 @@ protected function getLinks() {
       }
       // Make sure that tokens are replaced for this paths as well.
       $tokens = $this->get_render_tokens(array());
-      $path = strip_tags(decode_entities(strtr($path, $tokens)));
+      $path = strip_tags(Unicode::decodeEntities(strtr($path, $tokens)));
 
       $links[$field] = array(
         'href' => $path,
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php
index e93b0de..70c795b 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php
@@ -10,6 +10,7 @@
 use Drupal\views\Plugin\views\HandlerBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\Component\Annotation\Plugin;
+use Drupal\Component\Utility\Unicode;
 use Drupal\views\ViewExecutable;
 
 /**
@@ -1161,7 +1162,7 @@ function prepare_filter_select_options(&$options) {
         $this->prepare_filter_select_options($options[$value]->option);
       }
       else {
-        $options[$value] = strip_tags(decode_entities($label));
+        $options[$value] = strip_tags(Unicode::decodeEntities($label));
       }
     }
   }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/InOperator.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/InOperator.php
index 5508f66..ca9c4ca 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/InOperator.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/InOperator.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Plugin\views\filter;
 
 use Drupal\Component\Annotation\Plugin;
+use Drupal\Component\Utility\Unicode;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ViewExecutable;
 
@@ -357,7 +358,7 @@ public function adminSummary() {
           if ($values !== '') {
             $values .= ', ';
           }
-          if (drupal_strlen($values) > 8) {
+          if (Unicode::strlen($values) > 8) {
             $values .= '...';
             break;
           }
diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
index 7e8fc93..4a50b61 100644
--- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Tests;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 use Drupal\views\ViewExecutable;
 
@@ -47,7 +48,7 @@ protected function setUp() {
     $this->vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
       'description' => $this->randomName(),
-      'vid' => drupal_strtolower($this->randomName()),
+      'vid' => Unicode::strtolower($this->randomName()),
       'langcode' => LANGUAGE_NOT_SPECIFIED,
       'help' => '',
       'nodes' => array('page' => 'page'),
@@ -56,7 +57,7 @@ protected function setUp() {
     $this->vocabulary->save();
 
     // Setup a field and instance.
-    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_name = Unicode::strtolower($this->randomName());
     $this->field = array(
       'field_name' => $this->field_name,
       'type' => 'taxonomy_term_reference',
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php
index 9d8c66e..34037b2 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\views\Tests\Handler;
 
+use Drupal\Component\Utility\Unicode;
+
 /**
  * Tests fields from within a UI.
  *
@@ -227,13 +229,13 @@ public function testAlterUrl() {
       $expected_result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
       $alter['path'] = 'node/123?foo=bar&bar=baz';
       $result = $id_field->theme($row);
-      $this->assertSubString(decode_entities($result), decode_entities($expected_result));
+      $this->assertSubString(Unicode::decodeEntities($result), Unicode::decodeEntities($expected_result));
 
       $expected_result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
       $alter['path'] = 'node/123?foo#bar';
       $result = $id_field->theme($row);
       // @fixme: The actual result is node/123?foo#bar so views has a bug here.
-      // $this->assertSubStringExists(decode_entities($result), decode_entities($expected_result));
+      // $this->assertSubStringExists(Unicode::decodeEntities($result), Unicode::decodeEntities($expected_result));
 
       $expected_result = url('<front>', array('absolute' => $absolute));
       $alter['path'] = '<front>';
@@ -471,7 +473,7 @@ public function testTextRendering() {
     // Tests for simple trimming by string length.
     $row->views_test_data_name = $this->randomName(8);
     $name_field->options['alter']['max_length'] = 5;
-    $trimmed_name = drupal_substr($row->views_test_data_name, 0, 5);
+    $trimmed_name = Unicode::substr($row->views_test_data_name, 0, 5);
 
     $output = $name_field->advanced_render($row);
     $this->assertSubString($output, $trimmed_name, format_string('Make sure the trimmed output (!trimmed) appears in the rendered output (!output).', array('!trimmed' => $trimmed_name, '!output' => $output)));
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index 3246dde..8436e9a 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -8,6 +8,7 @@
 namespace Drupal\views;
 
 use Symfony\Component\HttpFoundation\Response;
+use Drupal\Component\Utility\Unicode;
 use Drupal\views\Plugin\Core\Entity\View;
 
 /**
@@ -883,7 +884,7 @@ protected function _buildArguments() {
 
         // Add this argument's substitution
         $substitutions['%' . ($position + 1)] = $arg_title;
-        $substitutions['!' . ($position + 1)] = strip_tags(decode_entities($arg));
+        $substitutions['!' . ($position + 1)] = strip_tags(Unicode::decodeEntities($arg));
 
         // Since we're really generating the breadcrumb for the item above us,
         // check the default action of this argument.
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 44f48f6..c419710 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -5,6 +5,7 @@
  * Preprocessors and helper functions to make theming easier.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Template\Attribute;
 use Drupal\views\ViewExecutable;
 
@@ -874,7 +875,7 @@ function template_preprocess_views_view_rss(&$vars) {
   // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
   // We strip all HTML tags, but need to prevent double encoding from properly
   // escaped source data (such as &amp becoming &amp;amp;).
-  $vars['description'] = check_plain(decode_entities(strip_tags($style->get_description())));
+  $vars['description'] = check_plain(Unicode::decodeEntities(strip_tags($style->get_description())));
 
   if ($view->display_handler->getOption('sitename_title')) {
     $title = $config->get('name');
diff --git a/core/modules/views/views_ui/admin.inc b/core/modules/views/views_ui/admin.inc
index abf75b0..92fb492 100644
--- a/core/modules/views/views_ui/admin.inc
+++ b/core/modules/views/views_ui/admin.inc
@@ -5,6 +5,7 @@
  * Provides the Views' administrative interface.
  */
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\views\ViewExecutable;
 
@@ -408,14 +409,14 @@ function views_ui_build_form_url($form_state) {
 }
 
 function _views_sort_types($a, $b) {
-  $a_group = drupal_strtolower($a['group']);
-  $b_group = drupal_strtolower($b['group']);
+  $a_group = Unicode::strtolower($a['group']);
+  $b_group = Unicode::strtolower($b['group']);
   if ($a_group != $b_group) {
     return $a_group < $b_group ? -1 : 1;
   }
 
-  $a_title = drupal_strtolower($a['title']);
-  $b_title = drupal_strtolower($b['title']);
+  $a_title = Unicode::strtolower($a['title']);
+  $b_title = Unicode::strtolower($b['title']);
   if ($a_title != $b_title) {
     return $a_title < $b_title ? -1 : 1;
   }
diff --git a/core/modules/views/views_ui/views_ui.module b/core/modules/views/views_ui/views_ui.module
index 4c3ae95..8d322ea 100644
--- a/core/modules/views/views_ui/views_ui.module
+++ b/core/modules/views/views_ui/views_ui.module
@@ -11,6 +11,7 @@
 use Drupal\views\Analyzer;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Implements hook_menu().
@@ -481,8 +482,8 @@ function views_ui_views_analyze(ViewExecutable $view) {
  * This is often used in the UI to ensure long strings fit.
  */
 function views_ui_truncate($string, $length) {
-  if (drupal_strlen($string) > $length) {
-    $string = drupal_substr($string, 0, $length);
+  if (Unicode::strlen($string) > $length) {
+    $string = Unicode::substr($string, 0, $length);
     $string .= '...';
   }
 
diff --git a/core/update.php b/core/update.php
index d24d6e0..fda01e5 100644
--- a/core/update.php
+++ b/core/update.php
@@ -408,7 +408,6 @@ function update_check_requirements($skip_warnings = FALSE) {
 require_once DRUPAL_ROOT . '/core/includes/update.inc';
 require_once DRUPAL_ROOT . '/core/includes/common.inc';
 require_once DRUPAL_ROOT . '/core/includes/file.inc';
-require_once DRUPAL_ROOT . '/core/includes/unicode.inc';
 require_once DRUPAL_ROOT . '/core/includes/schema.inc';
 update_prepare_d8_bootstrap();
 
