如何在 React Router v7 中维护导航链接的语言环境
在内部导航中维护语言环境
问题
当区域信息编码在 URL 路径中时,每个导航链接都必须保留该区域信息,以保持一致的用户体验。如果用户浏览您网站的法语版本并点击链接 /about,他们期望仍然是法语,并导航到 /fr/about。如果没有支持区域信息的链接,用户会在会话中途被切换到默认语言,破坏了他们的浏览上下文,并迫使他们再次手动切换语言。这会增加摩擦并削弱本地化体验。
将区域前缀硬编码到每个链接中容易出错,并使代码库变得脆弱。随着用户在应用程序中导航,活动区域可能会更改,手动更新数百个链接变得不可持续。
解决方案
创建一个自定义的 Link 组件,该组件会自动从 URL 中读取当前区域信息,并将其添加到所有内部导航路径的前面。通过封装 React Router 的 Link 组件,您可以将区域信息处理集中在一个地方。封装组件从当前路由中提取区域参数,并确保每个目标路径都包含它,从而在不需要手动干预的情况下,保持用户的语言选择。
这种方法使得整个应用程序中的链接定义保持简洁且与区域无关,同时保证区域上下文随每次点击而传递。
步骤
1. 创建一个支持区域信息的 Link 封装组件
构建一个自定义组件,使用 useParams 从 URL 中提取当前区域信息,并封装 React Router 的 Link 组件,将区域信息添加到目标路径的前面。
import { Link, useParams } from "react-router";
import type { LinkProps } from "react-router";
export function LocaleLink({ to, ...props }: LinkProps) {
const { locale } = useParams<{ locale: string }>();
const localizedTo =
typeof to === "string"
? `/${locale}${to.startsWith("/") ? to : `/${to}`}`
: {
...to,
pathname: `/${locale}${to.pathname?.startsWith("/") ? to.pathname : `/${to.pathname}`}`,
};
return <Link to={localizedTo} {...props} />;
}
该组件从当前路由中读取区域参数,并自动将其前缀添加到传递给 to 属性的任何路径中,支持字符串和对象形式。
2. 在整个应用程序中使用 LocaleLink 组件
将标准的 Link 组件替换为 LocaleLink,以便在需要保留语言环境的导航时使用。
import { LocaleLink } from "./LocaleLink";
export function Navigation() {
return (
<nav>
<LocaleLink to="/">首页</LocaleLink>
<LocaleLink to="/about">关于</LocaleLink>
<LocaleLink to="/products">产品</LocaleLink>
</nav>
);
}
当用户在 /fr/products 页面点击 About 链接时,他们会导航到 /fr/about。语言环境前缀会自动添加,而不会使链接定义变得复杂。
3. 处理绝对路径和外部链接的边界情况
扩展包装器以检测路径是否已包含语言环境或指向外部 URL,从而避免双重前缀或破坏外部导航。
import { Link, useParams } from "react-router";
import type { LinkProps } from "react-router";
export function LocaleLink({ to, ...props }: LinkProps) {
const { locale } = useParams<{ locale: string }>();
if (!locale) {
return <Link to={to} {...props} />;
}
const isExternal =
typeof to === "string" &&
(to.startsWith("http://") || to.startsWith("https://"));
const alreadyLocalized =
typeof to === "string" && to.startsWith(`/${locale}/`);
if (isExternal || alreadyLocalized) {
return <Link to={to} {...props} />;
}
const localizedTo =
typeof to === "string"
? `/${locale}${to.startsWith("/") ? to : `/${to}`}`
: {
...to,
pathname: `/${locale}${to.pathname?.startsWith("/") ? to.pathname : `/${to.pathname}`}`,
};
return <Link to={localizedTo} {...props} />;
}
这可以防止路径已以语言环境开头时的双重前缀,并保持外部 URL 不变,确保组件在所有导航场景中可靠运行。