mirror of
https://github.com/nocobase/nocobase.git
synced 2026-08-29 02:03:53 +08:00
fix(docs): language switcher generates incorrect URLs in single-language builds (#9233)
This commit is contained in:
+50
-4
@@ -134,11 +134,57 @@ export function NavMenuDivider() {
|
||||
export function NavLangs() {
|
||||
const { items, activeValue } = useLangsMenu();
|
||||
|
||||
// Language links must use plain <a> tags instead of Rspress's Link component,
|
||||
// because each language is built separately with its own base (e.g., /cn/).
|
||||
// Rspress's Link would prepend the current base to the href, producing
|
||||
// incorrect URLs like /cn/es/ instead of /es/.
|
||||
const customChildren = (
|
||||
<>
|
||||
{items.map(item => {
|
||||
const isActive = item.text === activeValue;
|
||||
return (
|
||||
<li
|
||||
key={item.text}
|
||||
className={cls(
|
||||
'rp-hover-group__item',
|
||||
isActive && 'rp-hover-group__item--active',
|
||||
)}
|
||||
>
|
||||
<a
|
||||
href={item.link}
|
||||
aria-label={item.text}
|
||||
className="rp-hover-group__item__link rp-link"
|
||||
hrefLang={item.lang}
|
||||
lang={item.lang}
|
||||
rel={item.rel}
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
const { handleMouseEnter, handleMouseLeave, hoverGroup } = useHoverGroup({
|
||||
items: [],
|
||||
activeMatcher: item => item.text === activeValue,
|
||||
customChildren,
|
||||
});
|
||||
|
||||
return items.length > 1 ? (
|
||||
<NavMenuItemWithChildren
|
||||
menuItem={{ text: activeValue, items }}
|
||||
activeMatcher={item => item.text === activeValue}
|
||||
/>
|
||||
<li
|
||||
className="rp-nav-menu__item"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleMouseEnter}
|
||||
>
|
||||
<div className="rp-nav-menu__item__container">
|
||||
{activeValue}
|
||||
<SvgDown className="rp-nav-menu__item__icon" />
|
||||
</div>
|
||||
{hoverGroup}
|
||||
</li>
|
||||
) : null;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+9
-75
@@ -9,91 +9,25 @@ import {
|
||||
} from '@rspress/core/runtime';
|
||||
import { locales } from '../../locales';
|
||||
|
||||
function replaceLang(
|
||||
rawUrl: string,
|
||||
lang: {
|
||||
current: string;
|
||||
target: string;
|
||||
default: string;
|
||||
},
|
||||
version: {
|
||||
current: string;
|
||||
default: string;
|
||||
},
|
||||
cleanUrls: boolean,
|
||||
isPageNotFound: boolean,
|
||||
) {
|
||||
let url = removeBase(rawUrl);
|
||||
// rspress.rs/builder + switch to en -> rspress.rs/builder/en/index.html
|
||||
if (!url || isPageNotFound) {
|
||||
url = '/';
|
||||
}
|
||||
|
||||
url = normalizeHrefInRuntime(url);
|
||||
|
||||
let versionPart = '';
|
||||
let langPart = '';
|
||||
let purePathPart = '';
|
||||
|
||||
const parts = url.split('/').filter(Boolean);
|
||||
|
||||
if (version.current && version.current !== version.default) {
|
||||
versionPart = parts.shift() || '';
|
||||
}
|
||||
|
||||
// Should we remove the lang part?
|
||||
// The answer is as follows:
|
||||
if (lang.target !== lang.default) {
|
||||
langPart = lang.target;
|
||||
if (lang.current !== lang.default) {
|
||||
parts.shift();
|
||||
}
|
||||
} else {
|
||||
parts.shift();
|
||||
}
|
||||
|
||||
purePathPart = parts.join('/') || '';
|
||||
|
||||
if ((versionPart || langPart) && !purePathPart) {
|
||||
purePathPart = cleanUrls ? 'index' : 'index.html';
|
||||
}
|
||||
|
||||
return addLeadingSlash(
|
||||
[versionPart, langPart, purePathPart].filter(Boolean).join('/'),
|
||||
);
|
||||
}
|
||||
|
||||
export function useLangsMenu() {
|
||||
const { page } = usePage();
|
||||
const { site } = useSite();
|
||||
const currentVersion = useVersion();
|
||||
const { pathname, search } = useLocation();
|
||||
const defaultLang = site.lang || '';
|
||||
const defaultVersion = site.multiVersion.default || '';
|
||||
const { pathname } = useLocation();
|
||||
const { lang: currentLang } = page;
|
||||
const localeLanguages = locales;
|
||||
const cleanUrls = site.route?.cleanUrls || false;
|
||||
const hasMultiLanguage = localeLanguages.length > 1;
|
||||
const { lang: currentLang, pageType } = page;
|
||||
|
||||
// In single-language builds, removeBase strips the current base prefix
|
||||
// (e.g., /cn/) leaving the page-relative path (e.g., /ai/quick-start).
|
||||
// We then prepend the target language's base to build the correct URL.
|
||||
const pagePath = removeBase(pathname);
|
||||
|
||||
const translationMenuData = hasMultiLanguage
|
||||
? {
|
||||
items: localeLanguages.map(item => {
|
||||
const targetBase = item.code === 'en' ? '' : `/${item.code}`;
|
||||
return {
|
||||
text: item?.label,
|
||||
link: replaceLang(
|
||||
pathname + search,
|
||||
{
|
||||
current: currentLang,
|
||||
target: item.code,
|
||||
default: defaultLang,
|
||||
},
|
||||
{
|
||||
current: currentVersion,
|
||||
default: defaultVersion,
|
||||
},
|
||||
cleanUrls,
|
||||
pageType === '404',
|
||||
),
|
||||
link: `${targetBase}${pagePath}` || '/',
|
||||
lang: item.code,
|
||||
rel: 'alternate',
|
||||
};
|
||||
|
||||
+3
-4
@@ -2,7 +2,6 @@ import { useState } from 'react';
|
||||
import { useLangsMenu } from '../Nav/hooks';
|
||||
import './NavScreenLangs.scss';
|
||||
import { useI18n } from '@rspress/core/runtime';
|
||||
import { Link } from '@rspress/core/theme';
|
||||
import clsx from 'clsx';
|
||||
import { SvgDown } from './NavScreenMenuItem';
|
||||
|
||||
@@ -48,16 +47,16 @@ export function NavScreenLangs() {
|
||||
{item.text}
|
||||
</span>
|
||||
) : (
|
||||
<Link
|
||||
<a
|
||||
key={item.text}
|
||||
href={item.link}
|
||||
className={className}
|
||||
className={clsx(className, 'rp-link')}
|
||||
hrefLang={item.lang}
|
||||
lang={item.lang}
|
||||
rel="alternate"
|
||||
>
|
||||
{item.text}
|
||||
</Link>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user