feat: add i18n support with VITE_LOCALE env var (en/fr)
- Lightweight i18n system in src/lib/i18n.ts (no external deps) - All UI strings extracted to translation keys - English (default) and French locales included - Set VITE_LOCALE=fr in .env for French UI - Fallback to English for unknown locales
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Menu, Bot, Sparkles, LogOut } from 'lucide-react';
|
||||
import type { ConnectionStatus, Session } from '../types';
|
||||
import { t } from '../lib/i18n';
|
||||
|
||||
interface Props {
|
||||
status: ConnectionStatus;
|
||||
@@ -15,7 +16,7 @@ export function Header({ status, sessionKey, onToggleSidebar, activeSessionData,
|
||||
return (
|
||||
<>
|
||||
<header className="h-14 border-b border-white/8 bg-[#232329]/90 backdrop-blur-xl flex items-center px-4 gap-3 shrink-0" role="banner">
|
||||
<button onClick={onToggleSidebar} aria-label="Toggle sidebar" className="lg:hidden p-2 rounded-2xl hover:bg-white/5 text-zinc-400 transition-colors">
|
||||
<button onClick={onToggleSidebar} aria-label={t('header.toggleSidebar')} className="lg:hidden p-2 rounded-2xl hover:bg-white/5 text-zinc-400 transition-colors">
|
||||
<Menu size={20} />
|
||||
</button>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
@@ -24,7 +25,7 @@ export function Header({ status, sessionKey, onToggleSidebar, activeSessionData,
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold text-zinc-300 text-sm tracking-wide">PinchChat</span>
|
||||
<span className="font-semibold text-zinc-300 text-sm tracking-wide">{t('header.title')}</span>
|
||||
<Sparkles className="h-3.5 w-3.5 text-cyan-300/60" />
|
||||
</div>
|
||||
<span className="text-xs text-zinc-500 truncate block">{sessionLabel}</span>
|
||||
@@ -34,25 +35,25 @@ export function Header({ status, sessionKey, onToggleSidebar, activeSessionData,
|
||||
{status === 'connected' ? (
|
||||
<div className="flex items-center gap-2 rounded-2xl border border-white/8 bg-zinc-800/30 px-3 py-1.5">
|
||||
<span className="w-2 h-2 rounded-full bg-cyan-300/80 shadow-[0_0_12px_rgba(34,211,238,0.6)]" />
|
||||
<span className="text-xs text-zinc-300 hidden sm:inline">Connected</span>
|
||||
<span className="text-xs text-zinc-300 hidden sm:inline">{t('header.connected')}</span>
|
||||
</div>
|
||||
) : status === 'connecting' ? (
|
||||
<div className="flex items-center gap-2 rounded-2xl border border-white/8 bg-zinc-800/30 px-3 py-1.5">
|
||||
<span className="w-2 h-2 rounded-full bg-yellow-400/80 pulse-dot" />
|
||||
<span className="text-xs text-zinc-300 hidden sm:inline">Connecting…</span>
|
||||
<span className="text-xs text-zinc-300 hidden sm:inline">{t('login.connecting')}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 rounded-2xl border border-white/8 bg-zinc-800/30 px-3 py-1.5">
|
||||
<span className="w-2 h-2 rounded-full bg-red-400/80" />
|
||||
<span className="text-xs text-zinc-300 hidden sm:inline">Disconnected</span>
|
||||
<span className="text-xs text-zinc-300 hidden sm:inline">{t('header.disconnected')}</span>
|
||||
</div>
|
||||
)}
|
||||
{onLogout && (
|
||||
<button
|
||||
onClick={onLogout}
|
||||
aria-label="Disconnect and logout"
|
||||
aria-label={t('header.logout')}
|
||||
className="p-2 rounded-2xl hover:bg-white/5 text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
title="Logout"
|
||||
title={t('header.logout')}
|
||||
>
|
||||
<LogOut size={16} />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user