core-bundle/src/Routing/RouteProvider.php
private function sortRoutes(array &$routes, array $languages = null): void
    {
        // Convert languages array so key is language and value is priority
        if (null !== $languages) {
            foreach ($languages as &$language) {
                $language = str_replace('_', '-', $language);

                if (5 === \strlen($language)) {
                    $lng = substr($language, 0, 2);

                    // Append the language if only language plus dialect is given (see #430)
                    if (!\in_array($lng, $languages, true)) {
                        $languages[] = $lng;
                    }
                }
            }

            unset($language);

            $languages = array_flip(array_values($languages));
        }

        uasort($routes, static function (Route $a, Route $b) use ($languages, $routes) {
            $fallbackA = '.fallback' === substr(array_search($a, $routes, true), -9);
            $fallbackB = '.fallback' === substr(array_search($b, $routes, true), -9);

            if ($fallbackA && !$fallbackB) {
                return 1;
            }

            if ($fallbackB && !$fallbackA) {
                return -1;
            }

            if ('' !== $a->getHost() && '' === $b->getHost()) {
                return -1;
            }

            if ('' === $a->getHost() && '' !== $b->getHost()) {
                return 1;
            }

            /** @var PageModel $pageA */
            $pageA = $a->getDefault('pageModel');

            /** @var PageModel $pageB */
            $pageB = $b->getDefault('pageModel');

            // TODO Check if this is really necessary, as routes are generated from pages so pageModel is always there
            if (!$pageA instanceof PageModel || !$pageB instanceof PageModel) {
                return 0;
            }

            if ('root' !== $pageA->type && 'root' === $pageB->type) {
                return -1;
            }

            if ('root' === $pageA->type && 'root' !== $pageB->type) {
                return 1;
            }

            if (null !== $languages && $pageA->rootLanguage !== $pageB->rootLanguage) {
                $langA = $languages[$pageA->rootLanguage] ?? null;
                $langB = $languages[$pageB->rootLanguage] ?? null;

                if (null === $langA && null === $langB) {
                    if ($pageA->rootIsFallback) {
                        return -1;
                    }

                    if ($pageB->rootIsFallback) {
                        return 1;
                    }

                    return $pageA->rootSorting <=> $pageB->rootSorting;
                }

                if (null === $langA && null !== $langB) {
                    return 1;
                }

                if (null !== $langA && null === $langB) {
                    return -1;
                }

                return $langA < $langB ? -1 : 1;
            }

            return strnatcasecmp((string) $pageB->alias, (string) $pageA->alias);
        });
    }
private function sortRoutes(array &$routes, array $languages = null): void
    {
        // Convert languages array so key is language and value is priority
        if (null !== $languages) {
            foreach ($languages as &$language) {
                $language = str_replace('_', '-', $language);

                if (5 === \strlen($language)) {
                    $lng = substr($language, 0, 2);

                    // Append the language if only language plus dialect is given (see #430)
                    if (!\in_array($lng, $languages, true)) {
                        $languages[] = $lng;
                    }
                }
            }

            unset($language);

            $languages = array_flip(array_values($languages));
        }

        uasort($routes, static function (Route $a, Route $b) use ($languages, $routes) {
            $fallbackA = '.fallback' === substr(array_search($a, $routes, true), -9);
            $fallbackB = '.fallback' === substr(array_search($b, $routes, true), -9);

            if ($fallbackA && !$fallbackB) {
                return 1;
            }

            if ($fallbackB && !$fallbackA) {
                return -1;
            }

            if ('' !== $a->getHost() && '' === $b->getHost()) {
                return -1;
            }

            if ('' === $a->getHost() && '' !== $b->getHost()) {
                return 1;
            }

            /** @var PageModel $pageA */
            $pageA = $a->getDefault('pageModel');

            /** @var PageModel $pageB */
            $pageB = $b->getDefault('pageModel');

            // TODO Check if this is really necessary, as routes are generated from pages so pageModel is always there
            if (!$pageA instanceof PageModel || !$pageB instanceof PageModel) {
                return 0;
            }

            if ('root' !== $pageA->type && 'root' === $pageB->type) {
                return -1;
            }

            if ('root' === $pageA->type && 'root' !== $pageB->type) {
                return 1;
            }

            if (null !== $languages && $pageA->rootLanguage !== $pageB->rootLanguage) {
                $langA = $languages[$pageA->rootLanguage] ?? null;
                $langB = $languages[$pageB->rootLanguage] ?? null;

                if (null === $langA && null === $langB) {
                    if ($pageA->rootIsFallback) {
                        return -1;
                    }

                    if ($pageB->rootIsFallback) {
                        return 1;
                    }

                    return $pageA->rootSorting <=> $pageB->rootSorting;
                }

                if (null === $langA && null !== $langB) {
                    return 1;
                }

                if (null !== $langA && null === $langB) {
                    return -1;
                }

                return $langA < $langB ? -1 : 1;
            }

            return strnatcasecmp((string) $pageB->alias, (string) $pageA->alias);
        });
    }
Sorts routes so that the FinalMatcher will correctly resolve them.

1. The ones with hostname should come first, so the ones with empty host are only taken if no hostname matches
2. Root pages come last, so non-root pages with index alias (= identical path) match first
3. Root/Index pages must be sorted by accept language and fallback, so the best language matches first
4. Pages with longer alias (folder page) must come first to match if applicable