70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
'use client';
|
|
|
|
import { useLocale, useTranslations } from 'next-intl';
|
|
import { useTransition } from 'react';
|
|
|
|
import { LOCALE_LABELS, LOCALES, type Locale } from '@sport/types';
|
|
import { cn } from '@sport/ui';
|
|
|
|
import { usePathname, useRouter } from '@/i18n/navigation';
|
|
|
|
/**
|
|
* Switches locale while staying on the current page.
|
|
*
|
|
* `usePathname` from `@/i18n/navigation` returns the path *without* the locale
|
|
* prefix, so re-pushing it under a different locale lands on the same page in
|
|
* the other language — rather than dumping the user on the homepage, which is
|
|
* what a plain `<a href="/en">` does and is the usual way this is got wrong.
|
|
*
|
|
* Product pages are the interesting case: slugs are localised, so switching on
|
|
* `/products/ao-chay-bo-aero` produces `/en/products/ao-chay-bo-aero`. That is
|
|
* intentional — the API resolves a slug in any locale, and the PDP then
|
|
* redirects to the canonical `/en/products/aero-run-tee`. One redirect keeps
|
|
* this component simple and keeps every cross-locale link working.
|
|
*/
|
|
export function LanguageSwitcher({ className }: { className?: string }) {
|
|
const t = useTranslations('nav');
|
|
const activeLocale = useLocale() as Locale;
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const [isPending, startTransition] = useTransition();
|
|
|
|
function switchTo(locale: Locale) {
|
|
if (locale === activeLocale) return;
|
|
|
|
// `replace`, not `push`: a language switch is a correction, not a
|
|
// navigation step. Leaving it in history makes the back button toggle
|
|
// languages instead of going back.
|
|
startTransition(() => {
|
|
router.replace(pathname, { locale });
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={cn('flex items-center gap-1', isPending && 'opacity-60', className)}
|
|
role="group"
|
|
aria-label={t('changeLanguage')}
|
|
>
|
|
{LOCALES.map((locale) => (
|
|
<button
|
|
key={locale}
|
|
type="button"
|
|
onClick={() => switchTo(locale)}
|
|
aria-current={locale === activeLocale ? 'true' : undefined}
|
|
lang={locale}
|
|
title={LOCALE_LABELS[locale]}
|
|
className={cn(
|
|
'px-1.5 py-1 text-xs font-semibold uppercase tracking-widest transition-colors',
|
|
locale === activeLocale
|
|
? 'text-ink-950 underline underline-offset-4'
|
|
: 'text-ink-400 hover:text-ink-950',
|
|
)}
|
|
>
|
|
{locale}
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|