docs/solutions/developer-experience/2026-05-24-shadcn-header-nav-needs-locale-safe-client-links.md
After moving docs navigation to Fumadocs pageTree, header surfaces still had client-side locale work to do. The server can pass page-tree-shaped data into MobileNav and CommandMenu, but those client components own the current pathname locale and must localize their fallback links before navigation.
/cn mobile nav rendered docs entries from the shared tree but could point internal links at /docs instead of /cn/docs.href values even when the current route was Chinese./ should become /cn, existing /cn/* should stay unchanged, and absolute external links should never receive a locale prefix.locale === 'cn'. That double-prefixes existing CN links and corrupts absolute external URLs.Make hrefWithLocale the single href normalizer for docs/header links:
const ABSOLUTE_HREF_REGEX = /^[a-z][a-z\d+\-.]*:/i;
export const hrefWithLocale = (href: string, locale: string) => {
if (
locale !== 'cn' ||
href.startsWith('/cn') ||
href.startsWith('#') ||
ABSOLUTE_HREF_REGEX.test(href)
) {
return href;
}
if (href === '/') {
return '/cn';
}
return `/cn${href}`;
};
Use that helper from MobileNav, MainNav, Logo, and CommandMenu. For display text, read titleCn / labelCn in client components based on useLocale(). Keep external links explicit:
<MobileLink
href={href}
rel={external ? 'noreferrer' : undefined}
target={external ? '_blank' : undefined}
>
{getNavTitle(item, locale)}
</MobileLink>
Move the header breakpoint to the upstream shadcn shape: mobile nav remains available until lg, while desktop logo/nav starts at lg.
Fumadocs owns docs ordering and page discovery, but locale selection is a route-state concern in the rendered app. Normalizing hrefs at the click surface keeps Fumadocs metadata reusable across English and Chinese routes without duplicating the whole header tree.
The external-link guard matters because product links such as Plate Plus are part of the header too. A locale helper that corrupts external URLs is worse than no helper; it quietly breaks the product path.
/, /cn/*, hash links, and absolute URLs./ and /cn at mobile width after header/nav changes.