pub(crate) fn detect_locale(url: PathWithoutLocale, locales: &Locales) -> String {
    // If nothing matches, we'll use the default locale
    let mut locale = locales.default.clone();

    // We'll use `navigator.languages` to figure out the best locale, falling back
    // to `navigator.language` if necessary
    let navigator = web_sys::window().unwrap().navigator();
    let langs = navigator.languages().to_vec();
    if langs.is_empty() {
        // We'll fall back to `language`, which only gives us one locale to compare with
        // If that isn't supported, we'll automatically fall back to the default locale
        if let Some(lang) = navigator.language() {
            locale = match compare_locale(&lang, &locales.get_all()) {
                LocaleMatch::Exact(matched) | LocaleMatch::Language(matched) => matched,
                LocaleMatch::None => locales.default.to_string(),
            }
        }
    } else {
        // We'll match each language individually, remembering that any exact match is
        // preferable to a language-only match
        for cmp in langs {
            // We can reasonably assume that the user's locales are strings
            let cmp_str = cmp.as_string().unwrap();
            // As per RFC 4647, the first match (exact or language-only) is the one we'll
            // use
            if let LocaleMatch::Exact(matched) | LocaleMatch::Language(matched) =
                compare_locale(&cmp_str, &locales.get_all())
            {
                locale = matched;
                break;
            }
        }
    }

    // Figure out what the new localized route should be
    // This is complex because we need to strip away the base path
    // We use the pathname part of the URL because the base path getter gets the
    // pathname too
    let url = url.strip_suffix('/').unwrap_or(&url);
    let url = url.strip_prefix('/').unwrap_or(url);
    let url = format!("/{}", url);
    let base_path = get_path_prefix_client(); // We know this doesn't have a trailing slash
    let loc = url.strip_prefix(&base_path).unwrap_or(&url);
    // The location develops a leading slash during the base path stripping, so we
    // remove it (again)
    let loc = loc.strip_prefix('/').unwrap_or(loc);
    let new_loc = format!("{}/{}/{}", base_path, locale, loc);
    let new_loc = new_loc.strip_suffix('/').unwrap_or(&new_loc);

    // Unset the initial state variable so we perform subsequent renders correctly
    // This monstrosity is needed until `web-sys` adds a `.set()` method on `Window`
    Reflect::set(
        &JsValue::from(web_sys::window().unwrap()),
        &JsValue::from("__PERSEUS_INITIAL_STATE"),
        &JsValue::undefined(),
    )
    .unwrap();

    new_loc.to_string()
}
Detects which locale the user should be served and redirects appropriately.
This should only be used when the user navigates to a page like `/about`,
without a locale. This will only work on the client-side (needs access to
browser i18n settings). Any pages that direct to this should be explicitly
excluded from search engines (they don't show anything until redirected).
This is guided by [RFC 4647](https://www.rfc-editor.org/rfc/rfc4647.txt), but is not yet fully compliant (only supports `xx-XX` form locales).

Note that this does not actually redirect on its own, it merely provides an
argument for `sycamore_router::navigate_replace()`.
packages/perseus/src/i18n/locale_detector.rs