如何在 Next.js(Pages Router)v16 中跨会话记住语言选择
存储用户明确选择的语言偏好
问题
当用户明确选择了一种语言时,这一选择代表了他们的偏好,应当优先于通过浏览器头信息或地理位置的自动检测。如果没有持久化机制,这一偏好会在浏览器关闭或会话结束后丢失。下次访问时,应用会重新开始,用户被迫再次选择语言。这种重复操作表明应用未能尊重用户偏好,增加了使用阻力并降低了信任感。
挑战有两个方面:一是在用户选择语言时及时捕获其明确选择,二是在后续访问时,在任何自动检测逻辑运行之前检索该选择。如果未能在请求生命周期的早期检查已存储的偏好,用户可能会根据浏览器设置被重定向,而不是根据其明确选择,从而削弱了选择本身的意义。
解决方案
当用户明确选择语言时,将其选择存储在持久化 cookie 中。用户下次访问应用根路径时,优先检查该 cookie,再考虑基于浏览器的语言检测。如果找到有效的已存储语言,则将用户重定向到该语言的根路径,确保其偏好被立即尊重。
这种方法将用户的明确选择与自动检测分离。cookie 作为持久的意图信号,可以在浏览器重启后依然生效,并优先于 Accept-Language 头等临时信号。通过在服务端初始请求阶段检查 cookie,重定向会在页面渲染前完成,为用户提供无缝体验。
步骤
1. 创建一个辅助函数,在客户端设置语言偏好 cookie
当用户选择语言时,将其选择存储在一个 cookie 中,以便在会话之间保持。
export function setLocalePreference(locale: string) {
const maxAge = 60 * 60 * 24 * 365;
document.cookie = `NEXT_LOCALE=${locale}; path=/; max-age=${maxAge}; SameSite=Lax`;
}
此函数会写入一个名为 NEXT_LOCALE 的 cookie,内容为所选语言,有效期为一年。path=/ 确保该 cookie 可在整个应用中访问,SameSite=Lax 则在允许 cookie 随顶级导航发送的同时,提供合理的 CSRF 防护。
2. 用户选择语言时调用辅助函数
将该辅助函数集成到语言切换组件中,用户选择后立即保存偏好设置。
import { useRouter } from "next/router";
import { setLocalePreference } from "@/lib/locale";
export default function LanguageSwitcher() {
const router = useRouter();
const { locales, locale: currentLocale } = router;
const handleLocaleChange = (newLocale: string) => {
setLocalePreference(newLocale);
router.push(router.pathname, router.asPath, { locale: newLocale });
};
return (
<select
value={currentLocale}
onChange={(e) => handleLocaleChange(e.target.value)}
>
{locales?.map((loc) => (
<option key={loc} value={loc}>
{loc.toUpperCase()}
</option>
))}
</select>
);
}
当用户更改选择时,会设置 cookie,并且路由器会跳转到新语言的同一页面。此 cookie 会在后续所有请求中可用。
3. 在根页面检查已存储的偏好
在根页面的 getServerSideProps 中读取 cookie,如果存在且有效,则重定向到已存储的语言。
import { GetServerSideProps } from "next";
export const getServerSideProps: GetServerSideProps = async (context) => {
const storedLocale = context.req.cookies.NEXT_LOCALE;
const { locales, defaultLocale } = context;
if (
storedLocale &&
locales?.includes(storedLocale) &&
storedLocale !== defaultLocale
) {
return {
redirect: {
destination: `/${storedLocale}`,
permanent: false,
},
};
}
return {
redirect: {
destination: `/${defaultLocale}`,
permanent: false,
},
};
};
export default function RootPage() {
return null;
}
此操作会检查 NEXT_LOCALE cookie 是否存在且包含应用配置列表中的有效语言。如果存储的语言不是默认值,则用户会被重定向到该语言的根页面,否则重定向到默认语言。重定向在服务端渲染前完成,确保用户立即进入正确的语言环境。
4. 在 Next.js 中配置语言路由
确保 next.config.js 定义了支持的语言,以便重定向逻辑可以校验已存储的偏好。
module.exports = {
i18n: {
locales: ["en", "fr", "de", "es"],
defaultLocale: "en",
},
};
此配置启用了 Next.js 的内置 i18n 路由功能,并使 locales 和 defaultLocale 可用于 getServerSideProps。根页面逻辑会使用这些值来验证已存储的 cookie,并构建正确的重定向目标。