diff --git a/src/ViewHelpers/Format/CaseViewHelper.php b/src/ViewHelpers/Format/CaseViewHelper.php
new file mode 100644
index 000000000..2dc697caa
--- /dev/null
+++ b/src/ViewHelpers/Format/CaseViewHelper.php
@@ -0,0 +1,151 @@
+Some Text with miXed case
+ *
+ * Output::
+ *
+ * SOME TEXT WITH MIXED CASE
+ *
+ * Example with given mode
+ * -----------------------
+ *
+ * ::
+ *
+ * someString
+ *
+ * Output::
+ *
+ * SomeString
+ */
+final class CaseViewHelper extends AbstractViewHelper
+{
+ use CompileWithRenderStatic;
+
+ /**
+ * Directs the input string being converted to "lowercase"
+ */
+ private const CASE_LOWER = 'lower';
+
+ /**
+ * Directs the input string being converted to "UPPERCASE"
+ */
+ private const CASE_UPPER = 'upper';
+
+ /**
+ * Directs the input string being converted to "Capital case"
+ */
+ private const CASE_CAPITAL = 'capital';
+
+ /**
+ * Directs the input string being converted to "unCapital case"
+ */
+ private const CASE_UNCAPITAL = 'uncapital';
+
+ /**
+ * Directs the input string being converted to "Capital Case For Each Word"
+ */
+ private const CASE_CAPITAL_WORDS = 'capitalWords';
+
+ /**
+ * Output is escaped already. We must not escape children, to avoid double encoding.
+ *
+ * @var bool
+ */
+ protected $escapeChildren = false;
+
+ public function initializeArguments(): void
+ {
+ $this->registerArgument('value', 'string', 'The input value. If not given, the evaluated child nodes will be used.', false);
+ $this->registerArgument('mode', 'string', 'The case to apply, must be one of this\' CASE_* constants. Defaults to uppercase application.', false, self::CASE_UPPER);
+ }
+
+ /**
+ * Changes the case of the input string
+ * @throws Exception
+ */
+ public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string
+ {
+ $value = $arguments['value'];
+ $mode = $arguments['mode'];
+
+ if ($value === null) {
+ $value = (string)$renderChildrenClosure();
+ }
+
+ switch ($mode) {
+ case self::CASE_LOWER:
+ $output = mb_strtolower($value, 'utf-8');
+ break;
+ case self::CASE_UPPER:
+ $output = mb_strtoupper($value, 'utf-8');
+ break;
+ case self::CASE_CAPITAL:
+ $firstChar = mb_substr($value, 0, 1, 'utf-8');
+ $firstChar = mb_strtoupper($firstChar, 'utf-8');
+ $remainder = mb_substr($value, 1, null, 'utf-8');
+ $output = $firstChar . $remainder;
+ break;
+ case self::CASE_UNCAPITAL:
+ $firstChar = mb_substr($value, 0, 1, 'utf-8');
+ $firstChar = mb_strtolower($firstChar, 'utf-8');
+ $remainder = mb_substr($value, 1, null, 'utf-8');
+ $output = $firstChar . $remainder;
+ break;
+ case self::CASE_CAPITAL_WORDS:
+ $output = mb_convert_case($value, MB_CASE_TITLE, 'utf-8');
+ break;
+ default:
+ throw new Exception('The case mode "' . $mode . '" supplied to Fluid\'s format.case ViewHelper is not supported.', 1358349150);
+ }
+
+ return $output;
+ }
+}
diff --git a/tests/Functional/ViewHelpers/Format/CaseViewHelperTest.php b/tests/Functional/ViewHelpers/Format/CaseViewHelperTest.php
new file mode 100644
index 000000000..e9b1a0319
--- /dev/null
+++ b/tests/Functional/ViewHelpers/Format/CaseViewHelperTest.php
@@ -0,0 +1,96 @@
+ [
+ '',
+ '',
+ ],
+ 'value from child, uppercase default' => [
+ 'foob4r',
+ 'FOOB4R',
+ ],
+ 'simple value' => [
+ '',
+ 'FOO',
+ ],
+ 'mode lower' => [
+ '',
+ 'foob4r',
+ ],
+ 'mode upper' => [
+ '',
+ 'FOOB4R',
+ ],
+ 'mode capital' => [
+ '',
+ 'Foo bar',
+ ],
+ 'mode uncapital' => [
+ '',
+ 'fOO Bar',
+ ],
+ 'special chars 1' => [
+ '',
+ 'SMØRREBRØD',
+ ],
+ 'special chars 2' => [
+ '',
+ 'Smørrebrød',
+ ],
+ 'special chars 3' => [
+ '',
+ 'RÖMTÖMTÖMTÖM',
+ ],
+ 'special chars 4' => [
+ '',
+ 'ἝΛΛΆΣ Α Ω',
+ ],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider renderConvertsAValueDataProvider
+ */
+ public function renderConvertsAValue(string $template, string $expected): void
+ {
+ $view = new TemplateView();
+ $view->getRenderingContext()->setCache(self::$cache);
+ $view->getRenderingContext()->getTemplatePaths()->setTemplateSource($template);
+ self::assertSame($expected, $view->render());
+
+ $view = new TemplateView();
+ $view->getRenderingContext()->setCache(self::$cache);
+ $view->getRenderingContext()->getTemplatePaths()->setTemplateSource($template);
+ self::assertSame($expected, $view->render());
+ }
+
+ /**
+ * @test
+ */
+ public function viewHelperThrowsExceptionIfIncorrectModeIsGiven(): void
+ {
+ $this->expectException(Exception::class);
+ $this->expectExceptionCode(1358349150);
+ $view = new TemplateView();
+ $view->getRenderingContext()->getTemplatePaths()->setTemplateSource('');
+ $view->render();
+ }
+}