Translate pages
This commit is contained in:
parent
a7aaf5effa
commit
96402228e3
49 changed files with 1458 additions and 926 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { AuthUser } from '../../hooks/useAuth';
|
||||
import { shortenUrl, prewarmScreenshot } from '../../lib/api';
|
||||
import { copyToClipboard } from '../../lib/clipboard';
|
||||
|
|
@ -13,6 +14,7 @@ import { MoonIcon } from './icons/MoonIcon';
|
|||
import { SpinnerIcon } from './icons/SpinnerIcon';
|
||||
import UserMenu from './UserMenu';
|
||||
import MobileMenu from './MobileMenu';
|
||||
import LanguageDropdown from './LanguageDropdown';
|
||||
|
||||
export type Page =
|
||||
| 'home'
|
||||
|
|
@ -64,6 +66,7 @@ export default function Header({
|
|||
onLogout: () => void;
|
||||
isMobile: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [sharing, setSharing] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
|
@ -131,7 +134,7 @@ export default function Header({
|
|||
onClick={(e) => navLink('home', e)}
|
||||
>
|
||||
<LogoIcon className="w-5 h-5 text-teal-400" />
|
||||
<span className="font-semibold text-lg">Perfect Postcode</span>
|
||||
<span className="font-semibold text-lg">{t('header.appName')}</span>
|
||||
</a>
|
||||
|
||||
{/* Desktop nav */}
|
||||
|
|
@ -142,7 +145,7 @@ export default function Header({
|
|||
className={tabClass('dashboard')}
|
||||
onClick={(e) => navLink('dashboard', e)}
|
||||
>
|
||||
Dashboard
|
||||
{t('header.dashboard')}
|
||||
</a>
|
||||
{user && (
|
||||
<a
|
||||
|
|
@ -150,7 +153,7 @@ export default function Header({
|
|||
className={tabClass('invites')}
|
||||
onClick={(e) => navLink('invites', e)}
|
||||
>
|
||||
Invite Friends
|
||||
{t('header.inviteFriends')}
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
|
|
@ -158,7 +161,7 @@ export default function Header({
|
|||
className={tabClass('learn')}
|
||||
onClick={(e) => navLink('learn', e)}
|
||||
>
|
||||
Learn
|
||||
{t('header.learn')}
|
||||
</a>
|
||||
{user?.subscription !== 'licensed' && !user?.isAdmin && (
|
||||
<a
|
||||
|
|
@ -166,7 +169,7 @@ export default function Header({
|
|||
className={tabClass('pricing')}
|
||||
onClick={(e) => navLink('pricing', e)}
|
||||
>
|
||||
Pricing
|
||||
{t('header.pricing')}
|
||||
</a>
|
||||
)}
|
||||
</nav>
|
||||
|
|
@ -186,17 +189,17 @@ export default function Header({
|
|||
{sharing ? (
|
||||
<>
|
||||
<SpinnerIcon className="w-4 h-4 animate-spin" />
|
||||
Sharing...
|
||||
{t('header.sharing')}
|
||||
</>
|
||||
) : copied ? (
|
||||
<>
|
||||
<CheckIcon className="w-4 h-4" />
|
||||
Copied!
|
||||
{t('common.copied')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ClipboardIcon className="w-4 h-4" />
|
||||
Share
|
||||
{t('common.share')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
|
@ -204,10 +207,10 @@ export default function Header({
|
|||
onClick={onExport ?? undefined}
|
||||
disabled={!onExport || exporting}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded bg-navy-800 hover:bg-navy-700 transition-colors text-sm disabled:opacity-50 disabled:cursor-wait"
|
||||
title="Export to Excel"
|
||||
title={t('header.exportToExcel')}
|
||||
>
|
||||
<DownloadIcon className="w-4 h-4" />
|
||||
{exporting ? 'Exporting...' : 'Export'}
|
||||
{exporting ? t('header.exporting') : t('header.exportLabel')}
|
||||
</button>
|
||||
{onSaveSearch && (
|
||||
<button
|
||||
|
|
@ -220,7 +223,7 @@ export default function Header({
|
|||
) : (
|
||||
<BookmarkIcon className="w-4 h-4" />
|
||||
)}
|
||||
Save
|
||||
{t('common.save')}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -231,7 +234,7 @@ export default function Header({
|
|||
className={tabClass('saved')}
|
||||
onClick={(e) => navLink('saved', e)}
|
||||
>
|
||||
Saved
|
||||
{t('header.saved')}
|
||||
</a>
|
||||
)}
|
||||
|
||||
|
|
@ -252,13 +255,13 @@ export default function Header({
|
|||
onClick={onLoginClick}
|
||||
className="px-3 py-1.5 rounded bg-navy-800 hover:bg-navy-700 transition-colors text-sm"
|
||||
>
|
||||
Log in
|
||||
{t('header.logIn')}
|
||||
</button>
|
||||
<button
|
||||
onClick={onRegisterClick}
|
||||
className="px-3 py-1.5 rounded bg-teal-600 hover:bg-teal-700 transition-colors text-sm font-medium"
|
||||
>
|
||||
Create account
|
||||
{t('header.createAccount')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -271,16 +274,19 @@ export default function Header({
|
|||
onClick={onRegisterClick}
|
||||
className="px-4 py-1.5 rounded bg-teal-600 hover:bg-teal-700 transition-colors text-sm font-semibold"
|
||||
>
|
||||
Create account
|
||||
{t('header.createAccount')}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Language selector (desktop) */}
|
||||
{!isMobile && <LanguageDropdown />}
|
||||
|
||||
{/* Theme toggle (desktop, logged-out only — logged-in users use UserMenu) */}
|
||||
{!isMobile && !user && (
|
||||
<button
|
||||
onClick={onToggleTheme}
|
||||
className="flex items-center justify-center w-8 h-8 rounded bg-navy-800 hover:bg-navy-700 transition-colors"
|
||||
title={`Theme: ${theme}`}
|
||||
title={theme === 'light' ? t('userMenu.themeLight') : t('userMenu.themeDark')}
|
||||
>
|
||||
{theme === 'light' ? <SunIcon className="w-4 h-4" /> : <MoonIcon className="w-4 h-4" />}
|
||||
</button>
|
||||
|
|
@ -291,7 +297,7 @@ export default function Header({
|
|||
<button
|
||||
onClick={() => setMenuOpen(true)}
|
||||
className="flex items-center justify-center w-10 h-10 -mr-2 rounded hover:bg-navy-800 transition-colors"
|
||||
aria-label="Open menu"
|
||||
aria-label={t('header.openMenu')}
|
||||
>
|
||||
<MenuIcon className="w-6 h-6" />
|
||||
</button>
|
||||
|
|
@ -322,7 +328,7 @@ export default function Header({
|
|||
{isMobile && copied && (
|
||||
<div className="fixed top-14 left-1/2 -translate-x-1/2 z-[60] flex items-center gap-2 px-4 py-2 rounded-lg bg-navy-900 text-white text-sm shadow-lg animate-fade-in">
|
||||
<CheckIcon className="w-4 h-4 text-teal-400" />
|
||||
Copied to clipboard
|
||||
{t('common.copiedToClipboard')}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue