如何在 React Router v7 中检测用户的语言偏好

基于浏览器偏好自动重定向

问题

每个浏览器在发送 HTTP 请求时都会携带 Accept-Language 头部,按优先顺序指明用户偏好的语言。这一头部包含了用户期望看到的语言的重要信息,但大多数应用却完全忽略了它。相反,应用通常会向每位访问者展示默认语言(通常为英语),即使浏览器已经传达了用户的偏好,用户仍需手动查找语言切换器。这不仅增加了不必要的操作,还会给国际用户留下不佳的第一印象。

解决方案

为根路径创建一个 loader,从传入请求中读取 Accept-Language 头部。解析该头部,提取用户偏好的语言及其权重值。将这些偏好语言与应用支持的本地化语言进行比对。如果找到匹配项,则将用户重定向到对应语言的路径;如果没有匹配,则重定向到默认语言。这样,用户可以根据其浏览器已配置的偏好,自动访问本地化版本的网站。

步骤

1. 安装用于解析 Accept-Language 头部的库

Accept-Language 头部有特定格式,并包含权重值,需要仔细解析。建议使用专门的库来正确处理。

npm install accept-language-parser

该库会将头部字符串解析为有序的语言偏好列表,按照 HTTP 规范正确处理权重值和各种边界情况。

2. 定义支持的本地化语言

创建一个辅助文件,列出应用支持的所有本地化语言,并指定默认回退语言。

export const supportedLocales = ["en", "fr", "de", "es", "ja"] as const;

export const defaultLocale = "en";

export type Locale = (typeof supportedLocales)[number];

这为应用程序支持的语言提供了单一的可信来源,并确保整个代码库的类型安全。

3. 创建本地化检测辅助函数

编写一个函数,接收 Accept-Language 头部的值,并返回与支持语言最匹配的 locale。

import parser from "accept-language-parser";
import { supportedLocales, defaultLocale, type Locale } from "./locales";

export function detectLocale(acceptLanguageHeader: string | null): Locale {
  if (!acceptLanguageHeader) {
    return defaultLocale;
  }

  const languages = parser.parse(acceptLanguageHeader);

  for (const lang of languages) {
    const code = lang.code.toLowerCase();
    if (supportedLocales.includes(code as Locale)) {
      return code as Locale;
    }
  }

  return defaultLocale;
}

该函数会解析头部,按优先级顺序遍历用户的语言偏好,并返回与支持语言列表中第一个匹配的 locale,若无匹配则回退到默认值。

4. 配置根索引路由

在路由配置中添加一个索引路由,用于处理根路径的请求。

import { type RouteConfig, index, route } from "@react-router/dev/routes";

export default [
  index("routes/index.tsx"),
  route(":locale", "routes/locale-root.tsx", []),
] satisfies RouteConfig;

索引路由会在其他路由匹配前拦截根路径请求,使你能够执行语言检测和重定向。

5. 实现带有语言检测的索引路由加载器

创建索引路由模块,读取 Accept-Language 头部并重定向到对应的 locale 路径。

import { redirect } from "react-router";
import type { Route } from "./+types/index";
import { detectLocale } from "~/utils/detect-locale";

export async function loader({ request }: Route.LoaderArgs) {
  const acceptLanguage = request.headers.get("Accept-Language");
  const locale = detectLocale(acceptLanguage);
  return redirect(`/${locale}`);
}

当用户访问根路径时,该加载器会从请求中提取 Accept-Language 头部,确定最佳 locale,并将其重定向到该 locale 的根路径,确保用户从首次加载页面起就能看到其偏好语言的内容。