config, language

This commit is contained in:
2026-03-17 10:34:47 +00:00
parent 22a34506c9
commit b7c59c6305
2 changed files with 606 additions and 70 deletions

View File

@@ -6,7 +6,7 @@ import {
Edit2, Image as ImageIcon, Check, RotateCcw, Trash, Edit2, Image as ImageIcon, Check, RotateCcw, Trash,
PanelLeftClose, PanelLeftOpen, Sparkles, CloudSun, PanelLeftClose, PanelLeftOpen, Sparkles, CloudSun,
ArrowRight, Droplets, CheckCircle2, PieChart, History, ArrowRight, Droplets, CheckCircle2, PieChart, History,
X X, Download, Bell, Globe
} from 'lucide-react'; } from 'lucide-react';
import { import {
@@ -22,6 +22,7 @@ import { auth, db, appId } from './lib/firebase';
import { Card } from './components/ui/Card'; import { Card } from './components/ui/Card';
import { Badge } from './components/ui/Badge'; import { Badge } from './components/ui/Badge';
import { Input } from './components/ui/Input'; import { Input } from './components/ui/Input';
import { translations } from './lib/i18n';
export default function App() { export default function App() {
const [view, setView] = useState('auth'); const [view, setView] = useState('auth');
@@ -41,6 +42,13 @@ export default function App() {
// Estado para criação de Looks // Estado para criação de Looks
const [selectedForLook, setSelectedForLook] = useState([]); const [selectedForLook, setSelectedForLook] = useState([]);
// Estado para Definições
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
const [weatherAlerts, setWeatherAlerts] = useState(true);
const [language, setLanguage] = useState('PT');
const t = (key) => translations[language]?.[key] || translations['PT'][key] || key;
// 1. Inicializar Autenticação // 1. Inicializar Autenticação
useEffect(() => { useEffect(() => {
const initAuth = async () => { const initAuth = async () => {
@@ -103,7 +111,7 @@ export default function App() {
case 'restore': await updateDoc(docRef, { status: 'active', trashedAt: null }); break; case 'restore': await updateDoc(docRef, { status: 'active', trashedAt: null }); break;
case 'laundry': await updateDoc(docRef, { status: 'laundry' }); break; case 'laundry': await updateDoc(docRef, { status: 'laundry' }); break;
case 'clean': await updateDoc(docRef, { status: 'active' }); break; case 'clean': await updateDoc(docRef, { status: 'active' }); break;
case 'delete': if (window.confirm("Apagar permanentemente?")) await deleteDoc(docRef); break; case 'delete': if (window.confirm(t('confirmDeletePerm'))) await deleteDoc(docRef); break;
} }
}; };
@@ -160,7 +168,7 @@ export default function App() {
}; };
const deleteLook = async (id) => { const deleteLook = async (id) => {
if (!window.confirm("Apagar este Look?")) return; if (!window.confirm(t('confirmDeleteLook'))) return;
const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'looks', id); const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'looks', id);
await deleteDoc(docRef); await deleteDoc(docRef);
}; };
@@ -179,7 +187,7 @@ export default function App() {
} catch (err) { } catch (err) {
console.error(err); console.error(err);
if (err.code === 'auth/operation-not-allowed') { if (err.code === 'auth/operation-not-allowed') {
setAuthError('O login por e-mail está desativado.'); setAuthError(t('authErrorDisabled'));
} else { } else {
setAuthError(err.message); setAuthError(err.message);
} }
@@ -189,7 +197,7 @@ export default function App() {
const emptyTrashPermanently = async () => { const emptyTrashPermanently = async () => {
if (!user || !window.confirm("Esvaziar o lixo permanentemente?")) return; if (!user || !window.confirm(t('confirmEmptyTrash'))) return;
setLoading(true); setLoading(true);
try { try {
const batch = writeBatch(db); const batch = writeBatch(db);
@@ -203,7 +211,7 @@ export default function App() {
}; };
const clearAllToTrash = async () => { const clearAllToTrash = async () => {
if (!user || !window.confirm("Mover todas as peças ativas para o lixo?")) return; if (!user || !window.confirm(t('confirmClearAll'))) return;
setLoading(true); setLoading(true);
try { try {
const batch = writeBatch(db); const batch = writeBatch(db);
@@ -227,24 +235,22 @@ export default function App() {
<Shirt className="text-white w-12 h-12" /> <Shirt className="text-white w-12 h-12" />
</div> </div>
<h1 className="text-5xl font-black tracking-tighter italic">MyCloset</h1> <h1 className="text-5xl font-black tracking-tighter italic">MyCloset</h1>
<p className="text-gray-400 mt-3 font-bold uppercase tracking-widest text-[10px]">O Futuro do Teu Estilo</p> <p className="text-gray-400 mt-3 font-bold uppercase tracking-widest text-[10px]">{t('loginModeIntro')}</p>
</div> </div>
{authError && <div className="mb-6 p-4 bg-red-50 text-red-600 text-[10px] rounded-2xl flex items-center gap-2 font-black uppercase tracking-widest border border-red-100"><AlertCircle size={16} /> {authError}</div>} {authError && <div className="mb-6 p-4 bg-red-50 text-red-600 text-[10px] rounded-2xl flex items-center gap-2 font-black uppercase tracking-widest border border-red-100"><AlertCircle size={16} /> {authError}</div>}
<form onSubmit={handleAuth} className="space-y-4"> <form onSubmit={handleAuth} className="space-y-4">
<input name="email" type="email" placeholder="E-mail" required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-indigo-500 outline-none font-bold" /> <input name="email" type="email" placeholder={t('emailPlaceholder')} required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-indigo-500 outline-none font-bold" />
<input name="password" type="password" placeholder="Palavra-passe" required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-indigo-500 outline-none font-bold" /> <input name="password" type="password" placeholder={t('passwordPlaceholder')} required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-indigo-500 outline-none font-bold" />
<button className="w-full py-5 bg-indigo-600 text-white rounded-[2rem] font-black text-lg shadow-2xl hover:scale-[1.02] active:scale-95 transition-all"> <button className="w-full py-5 bg-indigo-600 text-white rounded-[2rem] font-black text-lg shadow-2xl hover:scale-[1.02] active:scale-95 transition-all">
{authMode === 'login' ? 'ENTRAR' : 'REGISTAR'} {authMode === 'login' ? t('loginBtn') : t('registerBtn')}
</button> </button>
</form> </form>
<div className="mt-10 text-center"> <div className="mt-10 text-center">
<button onClick={() => setAuthMode(authMode === 'login' ? 'register' : 'login')} className="text-gray-400 font-black text-[10px] uppercase tracking-[0.3em] hover:text-indigo-600 transition-colors text-inherit"> <button onClick={() => setAuthMode(authMode === 'login' ? 'register' : 'login')} className="text-gray-400 font-black text-[10px] uppercase tracking-[0.3em] hover:text-indigo-600 transition-colors text-inherit">
{authMode === 'login' ? 'Criar Nova Conta' : 'Já Tenho Conta'} {authMode === 'login' ? t('createAccount') : t('haveAccount')}
</button> </button>
</div> </div>
</Card> </Card>
@@ -271,11 +277,11 @@ export default function App() {
<nav className="flex-1 space-y-3"> <nav className="flex-1 space-y-3">
{[ {[
{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard }, { id: 'dashboard', label: t('dashboard'), icon: LayoutDashboard },
{ id: 'closet', label: 'Armário', icon: Shirt }, { id: 'closet', label: t('closet'), icon: Shirt },
{ id: 'laundry', label: 'Lavandaria', icon: Droplets }, { id: 'laundry', label: t('laundry'), icon: Droplets },
{ id: 'outfits', label: 'Looks', icon: Sparkles }, { id: 'outfits', label: t('outfits'), icon: Sparkles },
{ id: 'settings', label: 'Definições', icon: Settings }, { id: 'settings', label: t('settings'), icon: Settings },
].map(item => ( ].map(item => (
<button <button
key={item.id} key={item.id}
@@ -294,12 +300,12 @@ export default function App() {
{user?.email?.[0]?.toUpperCase() || 'U'} {user?.email?.[0]?.toUpperCase() || 'U'}
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="text-sm font-black truncate">{user?.email?.split('@')[0] || 'Utilizador'}</p> <p className="text-sm font-black truncate">{user?.email?.split('@')[0] || t('userTitle')}</p>
<Badge variant="success">Online</Badge> <Badge variant="success">{t('online')}</Badge>
</div> </div>
</div> </div>
<button onClick={() => signOut(auth)} className="w-full py-4 text-red-500 font-black uppercase tracking-widest text-[10px] hover:bg-red-500/10 rounded-2xl transition-all flex items-center justify-center gap-3"> <button onClick={() => signOut(auth)} className="w-full py-4 text-red-500 font-black uppercase tracking-widest text-[10px] hover:bg-red-500/10 rounded-2xl transition-all flex items-center justify-center gap-3">
<LogOut size={16} /> Sair do Sistema <LogOut size={16} /> {t('logout')}
</button> </button>
</div> </div>
</div> </div>
@@ -315,11 +321,11 @@ export default function App() {
{sidebarOpen ? <PanelLeftClose size={24} /> : <PanelLeftOpen size={24} />} {sidebarOpen ? <PanelLeftClose size={24} /> : <PanelLeftOpen size={24} />}
</button> </button>
<h2 className="text-3xl font-black tracking-tighter"> <h2 className="text-3xl font-black tracking-tighter">
{view === 'dashboard' && 'Visão Geral'} {view === 'dashboard' && t('overview')}
{view === 'closet' && 'O Meu Armário'} {view === 'closet' && t('myCloset')}
{view === 'laundry' && 'Lavandaria'} {view === 'laundry' && t('laundry')}
{view === 'outfits' && 'Looks & Estilo'} {view === 'outfits' && t('outfitsAndStyle')}
{view === 'settings' && 'Definições'} {view === 'settings' && t('settings')}
</h2> </h2>
</div> </div>
@@ -342,10 +348,10 @@ export default function App() {
<div className="space-y-12 animate-in fade-in duration-700"> <div className="space-y-12 animate-in fade-in duration-700">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{[ {[
{ label: 'Roupas Prontas', val: activeClothes.length, icon: Shirt, col: 'indigo' }, { label: t('readyClothes'), val: activeClothes.length, icon: Shirt, col: 'indigo' },
{ label: 'Na Lavandaria', val: laundryClothes.length, icon: Droplets, col: 'blue' }, { label: t('inLaundry'), val: laundryClothes.length, icon: Droplets, col: 'blue' },
{ label: 'Meus Looks', val: looks.length, icon: Sparkles, col: 'purple' }, { label: t('myLooks'), val: looks.length, icon: Sparkles, col: 'purple' },
{ label: 'Favoritos', val: activeClothes.filter(c => c.favorite).length, icon: Heart, col: 'rose' }, { label: t('favorites'), val: activeClothes.filter(c => c.favorite).length, icon: Heart, col: 'rose' },
].map((s, i) => ( ].map((s, i) => (
<Card key={i} className="p-8 group hover:-translate-y-2" darkMode={darkMode}> <Card key={i} className="p-8 group hover:-translate-y-2" darkMode={darkMode}>
<div className={`w-14 h-14 rounded-2xl flex items-center justify-center mb-6 shadow-inner ${darkMode ? 'bg-gray-700 text-indigo-400' : 'bg-indigo-50 text-indigo-600'}`}> <div className={`w-14 h-14 rounded-2xl flex items-center justify-center mb-6 shadow-inner ${darkMode ? 'bg-gray-700 text-indigo-400' : 'bg-indigo-50 text-indigo-600'}`}>
@@ -363,11 +369,11 @@ export default function App() {
<div> <div>
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<CloudSun size={28} className="text-indigo-200" /> <CloudSun size={28} className="text-indigo-200" />
<Badge variant="warning">Hoje em Portugal</Badge> <Badge variant="warning">{t('todayIn')}</Badge>
</div> </div>
<h3 className="text-5xl font-black tracking-tighter mb-4">22°C - Ensolarado</h3> <h3 className="text-5xl font-black tracking-tighter mb-4">{t('weatherUpdate')}</h3>
<p className="text-indigo-100 text-lg font-medium max-w-lg leading-relaxed"> <p className="text-indigo-100 text-lg font-medium max-w-lg leading-relaxed">
Está um dia fantástico! Recomendamos as tuas peças leves. Que tal um visual casual com as tuas sapatilhas favoritas? {t('weatherMsg')}
</p> </p>
</div> </div>
<div className="mt-10 flex gap-4"> <div className="mt-10 flex gap-4">
@@ -377,7 +383,7 @@ export default function App() {
</div> </div>
))} ))}
<button onClick={() => setView('closet')} className="flex items-center gap-2 font-black uppercase text-xs tracking-widest hover:translate-x-2 transition-transform"> <button onClick={() => setView('closet')} className="flex items-center gap-2 font-black uppercase text-xs tracking-widest hover:translate-x-2 transition-transform">
Explorar Sugestões <ArrowRight size={18} /> {t('exploreSuggestions')} <ArrowRight size={18} />
</button> </button>
</div> </div>
</div> </div>
@@ -385,9 +391,9 @@ export default function App() {
</Card> </Card>
<Card className="p-8" darkMode={darkMode}> <Card className="p-8" darkMode={darkMode}>
<h3 className="text-lg font-black tracking-tight mb-8 flex items-center gap-2 text-inherit"><PieChart size={20} /> Top Cores</h3> <h3 className="text-lg font-black tracking-tight mb-8 flex items-center gap-2 text-inherit"><PieChart size={20} /> {t('topColors')}</h3>
<div className="space-y-6"> <div className="space-y-6">
{['Preto', 'Branco', 'Azul'].map(color => ( {[t('colorBlack'), t('colorWhite'), t('colorBlue')].map(color => (
<div key={color} className="space-y-2"> <div key={color} className="space-y-2">
<div className="flex justify-between text-[10px] font-black uppercase tracking-widest opacity-50"> <div className="flex justify-between text-[10px] font-black uppercase tracking-widest opacity-50">
<span>{color}</span> <span>{color}</span>
@@ -411,14 +417,14 @@ export default function App() {
<div className="relative w-full max-w-2xl"> <div className="relative w-full max-w-2xl">
<Search className="absolute left-6 top-1/2 -translate-y-1/2 text-gray-400" size={24} /> <Search className="absolute left-6 top-1/2 -translate-y-1/2 text-gray-400" size={24} />
<input <input
placeholder="Procurar no meu guarda-roupa..." placeholder={t('searchPlaceholder')}
className={`w-full pl-16 pr-8 py-6 rounded-[2rem] shadow-inner outline-none border-none focus:ring-4 focus:ring-indigo-500/10 font-bold text-lg ${darkMode ? 'bg-gray-800' : 'bg-gray-100'}`} className={`w-full pl-16 pr-8 py-6 rounded-[2rem] shadow-inner outline-none border-none focus:ring-4 focus:ring-indigo-500/10 font-bold text-lg ${darkMode ? 'bg-gray-800' : 'bg-gray-100'}`}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
</div> </div>
<div className="flex gap-3 overflow-x-auto pb-4 w-full xl:w-auto custom-scrollbar"> <div className="flex gap-3 overflow-x-auto pb-4 w-full xl:w-auto custom-scrollbar">
{['Todos', 'Tops', 'Bottoms', 'Calçado', 'Casacos', 'Acessórios'].map(cat => ( {[t('all'), t('tops'), t('bottoms'), t('footwear'), t('coats'), t('accessories')].map(cat => (
<button <button
key={cat} key={cat}
onClick={() => setCategoryFilter(cat)} onClick={() => setCategoryFilter(cat)}
@@ -438,9 +444,9 @@ export default function App() {
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex flex-col justify-end p-8 text-white"> <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex flex-col justify-end p-8 text-white">
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<button onClick={() => { setEditingItem(item); setImageUrlDraft(''); setView('edit'); }} className="py-4 bg-white text-indigo-600 rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-indigo-50"><Edit2 size={16} /> Editar</button> <button onClick={() => { setEditingItem(item); setImageUrlDraft(''); setView('edit'); }} className="py-4 bg-white text-indigo-600 rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-indigo-50"><Edit2 size={16} /> {t('edit')}</button>
<button onClick={() => handleItemAction('laundry', item)} className="py-4 bg-blue-600 text-white rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-blue-700"><Droplets size={16} /> Sujar</button> <button onClick={() => handleItemAction('laundry', item)} className="py-4 bg-blue-600 text-white rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-blue-700"><Droplets size={16} /> {t('makeDirty')}</button>
<button onClick={() => handleItemAction('trash', item)} className="py-4 bg-red-600/20 text-red-100 backdrop-blur-md rounded-2xl font-black text-[10px] uppercase hover:bg-red-600 transition-colors col-span-2">Mover para Lixo</button> <button onClick={() => handleItemAction('trash', item)} className="py-4 bg-red-600/20 text-red-100 backdrop-blur-md rounded-2xl font-black text-[10px] uppercase hover:bg-red-600 transition-colors col-span-2">{t('moveToTrash')}</button>
</div> </div>
</div> </div>
@@ -472,8 +478,8 @@ export default function App() {
<div className="w-20 h-20 bg-blue-100 dark:bg-blue-900/30 rounded-[2rem] flex items-center justify-center mx-auto text-blue-600 shadow-inner"> <div className="w-20 h-20 bg-blue-100 dark:bg-blue-900/30 rounded-[2rem] flex items-center justify-center mx-auto text-blue-600 shadow-inner">
<Droplets size={40} /> <Droplets size={40} />
</div> </div>
<h3 className="text-4xl font-black tracking-tight">Cesto da Roupa</h3> <h3 className="text-4xl font-black tracking-tight">{t('laundryBasket')}</h3>
<p className="opacity-60 font-medium">Aqui encontras as peças que marcaste como sujas. Lava-as para que voltem ao armário principal.</p> <p className="opacity-60 font-medium">{t('laundryMsg')}</p>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8">
@@ -482,7 +488,7 @@ export default function App() {
<img src={item.imageUrl} className="w-20 h-20 rounded-2xl object-cover shadow-lg" alt="" /> <img src={item.imageUrl} className="w-20 h-20 rounded-2xl object-cover shadow-lg" alt="" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="font-black truncate">{item.name}</p> <p className="font-black truncate">{item.name}</p>
<Badge variant="warning">A lavar</Badge> <Badge variant="warning">{t('washing')}</Badge>
</div> </div>
<button onClick={() => handleItemAction('clean', item)} className="p-4 bg-green-500 text-white rounded-2xl shadow-lg shadow-green-500/30 hover:scale-110 transition-all"> <button onClick={() => handleItemAction('clean', item)} className="p-4 bg-green-500 text-white rounded-2xl shadow-lg shadow-green-500/30 hover:scale-110 transition-all">
<CheckCircle2 size={24} /> <CheckCircle2 size={24} />
@@ -490,7 +496,7 @@ export default function App() {
</Card> </Card>
))} ))}
{laundryClothes.length === 0 && ( {laundryClothes.length === 0 && (
<div className="col-span-full py-20 text-center opacity-20 font-black uppercase tracking-[0.5em] text-sm">Cesto Vazio</div> <div className="col-span-full py-20 text-center opacity-20 font-black uppercase tracking-[0.5em] text-sm">{t('emptyBasket')}</div>
)} )}
</div> </div>
</div> </div>
@@ -502,11 +508,11 @@ export default function App() {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
<div className="lg:col-span-1 space-y-8"> <div className="lg:col-span-1 space-y-8">
<Card className="p-8 border-indigo-200" darkMode={darkMode}> <Card className="p-8 border-indigo-200" darkMode={darkMode}>
<h3 className="text-2xl font-black tracking-tighter mb-6 flex items-center gap-3 text-inherit"><Sparkles className="text-indigo-600" /> Criar Novo Look</h3> <h3 className="text-2xl font-black tracking-tighter mb-6 flex items-center gap-3 text-inherit"><Sparkles className="text-indigo-600" /> {t('createNewLook')}</h3>
<form onSubmit={createLook} className="space-y-6"> <form onSubmit={createLook} className="space-y-6">
<input name="lookName" placeholder="Nome do Look" required className={`w-full p-4 rounded-xl border-none shadow-inner font-bold ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`} /> <input name="lookName" placeholder={t('lookName')} required className={`w-full p-4 rounded-xl border-none shadow-inner font-bold ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`} />
<div className="space-y-3"> <div className="space-y-3">
<p className="text-[10px] font-black uppercase opacity-40 tracking-widest">Peças Selecionadas ({selectedForLook.length})</p> <p className="text-[10px] font-black uppercase opacity-40 tracking-widest">{t('selectedPieces')} ({selectedForLook.length})</p>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{selectedForLook.map(id => { {selectedForLook.map(id => {
const item = clothes.find(c => c.id === id); const item = clothes.find(c => c.id === id);
@@ -517,17 +523,17 @@ export default function App() {
</div> </div>
); );
})} })}
{selectedForLook.length === 0 && <p className="text-xs text-gray-400 italic">Seleciona peças...</p>} {selectedForLook.length === 0 && <p className="text-xs text-gray-400 italic">{t('selectPieces')}</p>}
</div> </div>
</div> </div>
<button disabled={selectedForLook.length < 2} className="w-full py-4 bg-indigo-600 text-white rounded-2xl font-black uppercase tracking-widest text-xs shadow-xl shadow-indigo-600/30 disabled:opacity-30 transition-all"> <button disabled={selectedForLook.length < 2} className="w-full py-4 bg-indigo-600 text-white rounded-2xl font-black uppercase tracking-widest text-xs shadow-xl shadow-indigo-600/30 disabled:opacity-30 transition-all">
Guardar Look {t('saveLook')}
</button> </button>
</form> </form>
</Card> </Card>
<div className="space-y-4"> <div className="space-y-4">
<p className="text-xs font-black uppercase opacity-50 tracking-widest px-2">Armário</p> <p className="text-xs font-black uppercase opacity-50 tracking-widest px-2">{t('closetLabel')}</p>
<div className="grid grid-cols-4 gap-3 max-h-96 overflow-y-auto pr-2 custom-scrollbar"> <div className="grid grid-cols-4 gap-3 max-h-96 overflow-y-auto pr-2 custom-scrollbar">
{activeClothes.map(c => ( {activeClothes.map(c => (
<button key={c.id} onClick={() => !selectedForLook.includes(c.id) && setSelectedForLook([...selectedForLook, c.id])} className={`relative rounded-xl overflow-hidden aspect-square border-2 transition-all ${selectedForLook.includes(c.id) ? 'border-indigo-600 scale-90' : 'border-transparent hover:border-indigo-200'}`}> <button key={c.id} onClick={() => !selectedForLook.includes(c.id) && setSelectedForLook([...selectedForLook, c.id])} className={`relative rounded-xl overflow-hidden aspect-square border-2 transition-all ${selectedForLook.includes(c.id) ? 'border-indigo-600 scale-90' : 'border-transparent hover:border-indigo-200'}`}>
@@ -540,14 +546,14 @@ export default function App() {
</div> </div>
<div className="lg:col-span-2 space-y-8"> <div className="lg:col-span-2 space-y-8">
<h3 className="text-2xl font-black tracking-tighter flex items-center gap-3 px-2 text-inherit"><History className="text-gray-400" /> Histórico de Looks</h3> <h3 className="text-2xl font-black tracking-tighter flex items-center gap-3 px-2 text-inherit"><History className="text-gray-400" /> {t('lookHistory')}</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{looks.map(look => ( {looks.map(look => (
<Card key={look.id} className="p-8 group hover:shadow-2xl transition-all border-none shadow-md" darkMode={darkMode}> <Card key={look.id} className="p-8 group hover:shadow-2xl transition-all border-none shadow-md" darkMode={darkMode}>
<div className="flex justify-between items-start mb-6"> <div className="flex justify-between items-start mb-6">
<div className="text-inherit"> <div className="text-inherit">
<h4 className="text-xl font-black tracking-tight">{look.name}</h4> <h4 className="text-xl font-black tracking-tight">{look.name}</h4>
<p className="text-[10px] opacity-40 font-bold uppercase tracking-widest">{look.items.length} Peças {new Date(look.createdAt).toLocaleDateString()}</p> <p className="text-[10px] opacity-40 font-bold uppercase tracking-widest">{look.items.length} {t('pieces')} {new Date(look.createdAt).toLocaleDateString()}</p>
</div> </div>
<button onClick={() => deleteLook(look.id)} className="p-2 text-gray-300 hover:text-red-500 transition-colors"><Trash size={18} /></button> <button onClick={() => deleteLook(look.id)} className="p-2 text-gray-300 hover:text-red-500 transition-colors"><Trash size={18} /></button>
</div> </div>
@@ -574,14 +580,14 @@ export default function App() {
<div className="max-w-4xl mx-auto animate-in zoom-in-95 duration-500"> <div className="max-w-4xl mx-auto animate-in zoom-in-95 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-start"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-start">
<div className="space-y-8"> <div className="space-y-8">
<h3 className="text-5xl font-black tracking-tighter text-inherit">{editingItem ? 'Editar' : 'Novo Item'}</h3> <h3 className="text-5xl font-black tracking-tighter text-inherit">{editingItem ? t('edit') : t('newItem')}</h3>
<Card className="aspect-[3/4] overflow-hidden shadow-2xl relative" darkMode={darkMode}> <Card className="aspect-[3/4] overflow-hidden shadow-2xl relative" darkMode={darkMode}>
{editingItem?.imageUrl || imageUrlDraft.startsWith('http') ? ( {editingItem?.imageUrl || imageUrlDraft.startsWith('http') ? (
<img src={imageUrlDraft || editingItem?.imageUrl} className="w-full h-full object-cover" alt="" /> <img src={imageUrlDraft || editingItem?.imageUrl} className="w-full h-full object-cover" alt="" />
) : ( ) : (
<div className="h-full flex flex-col items-center justify-center opacity-10"> <div className="h-full flex flex-col items-center justify-center opacity-10">
<ImageIcon size={100} /> <ImageIcon size={100} />
<p className="font-black uppercase tracking-[0.5em] mt-6">Preview</p> <p className="font-black uppercase tracking-[0.5em] mt-6">{t('preview')}</p>
</div> </div>
)} )}
</Card> </Card>
@@ -589,22 +595,22 @@ export default function App() {
<Card className="p-10 shadow-2xl" darkMode={darkMode}> <Card className="p-10 shadow-2xl" darkMode={darkMode}>
<form onSubmit={saveItem} className="space-y-8"> <form onSubmit={saveItem} className="space-y-8">
<Input label="Nome" name="name" defaultValue={editingItem?.name} required /> <Input label={t('name')} name="name" defaultValue={editingItem?.name} required />
<div className="grid grid-cols-2 gap-6"> <div className="grid grid-cols-2 gap-6">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">Categoria</label> <label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('category')}</label>
<select name="category" defaultValue={editingItem?.category || 'Tops'} className={`w-full p-5 rounded-2xl border-none outline-none focus:ring-4 focus:ring-indigo-500/10 font-bold ${darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100'}`}> <select name="category" defaultValue={editingItem?.category || 'Tops'} className={`w-full p-5 rounded-2xl border-none outline-none focus:ring-4 focus:ring-indigo-500/10 font-bold ${darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100'}`}>
<option>Tops</option><option>Bottoms</option><option>Calçado</option><option>Casacos</option><option>Acessórios</option> <option>{t('tops')}</option><option>{t('bottoms')}</option><option>{t('footwear')}</option><option>{t('coats')}</option><option>{t('accessories')}</option>
</select> </select>
</div> </div>
<Input label="Cor" name="color" defaultValue={editingItem?.color} required /> <Input label={t('color')} name="color" defaultValue={editingItem?.color} required />
</div> </div>
<Input label="URL da Imagem" name="imageUrl" defaultValue={editingItem?.imageUrl} onChange={(v) => setImageUrlDraft(v)} /> <Input label={t('imageUrl')} name="imageUrl" defaultValue={editingItem?.imageUrl} onChange={(v) => setImageUrlDraft(v)} />
<div className="flex gap-4 pt-6"> <div className="flex gap-4 pt-6">
<button type="button" onClick={() => { setEditingItem(null); setImageUrlDraft(''); setView('closet'); }} className="flex-1 font-black uppercase text-[10px] opacity-40 hover:opacity-100 tracking-widest transition-all text-inherit">Cancelar</button> <button type="button" onClick={() => { setEditingItem(null); setImageUrlDraft(''); setView('closet'); }} className="flex-1 font-black uppercase text-[10px] opacity-40 hover:opacity-100 tracking-widest transition-all text-inherit">{t('cancel')}</button>
<button type="submit" className="flex-1 py-5 bg-indigo-600 text-white rounded-[2rem] font-black uppercase tracking-widest text-[10px] shadow-2xl shadow-indigo-600/40 hover:scale-[1.02] active:scale-95 transition-all"> <button type="submit" className="flex-1 py-5 bg-indigo-600 text-white rounded-[2rem] font-black uppercase tracking-widest text-[10px] shadow-2xl shadow-indigo-600/40 hover:scale-[1.02] active:scale-95 transition-all">
{editingItem ? 'Guardar' : 'Registar'} {editingItem ? t('save') : t('register')}
</button> </button>
</div> </div>
</form> </form>
@@ -622,16 +628,78 @@ export default function App() {
{user?.email?.[0]?.toUpperCase() || 'U'} {user?.email?.[0]?.toUpperCase() || 'U'}
</div> </div>
<div> <div>
<h3 className="text-3xl font-black tracking-tighter">A Tua Conta</h3> <h3 className="text-3xl font-black tracking-tighter">{t('yourAccount')}</h3>
<p className="opacity-60 font-bold text-sm">{user?.email || 'Modo PAP'}</p> <p className="opacity-60 font-bold text-sm">{user?.email || t('papMode')}</p>
</div> </div>
</div> </div>
</Card> </Card>
{/* Preferências */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<Card className="p-8" darkMode={darkMode}>
<h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Settings className="text-indigo-600" /> {t('preferences')}</h3>
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<p className="font-bold text-inherit">{t('darkMode')}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('interfaceAppearance')}</p>
</div>
<button onClick={() => setDarkMode(!darkMode)} className={`w-14 h-8 rounded-full transition-colors relative ${darkMode ? 'bg-indigo-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${darkMode ? 'left-7' : 'left-1'}`}></div>
</button>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-bold text-inherit flex items-center gap-2">{t('notifications')}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('lookReminders')}</p>
</div>
<button onClick={() => setNotificationsEnabled(!notificationsEnabled)} className={`w-14 h-8 rounded-full transition-colors relative ${notificationsEnabled ? 'bg-indigo-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${notificationsEnabled ? 'left-7' : 'left-1'}`}></div>
</button>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-bold text-inherit flex items-center gap-2">{t('weatherAlerts')}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('weatherSuggestions')}</p>
</div>
<button onClick={() => setWeatherAlerts(!weatherAlerts)} className={`w-14 h-8 rounded-full transition-colors relative ${weatherAlerts ? 'bg-indigo-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${weatherAlerts ? 'left-7' : 'left-1'}`}></div>
</button>
</div>
</div>
</Card>
<Card className="p-8" darkMode={darkMode}>
<h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Globe className="text-indigo-600" /> {t('systemAndData')}</h3>
<div className="space-y-6">
<div className="space-y-2">
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('appLanguage')}</label>
<select value={language} onChange={(e) => setLanguage(e.target.value)} className={`w-full p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-indigo-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
<option value="PT">{t('portuguese')}</option>
<option value="EN">{t('english')}</option>
<option value="ES">{t('spanish')}</option>
<option value="FR">{t('french')}</option>
<option value="DE">{t('german')}</option>
</select>
</div>
<div>
<button onClick={() => alert(t('exportDataAlert'))} className="w-full py-4 rounded-xl font-black uppercase text-[10px] tracking-widest transition-all bg-indigo-50 text-indigo-600 hover:bg-indigo-100 dark:bg-indigo-900/30 dark:text-indigo-300 dark:hover:bg-indigo-900/50 flex items-center justify-center gap-2">
<Download size={16} /> {t('exportData')}
</button>
</div>
<div>
<button className="w-full py-4 rounded-xl font-black uppercase text-[10px] tracking-widest transition-all bg-gray-100 text-gray-500 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 flex items-center justify-center gap-2">
<ShieldAlert size={16} /> {t('privacyPolicy')}
</button>
</div>
</div>
</Card>
</div>
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center justify-between px-2 text-inherit"> <div className="flex items-center justify-between px-2 text-inherit">
<h3 className="text-xl font-black text-red-500 flex items-center gap-3 tracking-widest uppercase"><Trash2 size={24} /> Reciclagem</h3> <h3 className="text-xl font-black text-red-500 flex items-center gap-3 tracking-widest uppercase"><Trash2 size={24} /> {t('recycleBin')}</h3>
{trashClothes.length > 0 && <button onClick={emptyTrashPermanently} className="text-[10px] font-black text-red-500 uppercase tracking-widest hover:underline">Esvaziar</button>} {trashClothes.length > 0 && <button onClick={emptyTrashPermanently} className="text-[10px] font-black text-red-500 uppercase tracking-widest hover:underline">{t('empty')}</button>}
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{trashClothes.map(item => ( {trashClothes.map(item => (
@@ -639,7 +707,7 @@ export default function App() {
<img src={item.imageUrl} className="w-16 h-16 rounded-2xl object-cover grayscale opacity-40" alt="" /> <img src={item.imageUrl} className="w-16 h-16 rounded-2xl object-cover grayscale opacity-40" alt="" />
<div className="flex-1 min-w-0 text-inherit"> <div className="flex-1 min-w-0 text-inherit">
<p className="font-black text-sm truncate">{item.name}</p> <p className="font-black text-sm truncate">{item.name}</p>
<p className="text-[10px] font-black text-red-400 uppercase tracking-tighter">Eliminado</p> <p className="text-[10px] font-black text-red-400 uppercase tracking-tighter">{t('deleted')}</p>
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1">
<button onClick={() => handleItemAction('restore', item)} className="p-3 text-indigo-600 hover:bg-indigo-50 rounded-2xl transition-all"><RotateCcw size={18} /></button> <button onClick={() => handleItemAction('restore', item)} className="p-3 text-indigo-600 hover:bg-indigo-50 rounded-2xl transition-all"><RotateCcw size={18} /></button>
@@ -653,10 +721,10 @@ export default function App() {
<Card className="p-10 border-red-200 bg-red-50/10" darkMode={darkMode}> <Card className="p-10 border-red-200 bg-red-50/10" darkMode={darkMode}>
<div className="flex flex-col md:flex-row items-center justify-between gap-8"> <div className="flex flex-col md:flex-row items-center justify-between gap-8">
<div className="text-inherit"> <div className="text-inherit">
<h4 className="text-xl font-black text-red-700 flex items-center gap-3"><ShieldAlert /> Zona Crítica</h4> <h4 className="text-xl font-black text-red-700 flex items-center gap-3"><ShieldAlert /> {t('criticalZone')}</h4>
<p className="opacity-60 font-bold text-sm mt-2">Ações de limpeza total do armário.</p> <p className="opacity-60 font-bold text-sm mt-2">{t('fullCleanActions')}</p>
</div> </div>
<button onClick={clearAllToTrash} className="px-8 py-4 bg-red-600 text-white rounded-2xl font-black uppercase text-[10px] tracking-widest hover:bg-red-700 transition-all">Limpar Tudo</button> <button onClick={clearAllToTrash} className="px-8 py-4 bg-red-600 text-white rounded-2xl font-black uppercase text-[10px] tracking-widest hover:bg-red-700 transition-all">{t('clearAll')}</button>
</div> </div>
</Card> </Card>
</div> </div>

468
src/lib/i18n.js Normal file
View File

@@ -0,0 +1,468 @@
export const translations = {
PT: {
loginModeIntro: "O Futuro do Teu Estilo",
emailPlaceholder: "E-mail",
passwordPlaceholder: "Palavra-passe",
loginBtn: "ENTRAR",
registerBtn: "REGISTAR",
createAccount: "Criar Nova Conta",
haveAccount: "Já Tenho Conta",
authErrorDisabled: "O login por e-mail está desativado.",
dashboard: "Dashboard",
closet: "Armário",
laundry: "Lavandaria",
outfits: "Looks",
settings: "Definições",
online: "Online",
logout: "Sair do Sistema",
overview: "Visão Geral",
myCloset: "O Meu Armário",
outfitsAndStyle: "Looks & Estilo",
readyClothes: "Roupas Prontas",
inLaundry: "Na Lavandaria",
myLooks: "Meus Looks",
favorites: "Favoritos",
todayIn: "Hoje em Portugal",
weatherUpdate: "22°C - Ensolarado",
weatherMsg: "Está um dia fantástico! Recomendamos as tuas peças leves. Que tal um visual casual com as tuas sapatilhas favoritas?",
exploreSuggestions: "Explorar Sugestões",
topColors: "Top Cores",
searchPlaceholder: "Procurar no meu guarda-roupa...",
all: "Todos",
tops: "Tops",
bottoms: "Bottoms",
footwear: "Calçado",
coats: "Casacos",
accessories: "Acessórios",
edit: "Editar",
makeDirty: "Sujar",
moveToTrash: "Mover para Lixo",
laundryBasket: "Cesto da Roupa",
laundryMsg: "Aqui encontras as peças que marcaste como sujas. Lava-as para que voltem ao armário principal.",
washing: "A lavar",
emptyBasket: "Cesto Vazio",
createNewLook: "Criar Novo Look",
lookName: "Nome do Look",
selectedPieces: "Peças Selecionadas",
selectPieces: "Seleciona peças...",
saveLook: "Guardar Look",
closetLabel: "Armário",
lookHistory: "Histórico de Looks",
pieces: "Peças",
newItem: "Novo Item",
preview: "Preview",
name: "Nome",
category: "Categoria",
color: "Cor",
imageUrl: "URL da Imagem",
cancel: "Cancelar",
save: "Guardar",
register: "Registar",
yourAccount: "A Tua Conta",
papMode: "Modo PAP",
preferences: "Preferências",
darkMode: "Modo Escuro",
interfaceAppearance: "Aparência da interface",
notifications: "Notificações",
lookReminders: "Lembretes de looks",
weatherAlerts: "Alertas de Clima",
weatherSuggestions: "Sugestões pelo tempo",
systemAndData: "Sistema e Dados",
appLanguage: "Idioma da Aplicação",
portuguese: "Português (PT)",
english: "English (EN)",
spanish: "Español (ES)",
french: "Français (FR)",
german: "Deutsch (DE)",
exportData: "Exportar Dados (JSON)",
exportDataAlert: "Os teus dados seriam exportados agora em formato JSON.",
privacyPolicy: "Política de Privacidade",
recycleBin: "Reciclagem",
empty: "Esvaziar",
deleted: "Eliminado",
criticalZone: "Zona Crítica",
fullCleanActions: "Ações de limpeza total do armário.",
clearAll: "Limpar Tudo",
confirmDeletePerm: "Apagar permanentemente?",
confirmDeleteLook: "Apagar este Look?",
confirmEmptyTrash: "Esvaziar o lixo permanentemente?",
confirmClearAll: "Mover todas as peças ativas para o lixo?",
colorBlack: "Preto",
colorWhite: "Branco",
colorBlue: "Azul",
userTitle: "Utilizador"
},
EN: {
loginModeIntro: "The Future of Your Style",
emailPlaceholder: "Email",
passwordPlaceholder: "Password",
loginBtn: "LOGIN",
registerBtn: "REGISTER",
createAccount: "Create New Account",
haveAccount: "I Already Have an Account",
authErrorDisabled: "Email login is disabled.",
dashboard: "Dashboard",
closet: "Closet",
laundry: "Laundry",
outfits: "Outfits",
settings: "Settings",
online: "Online",
logout: "Logout",
overview: "Overview",
myCloset: "My Closet",
outfitsAndStyle: "Outfits & Style",
readyClothes: "Ready Clothes",
inLaundry: "In Laundry",
myLooks: "My Looks",
favorites: "Favorites",
todayIn: "Today in Portugal",
weatherUpdate: "22°C - Sunny",
weatherMsg: "It's a fantastic day! We recommend your light pieces. How about a casual look with your favorite sneakers?",
exploreSuggestions: "Explore Suggestions",
topColors: "Top Colors",
searchPlaceholder: "Search my wardrobe...",
all: "All",
tops: "Tops",
bottoms: "Bottoms",
footwear: "Footwear",
coats: "Coats",
accessories: "Accessories",
edit: "Edit",
makeDirty: "Make Dirty",
moveToTrash: "Move to Trash",
laundryBasket: "Laundry Basket",
laundryMsg: "Here you find the pieces you marked as dirty. Wash them to return them to the main closet.",
washing: "Washing",
emptyBasket: "Empty Basket",
createNewLook: "Create New Look",
lookName: "Look Name",
selectedPieces: "Selected Pieces",
selectPieces: "Select pieces...",
saveLook: "Save Look",
closetLabel: "Closet",
lookHistory: "Look History",
pieces: "Pieces",
newItem: "New Item",
preview: "Preview",
name: "Name",
category: "Category",
color: "Color",
imageUrl: "Image URL",
cancel: "Cancel",
save: "Save",
register: "Register",
yourAccount: "Your Account",
papMode: "PAP Mode",
preferences: "Preferences",
darkMode: "Dark Mode",
interfaceAppearance: "Interface Appearance",
notifications: "Notifications",
lookReminders: "Look reminders",
weatherAlerts: "Weather Alerts",
weatherSuggestions: "Weather-based suggestions",
systemAndData: "System and Data",
appLanguage: "App Language",
portuguese: "Português (PT)",
english: "English (EN)",
spanish: "Español (ES)",
french: "Français (FR)",
german: "Deutsch (DE)",
exportData: "Export Data (JSON)",
exportDataAlert: "Your data would be exported now in JSON format.",
privacyPolicy: "Privacy Policy",
recycleBin: "Recycle Bin",
empty: "Empty",
deleted: "Deleted",
criticalZone: "Critical Zone",
fullCleanActions: "Full closet wipe actions.",
clearAll: "Clear All",
confirmDeletePerm: "Delete permanently?",
confirmDeleteLook: "Delete this Look?",
confirmEmptyTrash: "Empty trash permanently?",
confirmClearAll: "Move all active pieces to trash?",
colorBlack: "Black",
colorWhite: "White",
colorBlue: "Blue",
userTitle: "User"
},
ES: {
loginModeIntro: "El Futuro de Tu Estilo",
emailPlaceholder: "Correo electrónico",
passwordPlaceholder: "Contraseña",
loginBtn: "ENTRAR",
registerBtn: "REGISTRAR",
createAccount: "Crear Nueva Cuenta",
haveAccount: "Ya Tengo Cuenta",
authErrorDisabled: "El inicio de sesión por correo electrónico está desactivado.",
dashboard: "Panel",
closet: "Armario",
laundry: "Lavandería",
outfits: "Looks",
settings: "Ajustes",
online: "En línea",
logout: "Cerrar Sesión",
overview: "Visión General",
myCloset: "Mi Armario",
outfitsAndStyle: "Looks y Estilo",
readyClothes: "Ropa Lista",
inLaundry: "En la Lavandería",
myLooks: "Mis Looks",
favorites: "Favoritos",
todayIn: "Hoy en Portugal",
weatherUpdate: "22°C - Soleado",
weatherMsg: "¡Es un día fantástico! Recomendamos tus piezas ligeras. ¿Qué tal un look casual con tus zapatillas favoritas?",
exploreSuggestions: "Explorar Sugerencias",
topColors: "Colores Principales",
searchPlaceholder: "Buscar en mi guardarropa...",
all: "Todos",
tops: "Tops",
bottoms: "Partes Inferiores",
footwear: "Calzado",
coats: "Abrigos",
accessories: "Accesorios",
edit: "Editar",
makeDirty: "Ensuciar",
moveToTrash: "Mover a la Papelera",
laundryBasket: "Cesto de Ropa",
laundryMsg: "Aquí encuentras las piezas que marcaste como sucias. Lávalas para que vuelvan al armario principal.",
washing: "Lavando",
emptyBasket: "Cesto Vacío",
createNewLook: "Crear Nuevo Look",
lookName: "Nombre del Look",
selectedPieces: "Piezas Seleccionadas",
selectPieces: "Elige piezas...",
saveLook: "Guardar Look",
closetLabel: "Armario",
lookHistory: "Historial de Looks",
pieces: "Piezas",
newItem: "Nuevo Artículo",
preview: "Vista Previa",
name: "Nombre",
category: "Categoría",
color: "Color",
imageUrl: "URL de la Imagen",
cancel: "Cancelar",
save: "Guardar",
register: "Registrar",
yourAccount: "Tu Cuenta",
papMode: "Modo PAP",
preferences: "Preferencias",
darkMode: "Modo Oscuro",
interfaceAppearance: "Apariencia de la interfaz",
notifications: "Notificaciones",
lookReminders: "Recordatorios de looks",
weatherAlerts: "Alertas del Clima",
weatherSuggestions: "Sugerencias por clima",
systemAndData: "Sistema y Datos",
appLanguage: "Idioma de la Aplicación",
portuguese: "Português (PT)",
english: "English (EN)",
spanish: "Español (ES)",
french: "Français (FR)",
german: "Deutsch (DE)",
exportData: "Exportar Datos (JSON)",
exportDataAlert: "Tus datos se exportarían ahora en formato JSON.",
privacyPolicy: "Política de Privacidad",
recycleBin: "Papelera de Reciclaje",
empty: "Vaciar",
deleted: "Eliminado",
criticalZone: "Zona Crítica",
fullCleanActions: "Acciones de limpieza total.",
clearAll: "Limpiar Todo",
confirmDeletePerm: "¿Borrar permanentemente?",
confirmDeleteLook: "¿Borrar este Look?",
confirmEmptyTrash: "¿Vaciar la papelera permanentemente?",
confirmClearAll: "¿Mover todas las piezas activas a la papelera?",
colorBlack: "Negro",
colorWhite: "Blanco",
colorBlue: "Azul",
userTitle: "Usuario"
},
FR: {
loginModeIntro: "Le Futur de Ton Style",
emailPlaceholder: "E-mail",
passwordPlaceholder: "Mot de passe",
loginBtn: "CONNEXION",
registerBtn: "S'INSCRIRE",
createAccount: "Créer un Nouveau Compte",
haveAccount: "J'ai Déjà un Compte",
authErrorDisabled: "La connexion par e-mail est désactivée.",
dashboard: "Tableau de bord",
closet: "Placard",
laundry: "Blanchisserie",
outfits: "Tenues",
settings: "Paramètres",
online: "En ligne",
logout: "Déconnexion",
overview: "Vue d'ensemble",
myCloset: "Mon Placard",
outfitsAndStyle: "Tenues & Style",
readyClothes: "Vêtements Prêts",
inLaundry: "À la Blanchisserie",
myLooks: "Mes Looks",
favorites: "Favoris",
todayIn: "Aujourd'hui au Portugal",
weatherUpdate: "22°C - Ensoleillé",
weatherMsg: "C'est une journée fantastique ! Nous recommandons vos pièces légères. Que diriez-vous d'un look décontracté avec vos baskets préférées ?",
exploreSuggestions: "Explorer les Suggestions",
topColors: "Couleurs Principales",
searchPlaceholder: "Chercher dans ma garde-robe...",
all: "Tout",
tops: "Hauts",
bottoms: "Bas",
footwear: "Chaussures",
coats: "Manteaux",
accessories: "Accessoires",
edit: "Modifier",
makeDirty: "Salir",
moveToTrash: "Mettre à la corbeille",
laundryBasket: "Panier à linge",
laundryMsg: "Ici vous trouvez les pièces que vous avez marquées comme sales. Lavez-les pour les remettre dans le placard principal.",
washing: "En lavage",
emptyBasket: "Panier Vide",
createNewLook: "Créer un Nouveau Look",
lookName: "Nom du Look",
selectedPieces: "Pièces Sélectionnées",
selectPieces: "Sélectionnez des pièces...",
saveLook: "Enregistrer le Look",
closetLabel: "Placard",
lookHistory: "Historique des Looks",
pieces: "Pièces",
newItem: "Nouvel Article",
preview: "Aperçu",
name: "Nom",
category: "Catégorie",
color: "Couleur",
imageUrl: "URL de l'image",
cancel: "Annuler",
save: "Enregistrer",
register: "S'inscrire",
yourAccount: "Votre Compte",
papMode: "Mode PAP",
preferences: "Préférences",
darkMode: "Mode Sombre",
interfaceAppearance: "Apparence de l'interface",
notifications: "Notifications",
lookReminders: "Rappels de looks",
weatherAlerts: "Alertes Météo",
weatherSuggestions: "Suggestions selon la météo",
systemAndData: "Système et Données",
appLanguage: "Langue de l'application",
portuguese: "Português (PT)",
english: "English (EN)",
spanish: "Español (ES)",
french: "Français (FR)",
german: "Deutsch (DE)",
exportData: "Exporter les Données (JSON)",
exportDataAlert: "Vos données seraient exportées maintenant au format JSON.",
privacyPolicy: "Politique de Confidentialité",
recycleBin: "Corbeille",
empty: "Vider",
deleted: "Supprimé",
criticalZone: "Zone Critique",
fullCleanActions: "Actions de nettoyage total.",
clearAll: "Tout Effacer",
confirmDeletePerm: "Supprimer définitivement ?",
confirmDeleteLook: "Supprimer ce Look ?",
confirmEmptyTrash: "Vider la corbeille définitivement ?",
confirmClearAll: "Déplacer toutes les pièces actives vers la corbeille ?",
colorBlack: "Noir",
colorWhite: "Blanc",
colorBlue: "Bleu",
userTitle: "Utilisateur"
},
DE: {
loginModeIntro: "Die Zukunft deines Stils",
emailPlaceholder: "E-Mail",
passwordPlaceholder: "Passwort",
loginBtn: "ANMELDEN",
registerBtn: "REGISTRIEREN",
createAccount: "Neues Konto erstellen",
haveAccount: "Ich habe bereits ein Konto",
authErrorDisabled: "E-Mail-Anmeldung ist deaktiviert.",
dashboard: "Dashboard",
closet: "Schrank",
laundry: "Wäsche",
outfits: "Outfits",
settings: "Einstellungen",
online: "Online",
logout: "Abmelden",
overview: "Übersicht",
myCloset: "Mein Schrank",
outfitsAndStyle: "Outfits & Stil",
readyClothes: "Fertige Kleidung",
inLaundry: "In der Wäsche",
myLooks: "Meine Looks",
favorites: "Favoriten",
todayIn: "Heute in Portugal",
weatherUpdate: "22°C - Sonnig",
weatherMsg: "Es ist ein fantastischer Tag! Wir empfehlen leichte Stücke. Wie wäre es mit einem lässigen Look mit deinen Lieblings-Sneakern?",
exploreSuggestions: "Vorschläge entdecken",
topColors: "Top Farben",
searchPlaceholder: "In meiner Garderobe suchen...",
all: "Alle",
tops: "Oberteile",
bottoms: "Unterteile",
footwear: "Schuhe",
coats: "Mäntel",
accessories: "Accessoires",
edit: "Bearbeiten",
makeDirty: "Schmutzig machen",
moveToTrash: "In den Papierkorb verschieben",
laundryBasket: "Wäschekorb",
laundryMsg: "Hier findest du die Stücke, die du als schmutzig markiert hast. Wasche sie, um sie in den Hauptschrank zurückzulegen.",
washing: "Waschen",
emptyBasket: "Leerer Korb",
createNewLook: "Neuen Look erstellen",
lookName: "Look Name",
selectedPieces: "Ausgewählte Stücke",
selectPieces: "Stücke auswählen...",
saveLook: "Look speichern",
closetLabel: "Schrank",
lookHistory: "Look-Verlauf",
pieces: "Stücke",
newItem: "Neuer Artikel",
preview: "Vorschau",
name: "Name",
category: "Kategorie",
color: "Farbe",
imageUrl: "Bild-URL",
cancel: "Abbrechen",
save: "Speichern",
register: "Registrieren",
yourAccount: "Dein Konto",
papMode: "PAP-Modus",
preferences: "Präferenzen",
darkMode: "Dunkelmodus",
interfaceAppearance: "Erscheinungsbild der Schnittstelle",
notifications: "Benachrichtigungen",
lookReminders: "Look-Erinnerungen",
weatherAlerts: "Wetterwarnungen",
weatherSuggestions: "Wetterbasierte Vorschläge",
systemAndData: "System und Daten",
appLanguage: "App-Sprache",
portuguese: "Português (PT)",
english: "English (EN)",
spanish: "Español (ES)",
french: "Français (FR)",
german: "Deutsch (DE)",
exportData: "Daten exportieren (JSON)",
exportDataAlert: "Deine Daten würden jetzt im JSON-Format exportiert werden.",
privacyPolicy: "Datenschutzrichtlinie",
recycleBin: "Papierkorb",
empty: "Leeren",
deleted: "Gelöscht",
criticalZone: "Kritische Zone",
fullCleanActions: "Aktionen zur vollständigen Bereinigung.",
clearAll: "Alles löschen",
confirmDeletePerm: "Dauerhaft löschen?",
confirmDeleteLook: "Diesen Look löschen?",
confirmEmptyTrash: "Papierkorb dauerhaft leeren?",
confirmClearAll: "Alle aktiven Stücke in den Papierkorb verschieben?",
colorBlack: "Schwarz",
colorWhite: "Weiß",
colorBlue: "Blau",
userTitle: "Benutzer"
}
};