diff --git a/typo3/sysext/core/Classes/Domain/Page.php b/typo3/sysext/core/Classes/Domain/Page.php
new file mode 100644
index 0000000000000000000000000000000000000000..dec1ab0ae2dd74f5420c43fbd6af05a7a642e729
--- /dev/null
+++ b/typo3/sysext/core/Classes/Domain/Page.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Domain;
+
+/**
+ * @internal not part of public API, as this needs to be streamlined and proven
+ */
+class Page implements \ArrayAccess
+{
+    use PropertyTrait;
+
+    protected array $specialPropertyNames = [
+        '_language',
+        '_LOCALIZED_UID',
+        '_MP_PARAM',
+        '_ORIG_uid',
+        '_ORIG_pid',
+        '_SHORTCUT_ORIGINAL_PAGE_UID',
+        '_PAGES_OVERLAY',
+        '_PAGES_OVERLAY_UID',
+        '_PAGES_OVERLAY_LANGUAGE',
+        '_PAGES_OVERLAY_REQUESTEDLANGUAGE',
+    ];
+
+    protected array $specialProperties = [];
+
+    public function __construct(array $properties)
+    {
+        foreach ($properties as $propertyName => $propertyValue) {
+            if (isset($this->specialPropertyNames[$propertyName])) {
+                $this->specialProperties[$propertyName] = $propertyValue;
+            } else {
+                $this->properties[$propertyName] = $propertyValue;
+            }
+        }
+    }
+
+    public function getLanguageId(): int
+    {
+        return $this->specialProperties['_language'] ?? $this->specialProperties['_PAGES_OVERLAY_LANGUAGE'] ?? $this->properties['sys_language_uid'];
+    }
+
+    public function getPageId(): int
+    {
+        $pageId = isset($this->properties['l10n_parent']) && $this->properties['l10n_parent'] > 0 ? $this->properties['l10n_parent'] : $this->properties['uid'];
+        return (int)$pageId;
+    }
+
+    public function toArray(): array
+    {
+        return $this->properties;
+    }
+}
diff --git a/typo3/sysext/core/Classes/Domain/PropertyTrait.php b/typo3/sysext/core/Classes/Domain/PropertyTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..bbf0ab3765d4e81b1afaa1358af421827ec5ed6c
--- /dev/null
+++ b/typo3/sysext/core/Classes/Domain/PropertyTrait.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Domain;
+
+/**
+ * @internal not part of public API, as this needs to be streamlined and proven
+ */
+trait PropertyTrait
+{
+    /**
+     * @var array<string, mixed>
+     */
+    protected array $properties = [];
+
+    #[\ReturnTypeWillChange]
+    public function offsetExists($offset)
+    {
+        return isset($this->properties[$offset]);
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetGet($offset)
+    {
+        return $this->properties[$offset] ?? null;
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetSet($offset, $value)
+    {
+        $this->properties[$offset] = $value;
+    }
+
+    #[\ReturnTypeWillChange]
+    public function offsetUnset($offset)
+    {
+        unset($this->properties[$offset]);
+    }
+}
diff --git a/typo3/sysext/core/Classes/Routing/PageRouter.php b/typo3/sysext/core/Classes/Routing/PageRouter.php
index 56ec9d393bb253887a3e8503d86726a57b3b2298..60b1c4b0c09351c16322019d6ddce886e022d86a 100644
--- a/typo3/sysext/core/Classes/Routing/PageRouter.php
+++ b/typo3/sysext/core/Classes/Routing/PageRouter.php
@@ -24,6 +24,7 @@ use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 use Symfony\Component\Routing\RequestContext;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\LanguageAspectFactory;
+use TYPO3\CMS\Core\Domain\Page;
 use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
 use TYPO3\CMS\Core\Http\Uri;
@@ -224,9 +225,9 @@ class PageRouter implements RouterInterface
     }
 
     /**
-     * API for generating a page where the $route parameter is typically an array (page record) or the page ID
+     * API for generating a page uri where the $route parameter is typically an array (a page record) or the page ID
      *
-     * @param array|string|int $route
+     * @param array|string|int|Page $route
      * @param array $parameters an array of query parameters which can be built into the URI path, also consider the special handling of "_language"
      * @param string $fragment additional #my-fragment part
      * @param string $type see the RouterInterface for possible types
@@ -249,7 +250,9 @@ class PageRouter implements RouterInterface
         }
 
         $pageId = 0;
-        if (is_array($route)) {
+        if ($route instanceof Page) {
+            $pageId = $route->getPageId();
+        } elseif (is_array($route)) {
             $pageId = (int)$route['uid'];
         } elseif (is_scalar($route)) {
             $pageId = (int)$route;
@@ -258,7 +261,19 @@ class PageRouter implements RouterInterface
         $context = clone $this->context;
         $context->setAspect('language', LanguageAspectFactory::createFromSiteLanguage($language));
         $pageRepository = GeneralUtility::makeInstance(PageRepository::class, $context);
-        $page = $pageRepository->getPage($pageId, true);
+
+        if ($route instanceof Page) {
+            $page = $route->toArray();
+        } elseif (is_array($route)
+            // Check 3rd party input $route for basic requirements
+            && isset($route['uid'], $route['sys_language_uid'], $route['l10n_parent'], $route['slug'])
+            && (int)$route['sys_language_uid'] === $language->getLanguageId()
+            && ((int)$route['l10n_parent'] === 0 || ($route['_PAGES_OVERLAY'] ?? false))
+        ) {
+            $page = $route;
+        } else {
+            $page = $pageRepository->getPage($pageId, true);
+        }
         $pagePath = $page['slug'] ?? '';
 
         if ($parameters['MP'] ?? '') {
diff --git a/typo3/sysext/core/Classes/Routing/RouterInterface.php b/typo3/sysext/core/Classes/Routing/RouterInterface.php
index 800a115315a60b4b3c938c9e9a375bb80120d19b..fb0ff3f49903a405cf966b7c76ba06228cb86738 100644
--- a/typo3/sysext/core/Classes/Routing/RouterInterface.php
+++ b/typo3/sysext/core/Classes/Routing/RouterInterface.php
@@ -47,7 +47,7 @@ interface RouterInterface
     /**
      * Builds a URI based on the $route and the given parameters.
      *
-     * @param string|array|int $route either the route name, or for pages it is usually the array of a page record, or the page ID
+     * @param string|array|int|\ArrayAccess $route either the route name, or for pages it is usually the array of a page record, or the page ID
      * @param array $parameters query parameters, specially reserved parameters are usually prefixed with "_"
      * @param string $fragment the section/fragment www.example.com/page/#fragment, WITHOUT the hash
      * @param string $type see the constants above.
diff --git a/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php b/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
index cf0515deb6b5f3fb4f7e55dfc7719687e0471aaa..9706abda2bac75aff9e0ef28c20479e78cdf08a3 100644
--- a/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
+++ b/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Context\LanguageAspectFactory;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
+use TYPO3\CMS\Core\Domain\Page;
 use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 use TYPO3\CMS\Core\Exception\Page\RootLineException;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
@@ -480,6 +481,7 @@ class PageLinkBuilder extends AbstractTypolinkBuilder
 
         $targetPageId = (int)($page['l10n_parent'] > 0 ? $page['l10n_parent'] : $page['uid']);
         $queryParameters['_language'] = $siteLanguageOfTargetPage;
+        $pageObject = new Page($page);
 
         if ($fragment
             && $useAbsoluteUrl === false
@@ -493,7 +495,7 @@ class PageLinkBuilder extends AbstractTypolinkBuilder
         } else {
             try {
                 $uri = $siteOfTargetPage->getRouter()->generateUri(
-                    $targetPageId,
+                    $pageObject,
                     $queryParameters,
                     $fragment,
                     $useAbsoluteUrl ? RouterInterface::ABSOLUTE_URL : RouterInterface::ABSOLUTE_PATH