语言切换
实现语言切换器,让用户可以更改其首选语言环境。
基本实现
使用 useLingoContext hook 获取当前 locale 以及用于切换 locale 的函数:
"use client"; // For Next.js
import { useLingoContext } from "@lingo.dev/compiler/react";
export function LanguageSwitcher() {
const { locale, setLocale } = useLingoContext();
return (
<select value={locale} onChange={(e) => setLocale(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
<option value="de">Deutsch</option>
<option value="fr">Français</option>
</select>
);
}
当调用 setLocale() 时:
- 新的语言环境会被持久化(通过自定义解析器或默认 cookie)
- 页面会重新加载以应用新的语言环境
自定义 UI
带国旗的下拉菜单
"use client";
import { useLingoContext } from "@lingo.dev/compiler/react";
const locales = [
{ code: "en", name: "English", flag: "🇺🇸" },
{ code: "es", name: "Español", flag: "🇪🇸" },
{ code: "de", name: "Deutsch", flag: "🇩🇪" },
{ code: "fr", name: "Français", flag: "🇫🇷" },
];
export function LanguageSwitcher() {
const { locale: currentLocale, setLocale } = useLingoContext();
return (
<div className="relative">
<select
value={currentLocale}
onChange={(e) => setLocale(e.target.value)}
className="px-4 py-2 border rounded-md"
>
{locales.map((locale) => (
<option key={locale.code} value={locale.code}>
{locale.flag} {locale.name}
</option>
))}
</select>
</div>
);
}
按钮组
"use client";
import { useLingoContext } from "@lingo.dev/compiler/react";
export function LanguageSwitcher() {
const { locale, setLocale } = useLingoContext();
const locales = ["en", "es", "de", "fr"];
return (
<div className="flex gap-2">
{locales.map((loc) => (
<button
key={loc}
onClick={() => setLocale(loc)}
className={`px-3 py-1 rounded ${
locale === loc ? "bg-blue-500 text-white" : "bg-gray-200"
}`}
>
{loc.toUpperCase()}
</button>
))}
</div>
);
}
下拉菜单
"use client";
import { useState } from "react";
import { useLingoContext } from "@lingo.dev/compiler/react";
export function LanguageSwitcher() {
const { locale, setLocale } = useLingoContext();
const [isOpen, setIsOpen] = useState(false);
const locales = [
{ code: "en", name: "English" },
{ code: "es", name: "Español" },
{ code: "de", name: "Deutsch" },
{ code: "fr", name: "Français" },
];
const currentLocaleName = locales.find((l) => l.code === locale)?.name;
return (
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="px-4 py-2 border rounded-md flex items-center gap-2"
>
<span>{currentLocaleName}</span>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{isOpen && (
<div className="absolute top-full mt-2 bg-white border rounded-md shadow-lg">
{locales.map((loc) => (
<button
key={loc.code}
onClick={() => {
setLocale(loc.code);
setIsOpen(false);
}}
className={`block w-full text-left px-4 py-2 hover:bg-gray-100 ${
locale === loc.code ? "bg-blue-50" : ""
}`}
>
{loc.name}
</button>
))}
</div>
)}
</div>
);
}
持久化
默认情况下,setLocale() 使用你配置的语言环境持久化方式(默认为 cookie)。
自定义持久化
通过 自定义语言环境解析器 实现自定义持久化:
// .lingo/locale-resolver.client.ts
export function persistLocale(locale: string): void {
// Custom logic: localStorage, URL params, API call, etc.
localStorage.setItem("locale", locale);
window.location.reload();
}
setLocale() 会自动调用你的 persistLocale() 方法。
避免页面刷新
默认情况下,setLocale() 会刷新页面以应用新的语言环境。要避免刷新:
- 使用客户端状态管理
- 预加载所有语言环境的翻译
- 在不刷新页面的情况下切换翻译
注意: 这需要自定义实现——编译器的默认行为为简化处理会刷新页面。
基于 URL 的语言切换
对于基于 URL 的语言环境路由(/en/about、/es/about):
"use client";
import { useRouter, usePathname } from "next/navigation";
export function LanguageSwitcher() {
const router = useRouter();
const pathname = usePathname();
// Extract current locale from path: /es/about → es
const currentLocale = pathname.split("/")[1];
function switchLocale(newLocale: string) {
// Replace locale in path: /es/about → /de/about
const newPath = pathname.replace(`/${currentLocale}`, `/${newLocale}`);
router.push(newPath);
}
return (
<select value={currentLocale} onChange={(e) => switchLocale(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
<option value="de">Deutsch</option>
</select>
);
}
基于子域名的切换
对于基于子域名的路由(es.example.com):
"use client";
export function LanguageSwitcher() {
const currentLocale = window.location.hostname.split(".")[0];
function switchLocale(newLocale: string) {
const host = window.location.hostname;
const domain = host.split(".").slice(1).join(".");
const newHost = `${newLocale}.${domain}`;
window.location.href = `${window.location.protocol}//${newHost}${window.location.pathname}`;
}
return (
<select value={currentLocale} onChange={(e) => switchLocale(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
<option value="de">Deutsch</option>
</select>
);
}
本地语言名称
以本地语言显示语言名称,以提升用户体验:
const locales = [
{ code: "en", name: "English" },
{ code: "es", name: "Español" },
{ code: "de", name: "Deutsch" },
{ code: "fr", name: "Français" },
{ code: "ja", name: "日本語" },
{ code: "zh", name: "中文" },
{ code: "ar", name: "العربية" },
{ code: "ru", name: "Русский" },
];
无障碍性
让您的语言切换器具备无障碍功能:
const { locale, setLocale } = useLingoContext();
<div role="group" aria-label="Language selector">
<label htmlFor="language-select" className="sr-only">
Choose language
</label>
<select
id="language-select"
value={locale}
onChange={(e) => setLocale(e.target.value)}
aria-label="Select language"
>
{locales.map((loc) => (
<option key={loc.code} value={loc.code}>
{loc.name}
</option>
))}
</select>
</div>
常见模式
导航栏集成
export function Navbar() {
return (
<nav className="flex items-center justify-between p-4">
<Logo />
<div className="flex items-center gap-4">
<NavLinks />
<LanguageSwitcher />
</div>
</nav>
);
}
页脚集成
export function Footer() {
return (
<footer className="p-4 border-t">
<div className="flex justify-between items-center">
<p>© 2024 Your Company</p>
<LanguageSwitcher />
</div>
</footer>
);
}
移动端菜单
export function MobileMenu() {
return (
<div className="mobile-menu">
<NavLinks />
<div className="border-t pt-4 mt-4">
<p className="text-sm text-gray-500 mb-2">Language</p>
<LanguageSwitcher />
</div>
</div>
);
}
测试
在开发环境中测试语言环境切换:
- 添加语言切换组件
- 运行开发服务器
- 点击切换语言环境
- 验证页面是否以新语言环境重新加载
- 检查翻译内容是否更新
启用伪翻译器后,您会立即看到伪造的翻译内容。
常见问题
为什么页面会重新加载? 编译器会重新加载页面以应用新的语言环境。这可以确保所有 Server 组件和元数据都能正确更新。
可以避免重新加载吗? 可以,但需要自定义实现。需预加载所有翻译并手动管理客户端状态。
需要将 LanguageSwitcher 包裹在 Suspense 里吗?
不需要。useLingoContext 是同步的——无需 suspense。
可以自动检测用户的浏览器语言吗? 可以。在 自定义语言解析器 中实现:
export function getClientLocale(): string {
return navigator.language.split("-")[0] || "en";
}
应该显示所有支持的语言还是只显示部分? 建议在下拉菜单中显示全部。如果支持的语言较多,可以按地区分组或使用搜索/筛选界面。