diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
index d5221dacfc69de00759058c78a2b5ef02e4e657d..ab2e7a0de9659750403ab595eb75dd74176ca288 100644
--- a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
+++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
@@ -8,8 +8,9 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Locale\CountryManagerInterface;
 use Drupal\Core\Site\Settings;
-use Drupal\user\UserStorageInterface;
 use Drupal\user\UserInterface;
+use Drupal\user\UserStorageInterface;
+use Drupal\user\UserNameValidator;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -67,13 +68,26 @@ class SiteConfigureForm extends ConfigFormBase {
    *   The module installer.
    * @param \Drupal\Core\Locale\CountryManagerInterface $country_manager
    *   The country manager.
+   * @param \Drupal\user\UserNameValidator|null $userNameValidator
+   *   The user validator.
    */
-  public function __construct($root, $site_path, UserStorageInterface $user_storage, ModuleInstallerInterface $module_installer, CountryManagerInterface $country_manager) {
+  public function __construct(
+    $root,
+    $site_path,
+    UserStorageInterface $user_storage,
+    ModuleInstallerInterface $module_installer,
+    CountryManagerInterface $country_manager,
+    protected ?UserNameValidator $userNameValidator = NULL,
+  ) {
     $this->root = $root;
     $this->sitePath = $site_path;
     $this->userStorage = $user_storage;
     $this->moduleInstaller = $module_installer;
     $this->countryManager = $country_manager;
+    if (!$userNameValidator) {
+      @\trigger_error('Calling ' . __METHOD__ . ' without the $userNameValidator argument is deprecated in drupal:10.3.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3431205', E_USER_DEPRECATED);
+      $this->userNameValidator = \Drupal::service('user.name_validator');
+    }
   }
 
   /**
@@ -85,7 +99,8 @@ public static function create(ContainerInterface $container) {
       $container->getParameter('site.path'),
       $container->get('entity_type.manager')->getStorage('user'),
       $container->get('module_installer'),
-      $container->get('country_manager')
+      $container->get('country_manager'),
+      $container->get('user.name_validator'),
     );
   }
 
@@ -253,8 +268,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
-    if ($error = user_validate_name($form_state->getValue(['account', 'name']))) {
-      $form_state->setErrorByName('account][name', $error);
+    $violations = $this->userNameValidator->validateName($form_state->getValue(['account', 'name']));
+    if ($violations->count() > 0) {
+      $form_state->setErrorByName('account][name', $violations[0]->getMessage());
     }
   }
 
diff --git a/core/modules/user/src/Form/UserPasswordForm.php b/core/modules/user/src/Form/UserPasswordForm.php
index db5efc6180bfcd9b322eb3fbe3430d51c8eb0950..5b28f7d9f2439f3f285d66f986108796b03a757a 100644
--- a/core/modules/user/src/Form/UserPasswordForm.php
+++ b/core/modules/user/src/Form/UserPasswordForm.php
@@ -4,7 +4,7 @@
 
 use Drupal\Component\Utility\EmailValidatorInterface;
 use Drupal\Core\Config\ConfigFactory;
-use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
 use Drupal\Core\Flood\FloodInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -13,6 +13,7 @@
 use Drupal\Core\TypedData\TypedDataManagerInterface;
 use Drupal\user\UserInterface;
 use Drupal\user\UserStorageInterface;
+use Drupal\user\UserNameValidator;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -24,6 +25,15 @@
  */
 class UserPasswordForm extends FormBase {
 
+  use DeprecatedServicePropertyTrait;
+
+  /**
+   * The deprecated properties.
+   */
+  protected array $deprecatedProperties = [
+    'typedDataManager' => 'typed_data_manager',
+  ];
+
   /**
    * The user storage.
    *
@@ -45,13 +55,6 @@ class UserPasswordForm extends FormBase {
    */
   protected $flood;
 
-  /**
-   * The typed data manager.
-   *
-   * @var \Drupal\Core\TypedData\TypedDataManagerInterface
-   */
-  protected $typedDataManager;
-
   /**
    * The email validator service.
    *
@@ -70,18 +73,28 @@ class UserPasswordForm extends FormBase {
    *   The config factory.
    * @param \Drupal\Core\Flood\FloodInterface $flood
    *   The flood service.
-   * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
-   *   The typed data manager.
+   * @param \Drupal\user\UserNameValidator|\Drupal\Core\TypedData\TypedDataManagerInterface $userNameValidator
+   *   The user validator service.
    * @param \Drupal\Component\Utility\EmailValidatorInterface $email_validator
    *   The email validator service.
    */
-  public function __construct(UserStorageInterface $user_storage, LanguageManagerInterface $language_manager, ConfigFactory $config_factory, FloodInterface $flood, TypedDataManagerInterface $typed_data_manager, EmailValidatorInterface $email_validator) {
+  public function __construct(
+    UserStorageInterface $user_storage,
+    LanguageManagerInterface $language_manager,
+    ConfigFactory $config_factory,
+    FloodInterface $flood,
+    protected UserNameValidator|TypedDataManagerInterface $userNameValidator,
+    EmailValidatorInterface $email_validator,
+  ) {
     $this->userStorage = $user_storage;
     $this->languageManager = $language_manager;
     $this->configFactory = $config_factory;
     $this->flood = $flood;
-    $this->typedDataManager = $typed_data_manager;
     $this->emailValidator = $email_validator;
+    if (!$userNameValidator instanceof UserNameValidator) {
+      @\trigger_error('Passing $userNameValidator as \Drupal\Core\TypedData\TypedDataManagerInterface to ' . __METHOD__ . ' () is deprecated in drupal:10.3.0 and is removed in drupal:10.0.0. Pass a Drupal\user\UserValidator instead. See https://www.drupal.org/node/3431205', E_USER_DEPRECATED);
+      $this->userNameValidator = \Drupal::service('user.name_validator');
+    }
   }
 
   /**
@@ -93,8 +106,8 @@ public static function create(ContainerInterface $container) {
       $container->get('language_manager'),
       $container->get('config.factory'),
       $container->get('flood'),
-      $container->get('typed_data_manager'),
-      $container->get('email.validator')
+      $container->get('user.name_validator'),
+      $container->get('email.validator'),
     );
   }
 
@@ -161,11 +174,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
     $this->flood->register('user.password_request_ip', $flood_config->get('ip_window'));
     // First, see if the input is possibly valid as a username.
     $name = trim($form_state->getValue('name'));
-    $definition = BaseFieldDefinition::create('string')
-      ->addConstraint('UserName', []);
-    $data = $this->typedDataManager->create($definition);
-    $data->setValue($name);
-    $violations = $data->validate();
+    $violations = $this->userNameValidator->validateName($name);
     // Usernames have a maximum length shorter than email addresses. Only print
     // this error if the input is not valid as a username or email address.
     if ($violations->count() > 0 && !$this->emailValidator->isValid($name)) {
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
index 46d919ae9a9b9e28e6a8c087bb1ad748e7d0d440..a0e0af8eb1448f81cc67cdde3f338b4b4608a625 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\user\Plugin\Validation\Constraint;
 
+use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\user\UserInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
@@ -15,11 +16,11 @@ class UserNameConstraintValidator extends ConstraintValidator {
    * {@inheritdoc}
    */
   public function validate($items, Constraint $constraint): void {
-    if (!isset($items) || !$items->value) {
+    if (empty($items) || ($items instanceof FieldItemListInterface && $items->isEmpty())) {
       $this->context->addViolation($constraint->emptyMessage);
       return;
     }
-    $name = $items->first()->value;
+    $name = $items instanceof FieldItemListInterface ? $items->first()->value : $items;
     if (str_starts_with($name, ' ')) {
       $this->context->addViolation($constraint->spaceBeginMessage);
     }
diff --git a/core/modules/user/src/UserNameValidator.php b/core/modules/user/src/UserNameValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..03a6cbcd4fdf569fbb55d3d7fb1ff52bdb203811
--- /dev/null
+++ b/core/modules/user/src/UserNameValidator.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\user;
+
+use Drupal\Core\Validation\BasicRecursiveValidatorFactory;
+use Drupal\Core\Validation\ConstraintManager;
+use Symfony\Component\Validator\ConstraintViolationListInterface;
+
+/**
+ * Provides a username validator.
+ *
+ * This validator re-uses the UserName constraint plugin but does not require a
+ * User entity.
+ */
+class UserNameValidator {
+
+  public function __construct(
+    protected readonly BasicRecursiveValidatorFactory $validatorFactory,
+    protected readonly ConstraintManager $constraintManager,
+  ) {}
+
+  /**
+   * Validates a user name.
+   *
+   * @return \Symfony\Component\Validator\ConstraintViolationListInterface
+   *   The list of constraint violations.
+   */
+  public function validateName(string $name): ConstraintViolationListInterface {
+    $validator = $this->validatorFactory->createValidator();
+    $constraint = $this->constraintManager->create('UserName', []);
+    return $validator->validate($name, $constraint);
+  }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/UserNameValidatorTest.php b/core/modules/user/tests/src/Kernel/UserNameValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..14959d2139f50252ede30ef5384f3b79422f9974
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/UserNameValidatorTest.php
@@ -0,0 +1,96 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\user\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\user\UserInterface;
+use Drupal\user\UserNameValidator;
+
+/**
+ * Verify that user validity checks behave as designed.
+ *
+ * @group user
+ */
+class UserNameValidatorTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['user'];
+
+  /**
+   * The user validator under test.
+   */
+  protected UserNameValidator $userValidator;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    $this->userValidator = $this->container->get('user.name_validator');
+  }
+
+  /**
+   * Tests valid user name validation.
+   *
+   * @dataProvider validUsernameProvider
+   */
+  public function testValidUsernames($name): void {
+    $violations = $this->userValidator->validateName($name);
+    $this->assertEmpty($violations);
+  }
+
+  /**
+   * Tests invalid user name validation.
+   *
+   * @dataProvider invalidUserNameProvider
+   */
+  public function testInvalidUsernames($name, $expectedMessage): void {
+    $violations = $this->userValidator->validateName($name);
+    $this->assertNotEmpty($violations);
+    $this->assertEquals($expectedMessage, $violations[0]->getMessage());
+  }
+
+  /**
+   * Provides valid user names.
+   */
+  public static function validUsernameProvider(): array {
+    // cSpell:disable
+    return [
+      'lowercase' => ['foo'],
+      'uppercase' => ['FOO'],
+      'contains space' => ['Foo O\'Bar'],
+      'contains @' => ['foo@bar'],
+      'allow email' => ['foo@example.com'],
+      'allow invalid domain' => ['foo@-example.com'],
+      'allow special chars' => ['þòøÇߪř€'],
+      'allow plus' => ['foo+bar'],
+      'utf8 runes' => ['ᚠᛇᚻ᛫ᛒᛦᚦ'],
+    ];
+    // cSpell:enable
+  }
+
+  /**
+   * Provides invalid user names.
+   */
+  public static function invalidUserNameProvider(): array {
+    return [
+      'starts with space' => [' foo', 'The username cannot begin with a space.'],
+      'ends with space' => ['foo ', 'The username cannot end with a space.'],
+      'contains 2 spaces' => ['foo  bar', 'The username cannot contain multiple spaces in a row.'],
+      'empty string' => ['', 'You must enter a username.'],
+      'invalid chars' => ['foo/', 'The username contains an illegal character.'],
+      // NULL.
+      'contains chr(0)' => ['foo' . chr(0) . 'bar', 'The username contains an illegal character.'],
+      // CR.
+      'contains chr(13)' => ['foo' . chr(13) . 'bar', 'The username contains an illegal character.'],
+      'excessively long' => [str_repeat('x', UserInterface::USERNAME_MAX_LENGTH + 1),
+        'The username xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx is too long: it must be 60 characters or less.',
+      ],
+    ];
+  }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/UserValidationTest.php b/core/modules/user/tests/src/Kernel/UserValidationTest.php
index f2fc70bca60af91e2d5b4a7751ded6ab9b1656b2..94f44c3ce37ef9c20d49e8e89d04d032dd5bc1b5 100644
--- a/core/modules/user/tests/src/Kernel/UserValidationTest.php
+++ b/core/modules/user/tests/src/Kernel/UserValidationTest.php
@@ -38,6 +38,8 @@ protected function setUp(): void {
 
   /**
    * Tests user name validation.
+   *
+   * @group legacy
    */
   public function testUsernames() {
     // cSpell:disable
@@ -66,6 +68,7 @@ public function testUsernames() {
       'foo' . chr(13) . 'bar'  => ['Invalid username containing chr(13)', 'assertNotNull'],
       str_repeat('x', UserInterface::USERNAME_MAX_LENGTH + 1) => ['Invalid excessively long username', 'assertNotNull'],
     ];
+    $this->expectDeprecation('user_validate_name() is deprecated in drupal:10.3.0 and is removed from drupal:12.0.0. Use \Drupal\user\UserValidator::validateName() instead. See https://www.drupal.org/node/3431205');
     // cSpell:enable
     foreach ($test_cases as $name => $test_case) {
       [$description, $test] = $test_case;
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 3f3d21fee0e3fd8f5b8b8c9a96ad5384a26d39a0..60244c932b33174e3284b342c623ab8d03c47e61 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -14,7 +14,6 @@
 use Drupal\Core\Batch\BatchBuilder;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -206,13 +205,15 @@ function user_load_by_name($name) {
  * @return string|null
  *   A translated violation message if the name is invalid or NULL if the name
  *   is valid.
+ *
+ * @deprecated in drupal:10.3.0 and is removed from drupal:12.0.0. Use
+ *   \Drupal\user\UserValidator::validateName() instead.
+ *
+ * @see https://www.drupal.org/node/3431205
  */
 function user_validate_name($name) {
-  $definition = BaseFieldDefinition::create('string')
-    ->addConstraint('UserName', []);
-  $data = \Drupal::typedDataManager()->create($definition);
-  $data->setValue($name);
-  $violations = $data->validate();
+  @trigger_error(__METHOD__ . '() is deprecated in drupal:10.3.0 and is removed from drupal:12.0.0. Use \Drupal\user\UserValidator::validateName() instead. See https://www.drupal.org/node/3431205', E_USER_DEPRECATED);
+  $violations = \Drupal::service('user.name_validator')->validateName($name);
   if (count($violations) > 0) {
     return $violations[0]->getMessage();
   }
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index fb0389dd973e50173ef3c7468b57b3d932f1a0e7..e2ec9cc3a0673d6658a52546ec0c0edc44f22aad 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -73,3 +73,7 @@ services:
   user.module_permissions_link_helper:
     class: Drupal\user\ModulePermissionsLinkHelper
     arguments: ['@user.permissions', '@access_manager', '@extension.list.module']
+  user.name_validator:
+    class: Drupal\user\UserNameValidator
+    arguments: ['@validation.basic_recursive_validator_factory', '@validation.constraint']
+  Drupal\user\UserNameValidator: '@user.name_validator'