如何在 Next.js (Pages Router) v16 中跨会话记住语言选择

存储用户明确的语言选择

问题

当用户明确选择了一种语言时,这一选择反映了他们的偏好,应优先于通过浏览器头信息或地理位置的自动检测。如果没有持久化,这种偏好会在浏览器关闭或会话结束时丢失。在下次访问时,应用程序会重新开始,迫使用户再次选择语言。这种重复操作表明应用程序不尊重用户的偏好,增加了使用的摩擦并降低了信任。

这个挑战有两个方面:在用户选择语言时捕获其明确选择,并在后续访问中在任何自动检测逻辑运行之前检索该选择。如果在请求生命周期的早期未检查存储的偏好,用户可能会根据浏览器设置被重定向,而不是根据他们的明确选择,这削弱了选择的价值。

解决方案

当用户明确选择语言时,将其语言选择存储在一个持久化的 cookie 中。在未来访问应用程序根目录时,先检查这个 cookie,再回退到基于浏览器的语言检测。如果找到有效的存储语言偏好,则将用户重定向到该语言的根路径,确保立即尊重他们的偏好。

这种方法将用户的明确选择与自动检测分开。cookie 作为一种持久的意图信号,可以在浏览器重启后仍然有效,并优先于诸如 Accept-Language 头之类的临时信号。通过在初始请求期间在服务器端检查 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=/ 确保它在整个应用程序中可用,而 SameSite=Lax 提供了合理的 CSRF 保护,同时允许在顶级导航时发送该 cookie。

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 路由,并使 localesdefaultLocale 可用于 getServerSideProps。根页面逻辑使用这些值来验证存储的 cookie 并构建正确的重定向目标。