Locale Switching
Implement language switchers to let users change their preferred locale.
Basic Implementation
Use the useLingoContext hook to get the current locale and a function to change it:
"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>
);
}
When setLocale() is called:
- New locale is persisted (via custom resolver or default cookie)
- Page reloads to apply new locale
Custom UI
Dropdown with Flags
"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>
);
}
Button Group
"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>
);
}
Dropdown Menu
"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>
);
}
Persistence
By default, setLocale() uses your configured locale persistence (cookie by default).
Custom Persistence
Implement custom persistence via Custom Locale Resolvers:
// .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() automatically calls your persistLocale() function.
Avoiding Page Reload
By default, setLocale() reloads the page to apply the new locale. To avoid reload:
- Use client-side state management
- Pre-load translations for all locales
- Switch translations without page reload
Note: This requires custom implementation—the compiler's default behavior reloads the page for simplicity.
URL-Based Locale Switching
For URL-based locale routing (/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>
);
}
Subdomain-Based Switching
For subdomain-based routing (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>
);
}
Native Language Names
Display language names in their native language for better UX:
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: "Русский" },
];
Accessibility
Make your language switcher accessible:
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>
Common Patterns
Navbar Integration
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>
);
}
Footer Integration
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>
);
}
Mobile Menu
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>
);
}
Testing
Test locale switching in development:
- Add language switcher component
- Run dev server
- Click to switch locales
- Verify page reloads with new locale
- Check that translations update
With pseudotranslator enabled, you'll see fake translations immediately.
Common Questions
Why does the page reload? The compiler reloads the page to apply the new locale. This ensures all Server Components and metadata update correctly.
Can I avoid the reload? Yes, but requires custom implementation. Pre-load all translations and manage client-side state manually.
Do I need to wrap LanguageSwitcher in Suspense?
No. useLingoContext is synchronous—no suspense needed.
Can I detect user's browser language automatically? Yes. Implement in Custom Locale Resolvers:
export function getClientLocale(): string {
return navigator.language.split("-")[0] || "en";
}
Should I show all supported locales or just a few? Show all in a dropdown. If many locales, group by region or use a search/filter UI.
Next Steps
- Custom Locale Resolvers — Customize persistence
- Framework Integration — Framework-specific routing patterns
- Best Practices — UX recommendations for language switching