Building a language switcher
Allowing users to switch languages on any page
Problem
A user is on a specific page, such as /en/products/123, and wants to view the same page in another language. Clicking a language switcher (e.g., 'Français') often sends them back to the homepage (/fr/) instead of the corresponding product page, breaking their workflow and forcing them to re-navigate.
Solution
Create a client component that reads the current URL pathname. It generates a list of links for all other supported languages by replacing the current language segment in the path. It also sets a preference cookie on click, ensuring the choice is remembered for future visits.
Steps
1. Define your language configuration
Ensure your i18n.config.ts file includes the list of locales and the name of the cookie you will use.
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const localeCookieName = 'NEXT_LOCALE';
2. Create the language switcher component
Create a new file, for example, app/components/LanguageSwitcher.tsx. This must be a client component to use hooks like usePathname.
// app/components/LanguageSwitcher.tsx
'use client';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
import { locales, localeCookieName } from '@/i18n.config';
export default function LanguageSwitcher() {
const pathname = usePathname();
// This function sets the cookie
const setLocaleCookie = (locale: string) => {
document.cookie = `${localeCookieName}=${locale}; path=/; max-age=31536000; samesite=lax`;
};
// This function strips the current locale from the path
const getRedirectedPath = (locale: string) => {
if (!pathname) return '/';
const segments = pathname.split('/');
segments[1] = locale; // The locale is always the first segment
return segments.join('/');
};
return (
<div>
{locales.map((locale) => (
<Link
key={locale}
href={getRedirectedPath(locale)}
onClick={() => setLocaleCookie(locale)}
style={{
display: 'inline-block',
padding: '0.5rem',
textDecoration: 'underline',
}}
>
{locale.toUpperCase()}
</Link>
))}
</div>
);
}
3. Add the switcher to your layout
Import and place your new component into the root layout file, app/[lang]/layout.tsx. This makes it visible on all pages.
// app/[lang]/layout.tsx
import LanguageSwitcher from '@/app/components/LanguageSwitcher';
export async function generateStaticParams() {
// This tells Next.js to pre-render 'en', 'es', and 'fr'
return [{ lang: 'en' }, { lang: 'es' }, { lang: 'fr' }];
}
export default function RootLayout({
children,
params,
}: {
children: React.ReactNode;
params: { lang: string };
}) {
return (
<html lang={params.lang}>
<body>
<header>
{/* Add the switcher to your header or nav */}
<LanguageSwitcher />
</header>
<main>{children}</main>
</body>
</html>
);
}
Now, when a user is on /es/products/123 and clicks "EN", the component calculates the new path as /en/products/123 and sets the NEXT_LOCALE cookie to 'en'.