config, language
This commit is contained in:
208
src/App.jsx
208
src/App.jsx
@@ -6,7 +6,7 @@ import {
|
||||
Edit2, Image as ImageIcon, Check, RotateCcw, Trash,
|
||||
PanelLeftClose, PanelLeftOpen, Sparkles, CloudSun,
|
||||
ArrowRight, Droplets, CheckCircle2, PieChart, History,
|
||||
X
|
||||
X, Download, Bell, Globe
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
@@ -22,6 +22,7 @@ import { auth, db, appId } from './lib/firebase';
|
||||
import { Card } from './components/ui/Card';
|
||||
import { Badge } from './components/ui/Badge';
|
||||
import { Input } from './components/ui/Input';
|
||||
import { translations } from './lib/i18n';
|
||||
|
||||
export default function App() {
|
||||
const [view, setView] = useState('auth');
|
||||
@@ -41,6 +42,13 @@ export default function App() {
|
||||
// Estado para criação de Looks
|
||||
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
|
||||
useEffect(() => {
|
||||
const initAuth = async () => {
|
||||
@@ -103,7 +111,7 @@ export default function App() {
|
||||
case 'restore': await updateDoc(docRef, { status: 'active', trashedAt: null }); break;
|
||||
case 'laundry': await updateDoc(docRef, { status: 'laundry' }); 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) => {
|
||||
if (!window.confirm("Apagar este Look?")) return;
|
||||
if (!window.confirm(t('confirmDeleteLook'))) return;
|
||||
const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'looks', id);
|
||||
await deleteDoc(docRef);
|
||||
};
|
||||
@@ -179,7 +187,7 @@ export default function App() {
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (err.code === 'auth/operation-not-allowed') {
|
||||
setAuthError('O login por e-mail está desativado.');
|
||||
setAuthError(t('authErrorDisabled'));
|
||||
} else {
|
||||
setAuthError(err.message);
|
||||
}
|
||||
@@ -189,7 +197,7 @@ export default function App() {
|
||||
|
||||
|
||||
const emptyTrashPermanently = async () => {
|
||||
if (!user || !window.confirm("Esvaziar o lixo permanentemente?")) return;
|
||||
if (!user || !window.confirm(t('confirmEmptyTrash'))) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const batch = writeBatch(db);
|
||||
@@ -203,7 +211,7 @@ export default function App() {
|
||||
};
|
||||
|
||||
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);
|
||||
try {
|
||||
const batch = writeBatch(db);
|
||||
@@ -227,24 +235,22 @@ export default function App() {
|
||||
<Shirt className="text-white w-12 h-12" />
|
||||
</div>
|
||||
<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>
|
||||
|
||||
{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">
|
||||
<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="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="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={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">
|
||||
{authMode === 'login' ? 'ENTRAR' : 'REGISTAR'}
|
||||
{authMode === 'login' ? t('loginBtn') : t('registerBtn')}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<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">
|
||||
{authMode === 'login' ? 'Criar Nova Conta' : 'Já Tenho Conta'}
|
||||
{authMode === 'login' ? t('createAccount') : t('haveAccount')}
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -271,11 +277,11 @@ export default function App() {
|
||||
|
||||
<nav className="flex-1 space-y-3">
|
||||
{[
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ id: 'closet', label: 'Armário', icon: Shirt },
|
||||
{ id: 'laundry', label: 'Lavandaria', icon: Droplets },
|
||||
{ id: 'outfits', label: 'Looks', icon: Sparkles },
|
||||
{ id: 'settings', label: 'Definições', icon: Settings },
|
||||
{ id: 'dashboard', label: t('dashboard'), icon: LayoutDashboard },
|
||||
{ id: 'closet', label: t('closet'), icon: Shirt },
|
||||
{ id: 'laundry', label: t('laundry'), icon: Droplets },
|
||||
{ id: 'outfits', label: t('outfits'), icon: Sparkles },
|
||||
{ id: 'settings', label: t('settings'), icon: Settings },
|
||||
].map(item => (
|
||||
<button
|
||||
key={item.id}
|
||||
@@ -294,12 +300,12 @@ export default function App() {
|
||||
{user?.email?.[0]?.toUpperCase() || 'U'}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-black truncate">{user?.email?.split('@')[0] || 'Utilizador'}</p>
|
||||
<Badge variant="success">Online</Badge>
|
||||
<p className="text-sm font-black truncate">{user?.email?.split('@')[0] || t('userTitle')}</p>
|
||||
<Badge variant="success">{t('online')}</Badge>
|
||||
</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">
|
||||
<LogOut size={16} /> Sair do Sistema
|
||||
<LogOut size={16} /> {t('logout')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -315,11 +321,11 @@ export default function App() {
|
||||
{sidebarOpen ? <PanelLeftClose size={24} /> : <PanelLeftOpen size={24} />}
|
||||
</button>
|
||||
<h2 className="text-3xl font-black tracking-tighter">
|
||||
{view === 'dashboard' && 'Visão Geral'}
|
||||
{view === 'closet' && 'O Meu Armário'}
|
||||
{view === 'laundry' && 'Lavandaria'}
|
||||
{view === 'outfits' && 'Looks & Estilo'}
|
||||
{view === 'settings' && 'Definições'}
|
||||
{view === 'dashboard' && t('overview')}
|
||||
{view === 'closet' && t('myCloset')}
|
||||
{view === 'laundry' && t('laundry')}
|
||||
{view === 'outfits' && t('outfitsAndStyle')}
|
||||
{view === 'settings' && t('settings')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -342,10 +348,10 @@ export default function App() {
|
||||
<div className="space-y-12 animate-in fade-in duration-700">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||
{[
|
||||
{ label: 'Roupas Prontas', val: activeClothes.length, icon: Shirt, col: 'indigo' },
|
||||
{ label: 'Na Lavandaria', val: laundryClothes.length, icon: Droplets, col: 'blue' },
|
||||
{ label: 'Meus Looks', val: looks.length, icon: Sparkles, col: 'purple' },
|
||||
{ label: 'Favoritos', val: activeClothes.filter(c => c.favorite).length, icon: Heart, col: 'rose' },
|
||||
{ label: t('readyClothes'), val: activeClothes.length, icon: Shirt, col: 'indigo' },
|
||||
{ label: t('inLaundry'), val: laundryClothes.length, icon: Droplets, col: 'blue' },
|
||||
{ label: t('myLooks'), val: looks.length, icon: Sparkles, col: 'purple' },
|
||||
{ label: t('favorites'), val: activeClothes.filter(c => c.favorite).length, icon: Heart, col: 'rose' },
|
||||
].map((s, i) => (
|
||||
<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'}`}>
|
||||
@@ -363,11 +369,11 @@ export default function App() {
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<CloudSun size={28} className="text-indigo-200" />
|
||||
<Badge variant="warning">Hoje em Portugal</Badge>
|
||||
<Badge variant="warning">{t('todayIn')}</Badge>
|
||||
</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">
|
||||
Está um dia fantástico! Recomendamos as tuas peças leves. Que tal um visual casual com as tuas sapatilhas favoritas?
|
||||
{t('weatherMsg')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-10 flex gap-4">
|
||||
@@ -377,7 +383,7 @@ export default function App() {
|
||||
</div>
|
||||
))}
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -385,9 +391,9 @@ export default function App() {
|
||||
</Card>
|
||||
|
||||
<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">
|
||||
{['Preto', 'Branco', 'Azul'].map(color => (
|
||||
{[t('colorBlack'), t('colorWhite'), t('colorBlue')].map(color => (
|
||||
<div key={color} className="space-y-2">
|
||||
<div className="flex justify-between text-[10px] font-black uppercase tracking-widest opacity-50">
|
||||
<span>{color}</span>
|
||||
@@ -411,14 +417,14 @@ export default function App() {
|
||||
<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} />
|
||||
<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'}`}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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
|
||||
key={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="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={() => 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('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={() => { 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} /> {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">{t('moveToTrash')}</button>
|
||||
</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">
|
||||
<Droplets size={40} />
|
||||
</div>
|
||||
<h3 className="text-4xl font-black tracking-tight">Cesto da Roupa</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>
|
||||
<h3 className="text-4xl font-black tracking-tight">{t('laundryBasket')}</h3>
|
||||
<p className="opacity-60 font-medium">{t('laundryMsg')}</p>
|
||||
</div>
|
||||
|
||||
<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="" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-black truncate">{item.name}</p>
|
||||
<Badge variant="warning">A lavar</Badge>
|
||||
<Badge variant="warning">{t('washing')}</Badge>
|
||||
</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">
|
||||
<CheckCircle2 size={24} />
|
||||
@@ -490,7 +496,7 @@ export default function App() {
|
||||
</Card>
|
||||
))}
|
||||
{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>
|
||||
@@ -502,11 +508,11 @@ export default function App() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||||
<div className="lg:col-span-1 space-y-8">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
{selectedForLook.map(id => {
|
||||
const item = clothes.find(c => c.id === id);
|
||||
@@ -517,17 +523,17 @@ export default function App() {
|
||||
</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>
|
||||
<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>
|
||||
</form>
|
||||
</Card>
|
||||
|
||||
<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">
|
||||
{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'}`}>
|
||||
@@ -540,14 +546,14 @@ export default function App() {
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{looks.map(look => (
|
||||
<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="text-inherit">
|
||||
<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>
|
||||
<button onClick={() => deleteLook(look.id)} className="p-2 text-gray-300 hover:text-red-500 transition-colors"><Trash size={18} /></button>
|
||||
</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="grid grid-cols-1 lg:grid-cols-2 gap-12 items-start">
|
||||
<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}>
|
||||
{editingItem?.imageUrl || imageUrlDraft.startsWith('http') ? (
|
||||
<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">
|
||||
<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>
|
||||
)}
|
||||
</Card>
|
||||
@@ -589,22 +595,22 @@ export default function App() {
|
||||
|
||||
<Card className="p-10 shadow-2xl" darkMode={darkMode}>
|
||||
<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="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'}`}>
|
||||
<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>
|
||||
</div>
|
||||
<Input label="Cor" name="color" defaultValue={editingItem?.color} required />
|
||||
<Input label={t('color')} name="color" defaultValue={editingItem?.color} required />
|
||||
</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">
|
||||
<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">
|
||||
{editingItem ? 'Guardar' : 'Registar'}
|
||||
{editingItem ? t('save') : t('register')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -622,16 +628,78 @@ export default function App() {
|
||||
{user?.email?.[0]?.toUpperCase() || 'U'}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-3xl font-black tracking-tighter">A Tua Conta</h3>
|
||||
<p className="opacity-60 font-bold text-sm">{user?.email || 'Modo PAP'}</p>
|
||||
<h3 className="text-3xl font-black tracking-tighter">{t('yourAccount')}</h3>
|
||||
<p className="opacity-60 font-bold text-sm">{user?.email || t('papMode')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</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="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>
|
||||
{trashClothes.length > 0 && <button onClick={emptyTrashPermanently} className="text-[10px] font-black text-red-500 uppercase tracking-widest hover:underline">Esvaziar</button>}
|
||||
<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">{t('empty')}</button>}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{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="" />
|
||||
<div className="flex-1 min-w-0 text-inherit">
|
||||
<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 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>
|
||||
@@ -653,10 +721,10 @@ export default function App() {
|
||||
<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="text-inherit">
|
||||
<h4 className="text-xl font-black text-red-700 flex items-center gap-3"><ShieldAlert /> Zona Crítica</h4>
|
||||
<p className="opacity-60 font-bold text-sm mt-2">Ações de limpeza total do armário.</p>
|
||||
<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">{t('fullCleanActions')}</p>
|
||||
</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>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
468
src/lib/i18n.js
Normal file
468
src/lib/i18n.js
Normal 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"
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user