refactor: współdzielony LangSwitcher także w nagłówkach podstron
Przełącznik siedział w Header.tsx, który renderuje się tylko na landingu — PrivacyPolicyPage i PlatformPage mają własne <header>. Z /en/privacy i /en/platforma nie dało się wrócić na PL inaczej niż przez stronę główną albo ręczną edycję URL-a. Wyciągnięty do src/components/LangSwitcher.tsx i wstawiony w oba nagłówki obok linku powrotnego. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3e452191de
commit
13ffb8754b
|
|
@ -1,39 +1,8 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Menu, X } from 'lucide-react';
|
import { Menu, X } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { localizePath, setLang, useLang, type Lang } from '../i18n';
|
import { localizePath, useLang } from '../i18n';
|
||||||
|
import LangSwitcher from './LangSwitcher';
|
||||||
const LANGS: Lang[] = ['pl', 'en'];
|
|
||||||
|
|
||||||
// PL / EN toggle. Buttons rather than links: switching is a client-side
|
|
||||||
// navigation (setLang), so a real href would reload the whole document.
|
|
||||||
const LangSwitcher = ({ onSwitch }: { onSwitch?: () => void }) => {
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-2 text-sm font-medium" role="group" aria-label="Język / Language">
|
|
||||||
{LANGS.map((code, i) => (
|
|
||||||
<React.Fragment key={code}>
|
|
||||||
{i > 0 && <span aria-hidden="true" className="text-muted/40">/</span>}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
lang={code}
|
|
||||||
aria-current={code === lang ? 'true' : undefined}
|
|
||||||
onClick={() => {
|
|
||||||
setLang(code);
|
|
||||||
onSwitch?.();
|
|
||||||
}}
|
|
||||||
className={`uppercase tracking-wide transition-colors ${
|
|
||||||
code === lang ? 'text-foreground' : 'text-muted hover:text-foreground'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{code}
|
|
||||||
</button>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
|
||||||
44
src/components/LangSwitcher.tsx
Normal file
44
src/components/LangSwitcher.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { setLang, useLang, type Lang } from '../i18n';
|
||||||
|
|
||||||
|
const LANGS: Lang[] = ['pl', 'en'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PL / EN toggle. Buttons rather than links: switching is a client-side
|
||||||
|
* navigation (setLang), so a real href would reload the whole document.
|
||||||
|
*
|
||||||
|
* Shared by the landing Header and the standalone pages' own headers — without
|
||||||
|
* it on those pages, /en/privacy and /en/platforma have no way back to Polish
|
||||||
|
* short of editing the URL.
|
||||||
|
*
|
||||||
|
* @param onSwitch runs after the language changes — used to close the mobile menu.
|
||||||
|
*/
|
||||||
|
const LangSwitcher = ({ onSwitch }: { onSwitch?: () => void }) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2 text-sm font-medium" role="group" aria-label="Język / Language">
|
||||||
|
{LANGS.map((code, i) => (
|
||||||
|
<React.Fragment key={code}>
|
||||||
|
{i > 0 && <span aria-hidden="true" className="text-muted/40">/</span>}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
lang={code}
|
||||||
|
aria-current={code === lang ? 'true' : undefined}
|
||||||
|
onClick={() => {
|
||||||
|
setLang(code);
|
||||||
|
onSwitch?.();
|
||||||
|
}}
|
||||||
|
className={`uppercase tracking-wide transition-colors ${
|
||||||
|
code === lang ? 'text-foreground' : 'text-muted hover:text-foreground'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{code}
|
||||||
|
</button>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LangSwitcher;
|
||||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import { ArrowRight, Building2, CheckCircle2, ChevronRight, Home } from 'lucide-react';
|
import { ArrowRight, Building2, CheckCircle2, ChevronRight, Home } from 'lucide-react';
|
||||||
import Footer from './Footer';
|
import Footer from './Footer';
|
||||||
import { localizePath, useLang } from '../i18n';
|
import { localizePath, useLang } from '../i18n';
|
||||||
|
import LangSwitcher from './LangSwitcher';
|
||||||
|
|
||||||
// Standalone product page at /platforma (and /en/platforma). The copy is not
|
// Standalone product page at /platforma (and /en/platforma). The copy is not
|
||||||
// localized yet — the site only translates contact.* and privacy.* — but the
|
// localized yet — the site only translates contact.* and privacy.* — but the
|
||||||
|
|
@ -16,9 +17,12 @@ const PlatformPage = () => {
|
||||||
<a href={localizePath('/', lang)} className="text-2xl font-bold tracking-tight hover:opacity-80 transition-opacity">
|
<a href={localizePath('/', lang)} className="text-2xl font-bold tracking-tight hover:opacity-80 transition-opacity">
|
||||||
human<span className="text-primary">AI</span>
|
human<span className="text-primary">AI</span>
|
||||||
</a>
|
</a>
|
||||||
<a href={localizePath('/', lang)} className="text-sm text-muted hover:text-foreground transition-colors">
|
<div className="flex items-center gap-6">
|
||||||
← Wróć na stronę główną
|
<a href={localizePath('/', lang)} className="text-sm text-muted hover:text-foreground transition-colors">
|
||||||
</a>
|
← Wróć na stronę główną
|
||||||
|
</a>
|
||||||
|
<LangSwitcher />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { localizePath, useLang, useT } from '../i18n';
|
import { localizePath, useLang, useT } from '../i18n';
|
||||||
import Footer from './Footer';
|
import Footer from './Footer';
|
||||||
|
import LangSwitcher from './LangSwitcher';
|
||||||
|
|
||||||
const PrivacyPolicyPage = () => {
|
const PrivacyPolicyPage = () => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
|
|
@ -14,9 +15,12 @@ const PrivacyPolicyPage = () => {
|
||||||
<a href={home} className="text-2xl font-bold tracking-tight hover:opacity-80 transition-opacity">
|
<a href={home} className="text-2xl font-bold tracking-tight hover:opacity-80 transition-opacity">
|
||||||
human<span className="text-primary">AI</span>
|
human<span className="text-primary">AI</span>
|
||||||
</a>
|
</a>
|
||||||
<a href={home} className="text-sm text-muted hover:text-foreground transition-colors">
|
<div className="flex items-center gap-6">
|
||||||
{t.privacy.backToHome}
|
<a href={home} className="text-sm text-muted hover:text-foreground transition-colors">
|
||||||
</a>
|
{t.privacy.backToHome}
|
||||||
|
</a>
|
||||||
|
<LangSwitcher />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue