diff --git a/core/lib/Drupal/Component/Utility/Unicode.php b/core/lib/Drupal/Component/Utility/Unicode.php
index c5e0d82..b52f8a9 100644
--- a/core/lib/Drupal/Component/Utility/Unicode.php
+++ b/core/lib/Drupal/Component/Utility/Unicode.php
@@ -315,6 +315,40 @@ public static function ucfirst($text) {
   }
 
   /**
+   * Lowercase the first letter of a UTF-8 string.
+   *
+   * @param string $text
+   *   The string that will be converted.
+   *
+   * @return string
+   *   The string with the first letter as lowercase.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function lcfirst($text) {
+    // Note: no mbstring equivalent!
+    return static::strtolower(static::substr($text, 0, 1)) . static::substr($text, 1);
+  }
+
+  /**
+   * Capitalizes the first letter of each word in a UTF-8 string.
+   *
+   * @param string $text
+   *   The text that will be converted.
+   *
+   * @return string
+   *   The input $text with each word capitalized.
+   *
+   * @ingroup php_wrappers
+   */
+  public static function ucwords($text) {
+    $regex = '/(^|[' . Unicode::PREG_CLASS_WORD_BOUNDARY . '])([^' . Unicode::PREG_CLASS_WORD_BOUNDARY . '])/u';
+    return preg_replace_callback($regex, function(array $matches) {
+      return $matches[1] . static::strtoupper($matches[2]);
+    }, $text);
+  }
+
+  /**
    * 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
diff --git a/core/lib/Drupal/Core/Utility/Error.php b/core/lib/Drupal/Core/Utility/Error.php
index 6cdc7e3..b8e3dba 100644
--- a/core/lib/Drupal/Core/Utility/Error.php
+++ b/core/lib/Drupal/Core/Utility/Error.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\Unicode;
 
 /**
  * Drupal error utility class.
@@ -164,7 +165,7 @@ public static function formatBacktrace(array $backtrace) {
             $call['args'][] = is_string($arg) ? '\'' . Xss::filter($arg) . '\'' : $arg;
           }
           else {
-            $call['args'][] = ucfirst(gettype($arg));
+            $call['args'][] = Unicode::ucfirst(gettype($arg));
           }
         }
       }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index 6c6b350..91efc8f 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -8,6 +8,7 @@
 namespace Drupal\field\Plugin\views\field;
 
 use Drupal\Component\Utility\MapArray;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
@@ -602,7 +603,7 @@ public function buildGroupByForm(&$form, &$form_state) {
 
     $group_columns = array(
       'entity_id' => t('Entity ID'),
-    ) + MapArray::copyValuesToKeys(array_keys($this->field_info->getColumns()), 'ucfirst');
+    ) + MapArray::copyValuesToKeys(array_keys($this->field_info->getColumns()), 'Unicode::ucfirst');
 
     $form['group_column'] = array(
       '#type' => 'select',
@@ -612,7 +613,7 @@ public function buildGroupByForm(&$form, &$form_state) {
       '#options' => $group_columns,
     );
 
-    $options = MapArray::copyValuesToKeys(array('bundle', 'language', 'entity_type'), 'ucfirst');
+    $options = MapArray::copyValuesToKeys(array('bundle', 'language', 'entity_type'), 'Unicode::ucfirst');
 
     // Add on defined fields, noting that they're prefixed with the field name.
     $form['group_columns'] = array(
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 9590e1c..bc73829 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -263,14 +263,9 @@ protected function caseTransform($string, $option) {
       case 'lower':
         return drupal_strtolower($string);
       case 'ucfirst':
-        return drupal_strtoupper(drupal_substr($string, 0, 1)) . drupal_substr($string, 1);
+        return Unicode::ucfirst($string);
       case 'ucwords':
-        if (Unicode::getStatus() == Unicode::STATUS_MULTIBYTE) {
-          return mb_convert_case($string, MB_CASE_TITLE);
-        }
-        else {
-          return ucwords($string);
-        }
+        return Unicode::ucwords($string);
     }
   }
 
diff --git a/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php b/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
index ee45829..350fa25 100644
--- a/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
@@ -194,6 +194,70 @@ public function providerUcfirst() {
   }
 
   /**
+   * Tests Unicode::lcfirst().
+   *
+   * @dataProvider providerLcfirst
+   */
+  public function testLcfirst($text, $expected, $multibyte = FALSE) {
+    $status = $multibyte ? Unicode::STATUS_MULTIBYTE : Unicode::STATUS_SINGLEBYTE;
+    Unicode::setStatus($status);
+    $this->assertEquals($expected, Unicode::lcfirst($text));
+  }
+
+  /**
+   * Data provider for testLcfirst().
+   *
+   * @see testLcfirst()
+   *
+   * @return array
+   *   An array containing a string, its lowercase version and whether it should
+   *   be processed as multibyte.
+   */
+  public function providerLcfirst() {
+    return array(
+      array('tHe QUIcK bRoWn', 'tHe QUIcK bRoWn'),
+      array('FrançAIS is ÜBER-åwesome', 'françAIS is ÜBER-åwesome'),
+      array('Über', 'über'),
+      array('Åwesome', 'åwesome'),
+      // Add a multibyte string.
+      array('ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ', 'αΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ', TRUE),
+    );
+  }
+
+  /**
+   * Tests Unicode::ucwords().
+   *
+   * @dataProvider providerUcwords
+   */
+  public function testUcwords($text, $expected, $multibyte = FALSE) {
+    $status = $multibyte ? Unicode::STATUS_MULTIBYTE : Unicode::STATUS_SINGLEBYTE;
+    Unicode::setStatus($status);
+    $this->assertEquals($expected, Unicode::ucwords($text));
+  }
+
+  /**
+   * Data provider for testUcwords().
+   *
+   * @see testUcwords()
+   *
+   * @return array
+   *   An array containing a string, its capitalized version and whether it should
+   *   be processed as multibyte.
+   */
+  public function providerUcwords() {
+    return array(
+      array('tHe QUIcK bRoWn', 'THe QUIcK BRoWn'),
+      array('françAIS', 'FrançAIS'),
+      array('über', 'Über'),
+      array('åwesome', 'Åwesome'),
+      // Make sure we don't mangle extra spaces.
+      array('frànçAIS is  über-åwesome', 'FrànçAIS Is  Über-Åwesome'),
+      // Add a multibyte string.
+      array('σion', 'Σion', TRUE),
+    );
+  }
+
+  /**
    * Tests Unicode::strlen().
    *
    * @dataProvider providerStrlen
