如何在 TanStack Start v1 中构建语言切换组件

在同一页面上切换语言

问题

当用户在多语言应用程序中切换语言时,他们期望能够停留在同一页面并以新语言查看相同的内容。一个实现不佳的语言切换器会将语言选择视为导航到不同的目的地,通常会将用户重定向到主页,从而完全丢失他们的位置。这会破坏用户流程并引发挫败感,尤其是当用户正在深入执行某个工作流程或查看特定内容时。挑战在于构建一个切换器,该切换器仅更改 URL 的语言段,同时保留路径、查询参数和哈希的其余部分。

解决方案

构建一个语言切换器组件,该组件读取当前 URL 的路径名,并通过替换路径中的语言区域段来为每种可用语言构建链接。从 URL 结构中提取当前语言区域,然后通过替换语言区域部分并保留所有其他 URL 段来生成其他支持语言的新路径。使用这些路径渲染链接,使用户能够在不丢失当前页面上下文的情况下切换语言。

步骤

1. 定义支持的语言区域

创建一个配置文件,列出您的应用程序支持的所有语言,并标识默认语言区域。

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

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

export const defaultLocale: Locale = "en";

此配置作为可用语言的单一事实来源,并将用于生成切换器链接。

2. 创建一个助手函数以从路径名中提取当前语言区域

编写一个实用函数,用于解析路径名并提取其中的语言区域段(如果存在)。

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocaleFromPathname(pathname: string): Locale {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  if (firstSegment && locales.includes(firstSegment as Locale)) {
    return firstSegment as Locale;
  }

  return defaultLocale;
}

此函数检查路径的第一个段落,如果它匹配支持的语言区域,则返回该段,否则返回默认语言区域。

3. 创建一个辅助函数以构建本地化路径

编写一个函数,该函数接收当前路径名和目标语言区域,然后构造一个新路径,将语言区域段替换掉。

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocalizedPath(
  pathname: string,
  targetLocale: Locale,
): string {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  const hasLocalePrefix =
    firstSegment && locales.includes(firstSegment as Locale);

  if (hasLocalePrefix) {
    segments[0] = targetLocale;
  } else {
    segments.unshift(targetLocale);
  }

  return "/" + segments.join("/");
}

此函数会替换现有的语言区域段,或者将目标语言区域添加到路径的开头,确保新 URL 指向同一页面的不同语言版本。

4. 构建语言切换器组件

创建一个组件,该组件使用当前位置生成所有支持语言的链接。

import { Link } from "@tanstack/react-router";
import { useLocation } from "@tanstack/react-router";
import { locales, type Locale } from "./locales";
import { getLocaleFromPathname, getLocalizedPath } from "./locale-helpers";

export function LanguageSwitcher() {
  const location = useLocation();
  const currentLocale = getLocaleFromPathname(location.pathname);

  return (
    <nav>
      <ul>
        {locales.map((locale) => {
          const isActive = locale === currentLocale;
          const localizedPath = getLocalizedPath(location.pathname, locale);

          return (
            <li key={locale}>
              <Link
                to={localizedPath}
                search={location.search}
                hash={location.hash}
                aria-current={isActive ? "page" : undefined}
              >
                {locale.toUpperCase()}
              </Link>
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

该组件读取当前路径名,确定活动语言区域,并为每种支持的语言渲染一个链接,同时保留当前页面结构、查询参数和哈希片段。用户可以在保持相同逻辑页面的情况下切换语言。