Stage M1
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user