feat: scaffold MUI theme, providers, color mode and i18n
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
ReactNode,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
|
||||
type Language = 'de' | 'en';
|
||||
|
||||
const translations = {
|
||||
de: {
|
||||
// App + Navigation
|
||||
appName: 'PATCHPILOT',
|
||||
tools: 'Werkzeuge',
|
||||
chat: 'KI-Assistent',
|
||||
admin: 'Admin',
|
||||
logout: 'Abmelden',
|
||||
administrator: 'Administrator',
|
||||
guestRole: 'Gast',
|
||||
staffRole: 'Mitarbeiter',
|
||||
user: 'Nutzer',
|
||||
role: 'Rolle',
|
||||
|
||||
// Hero
|
||||
heroBadge: 'KI-gestützte Reparaturlösungen',
|
||||
heroTitlePart1: 'Repariere klüger,',
|
||||
heroTitlePart2: 'nicht härter.',
|
||||
heroSubtitle:
|
||||
'Dein intelligenter Begleiter für jede Reparatur. Erhalte Schritt-für-Schritt Anleitungen, Werkzeug-Empfehlungen und sofortige Hilfe.',
|
||||
heroCTA: 'Jetzt Hilfe anfordern',
|
||||
aiPlaceholder: 'Frage RepairBuddy nach Hilfe…',
|
||||
miniStatInventory: 'Inventar',
|
||||
miniStatAssistant: 'KI-Assistent',
|
||||
miniStatResponse: 'Antwort',
|
||||
|
||||
// Login
|
||||
loginWelcomeBack: 'Willkommen zurück',
|
||||
loginSubtitle:
|
||||
'Melde dich an, um Werkzeuge zu reservieren und mit dem KI-Assistenten zu chatten.',
|
||||
loginDemoTitle: 'Demo-Zugänge',
|
||||
loginDemoBody:
|
||||
'admin / admin · vollständiger Zugriff\nguest / guest · Gast-Modus\nstaff / staff · Mitarbeiter',
|
||||
loginError: 'Falscher Benutzername oder Passwort. (Tipp: admin/admin oder guest/guest)',
|
||||
loginSubmit: 'Einloggen',
|
||||
loginSubmitting: 'Einloggen…',
|
||||
|
||||
// Tools page
|
||||
inventory: 'Werkzeug-Inventar',
|
||||
inventoryHint: 'Werkzeuge verfügbar',
|
||||
search: 'Werkzeuge durchsuchen…',
|
||||
filter: 'Filter',
|
||||
all: 'Alle',
|
||||
reserve: 'Jetzt reservieren',
|
||||
certified: 'Zertifiziert',
|
||||
trainingMissing: 'Schulung fehlt',
|
||||
trainingRequired: 'Einweisung benötigt',
|
||||
homeUse: 'Heimnutzung',
|
||||
freeAccess: 'Frei zugänglich',
|
||||
days: 'Tage',
|
||||
noToolsFound: 'Keine Werkzeuge gefunden. Passe Suche oder Filter an.',
|
||||
|
||||
// Chat
|
||||
newChat: 'Neuer Chat',
|
||||
chatHistory: 'Verlauf',
|
||||
chatEmptyTitle: 'Womit darf ich helfen?',
|
||||
chatEmptyHint:
|
||||
'Frag mich nach Werkzeugen, Reparatur-Tipps oder beschreibe dein Problem.',
|
||||
chatNoSessionTitle: 'Worüber sollen wir reden?',
|
||||
chatNoSessionHint: 'Wähle einen Vorschlag unten oder schreibe deine Frage.',
|
||||
chatRename: 'Umbenennen',
|
||||
chatDelete: 'Löschen',
|
||||
chatRetry: 'Letzte Anfrage erneut senden',
|
||||
chatCopy: 'Kopieren',
|
||||
chatCopied: 'Kopiert!',
|
||||
chatTypingPlaceholder: 'Tippe deine Frage…',
|
||||
modeLive: 'Live',
|
||||
modeFallback: 'Fallback',
|
||||
modeDemo: 'Demo',
|
||||
|
||||
// Reservation
|
||||
reservation: 'Reservierung',
|
||||
availability: 'Verfügbarkeit',
|
||||
booked: 'Reserviert',
|
||||
free: 'Frei',
|
||||
from: 'Von',
|
||||
to: 'Bis',
|
||||
maxRental: 'Maximale Leihdauer',
|
||||
cancel: 'Abbrechen',
|
||||
request: 'Anfragen',
|
||||
confirmReservation: 'Reservieren',
|
||||
reservedSuccess: 'Reserviert!',
|
||||
durationTotal: 'gesamt',
|
||||
notice:
|
||||
'Bitte bringe das Werkzeug rechtzeitig und unbeschädigt zurück.',
|
||||
|
||||
// Login form fields
|
||||
username: 'Benutzername',
|
||||
password: 'Passwort',
|
||||
login: 'Einloggen',
|
||||
|
||||
// Onboarding
|
||||
welcome: 'Willkommen bei PatchPilot!',
|
||||
onboarding1Desc:
|
||||
'Hier kannst du professionelle Werkzeuge für deine Reparaturen ausleihen. Achte auf die Einweisungspflicht!',
|
||||
onboarding2Title: 'Dein Reparatur-Assistent',
|
||||
onboarding2Desc:
|
||||
'Nutze unseren Chatbot, um Tipps für deine Reparatur zu bekommen oder das passende Werkzeug zu finden.',
|
||||
onboarding3Title: 'Verfügbarkeit prüfen',
|
||||
onboarding3Desc:
|
||||
'In unserem interaktiven Kalender siehst du sofort, wann Werkzeuge verfügbar sind.',
|
||||
onboarding4Title: 'Admin-Funktionen',
|
||||
onboarding4Desc:
|
||||
'Als Administrator kannst du das gesamte System verwalten — von Werkzeugen bis hin zu KI-Wissensquellen.',
|
||||
getStarted: 'Loslegen',
|
||||
next: 'Weiter',
|
||||
back: 'Zurück',
|
||||
skip: 'Überspringen',
|
||||
onboardingStep: 'Schritt',
|
||||
of: 'von',
|
||||
|
||||
// Admin
|
||||
adminDashboard: 'Admin Dashboard',
|
||||
adminSubtitle: 'Verwalte Werkzeuge, Reservierungen und Wissen',
|
||||
manageTools: 'Werkzeuge',
|
||||
manageRAG: 'KI-Wissen',
|
||||
manageUsers: 'Nutzer',
|
||||
calendar: 'Kalender',
|
||||
addTool: 'Werkzeug hinzufügen',
|
||||
editTool: 'Werkzeug bearbeiten',
|
||||
deleteTool: 'Werkzeug löschen',
|
||||
confirmDelete: 'Wirklich löschen?',
|
||||
delete: 'Löschen',
|
||||
edit: 'Bearbeiten',
|
||||
save: 'Speichern',
|
||||
name: 'Name',
|
||||
description: 'Beschreibung',
|
||||
location: 'Standort',
|
||||
category: 'Kategorie',
|
||||
emoji: 'Symbol',
|
||||
status: 'Status',
|
||||
actions: 'Aktionen',
|
||||
available: 'Verfügbar',
|
||||
statsTools: 'Werkzeuge',
|
||||
statsUsers: 'Nutzer',
|
||||
statsActive: 'Aktive Reservierungen',
|
||||
statsPending: 'Offen',
|
||||
addManual: 'Dokument hochladen',
|
||||
manualPreview: 'Vorschau',
|
||||
forTool: 'Werkzeug',
|
||||
title: 'Titel',
|
||||
content: 'Inhalt',
|
||||
certifications: 'Zertifizierungen',
|
||||
none: 'keine',
|
||||
pendingReservations: 'Offene Reservierungs-Anfragen',
|
||||
approve: 'Genehmigen',
|
||||
reject: 'Ablehnen',
|
||||
requestedBy: 'Angefragt von',
|
||||
period: 'Zeitraum',
|
||||
statusRequested: 'Angefragt',
|
||||
statusApproved: 'Genehmigt',
|
||||
statusActive: 'Aktiv',
|
||||
statusCompleted: 'Erledigt',
|
||||
statusOverdue: 'Überfällig',
|
||||
reservationCalendar: 'Reservierungskalender',
|
||||
},
|
||||
en: {
|
||||
// App + Navigation
|
||||
appName: 'PATCHPILOT',
|
||||
tools: 'Tools',
|
||||
chat: 'AI Assistant',
|
||||
admin: 'Admin',
|
||||
logout: 'Sign out',
|
||||
administrator: 'Administrator',
|
||||
guestRole: 'Guest',
|
||||
staffRole: 'Staff',
|
||||
user: 'User',
|
||||
role: 'Role',
|
||||
|
||||
// Hero
|
||||
heroBadge: 'AI-powered repair solutions',
|
||||
heroTitlePart1: 'Repair smarter,',
|
||||
heroTitlePart2: 'not harder.',
|
||||
heroSubtitle:
|
||||
'Your intelligent companion for every repair. Get step-by-step instructions, tool recommendations, and instant help.',
|
||||
heroCTA: 'Get help now',
|
||||
aiPlaceholder: 'Ask RepairBuddy for help…',
|
||||
miniStatInventory: 'Inventory',
|
||||
miniStatAssistant: 'AI assistant',
|
||||
miniStatResponse: 'Response',
|
||||
|
||||
// Login
|
||||
loginWelcomeBack: 'Welcome back',
|
||||
loginSubtitle:
|
||||
'Sign in to reserve tools and chat with the AI assistant.',
|
||||
loginDemoTitle: 'Demo accounts',
|
||||
loginDemoBody:
|
||||
'admin / admin · full access\nguest / guest · guest mode\nstaff / staff · staff member',
|
||||
loginError: 'Wrong username or password. (Hint: admin/admin or guest/guest)',
|
||||
loginSubmit: 'Sign in',
|
||||
loginSubmitting: 'Signing in…',
|
||||
|
||||
// Tools page
|
||||
inventory: 'Tool Inventory',
|
||||
inventoryHint: 'tools available',
|
||||
search: 'Search tools…',
|
||||
filter: 'Filter',
|
||||
all: 'All',
|
||||
reserve: 'Reserve now',
|
||||
certified: 'Certified',
|
||||
trainingMissing: 'Training missing',
|
||||
trainingRequired: 'Training required',
|
||||
homeUse: 'Home use',
|
||||
freeAccess: 'Free access',
|
||||
days: 'days',
|
||||
noToolsFound: 'No tools found. Adjust your search or filter.',
|
||||
|
||||
// Chat
|
||||
newChat: 'New chat',
|
||||
chatHistory: 'History',
|
||||
chatEmptyTitle: 'How can I help?',
|
||||
chatEmptyHint:
|
||||
'Ask me about tools, repair tips, or describe your problem.',
|
||||
chatNoSessionTitle: 'What shall we talk about?',
|
||||
chatNoSessionHint: 'Pick a suggestion below or type your question.',
|
||||
chatRename: 'Rename',
|
||||
chatDelete: 'Delete',
|
||||
chatRetry: 'Retry last request',
|
||||
chatCopy: 'Copy',
|
||||
chatCopied: 'Copied!',
|
||||
chatTypingPlaceholder: 'Type your question…',
|
||||
modeLive: 'Live',
|
||||
modeFallback: 'Fallback',
|
||||
modeDemo: 'Demo',
|
||||
|
||||
// Reservation
|
||||
reservation: 'Reservation',
|
||||
availability: 'Availability',
|
||||
booked: 'Booked',
|
||||
free: 'Free',
|
||||
from: 'From',
|
||||
to: 'To',
|
||||
maxRental: 'Max rental period',
|
||||
cancel: 'Cancel',
|
||||
request: 'Request',
|
||||
confirmReservation: 'Reserve',
|
||||
reservedSuccess: 'Reserved!',
|
||||
durationTotal: 'total',
|
||||
notice: 'Please return the tool on time and undamaged.',
|
||||
|
||||
// Login form fields
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
login: 'Sign in',
|
||||
|
||||
// Onboarding
|
||||
welcome: 'Welcome to PatchPilot!',
|
||||
onboarding1Desc:
|
||||
'Here you can rent professional tools for your repairs. Mind the training requirements!',
|
||||
onboarding2Title: 'Your repair assistant',
|
||||
onboarding2Desc:
|
||||
'Use our chatbot to get tips for your repair or find the right tool.',
|
||||
onboarding3Title: 'Check availability',
|
||||
onboarding3Desc:
|
||||
'See exactly when tools are available in our interactive calendar.',
|
||||
onboarding4Title: 'Admin features',
|
||||
onboarding4Desc:
|
||||
'As an administrator, you can manage the entire system — from tools to AI knowledge sources.',
|
||||
getStarted: 'Get started',
|
||||
next: 'Next',
|
||||
back: 'Back',
|
||||
skip: 'Skip',
|
||||
onboardingStep: 'Step',
|
||||
of: 'of',
|
||||
|
||||
// Admin
|
||||
adminDashboard: 'Admin Dashboard',
|
||||
adminSubtitle: 'Manage tools, reservations, and knowledge',
|
||||
manageTools: 'Tools',
|
||||
manageRAG: 'AI Knowledge',
|
||||
manageUsers: 'Users',
|
||||
calendar: 'Calendar',
|
||||
addTool: 'Add tool',
|
||||
editTool: 'Edit tool',
|
||||
deleteTool: 'Delete tool',
|
||||
confirmDelete: 'Really delete?',
|
||||
delete: 'Delete',
|
||||
edit: 'Edit',
|
||||
save: 'Save',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
location: 'Location',
|
||||
category: 'Category',
|
||||
emoji: 'Symbol',
|
||||
status: 'Status',
|
||||
actions: 'Actions',
|
||||
available: 'Available',
|
||||
statsTools: 'Tools',
|
||||
statsUsers: 'Users',
|
||||
statsActive: 'Active reservations',
|
||||
statsPending: 'Pending',
|
||||
addManual: 'Upload manual',
|
||||
manualPreview: 'Preview',
|
||||
forTool: 'Tool',
|
||||
title: 'Title',
|
||||
content: 'Content',
|
||||
certifications: 'Certifications',
|
||||
none: 'none',
|
||||
pendingReservations: 'Pending reservation requests',
|
||||
approve: 'Approve',
|
||||
reject: 'Reject',
|
||||
requestedBy: 'Requested by',
|
||||
period: 'Period',
|
||||
statusRequested: 'Requested',
|
||||
statusApproved: 'Approved',
|
||||
statusActive: 'Active',
|
||||
statusCompleted: 'Completed',
|
||||
statusOverdue: 'Overdue',
|
||||
reservationCalendar: 'Reservation calendar',
|
||||
},
|
||||
} as const;
|
||||
|
||||
type TranslationKey = keyof (typeof translations)['de'];
|
||||
|
||||
interface I18nContextType {
|
||||
t: (key: TranslationKey) => string;
|
||||
lang: Language;
|
||||
setLang: (lang: Language) => void;
|
||||
}
|
||||
|
||||
const I18nContext = createContext<I18nContextType | undefined>(undefined);
|
||||
|
||||
const STORAGE_KEY = 'patchpilot:lang';
|
||||
|
||||
function detectInitialLang(): Language {
|
||||
if (typeof window === 'undefined') return 'de';
|
||||
const stored = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (stored === 'de' || stored === 'en') return stored;
|
||||
const browser = window.navigator.language.toLowerCase();
|
||||
return browser.startsWith('en') ? 'en' : 'de';
|
||||
}
|
||||
|
||||
export function I18nProvider({ children }: { children: ReactNode }) {
|
||||
const [lang, setLangState] = useState<Language>('de');
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setLangState(detectInitialLang());
|
||||
setHydrated(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydrated) return;
|
||||
window.localStorage.setItem(STORAGE_KEY, lang);
|
||||
document.documentElement.lang = lang;
|
||||
}, [lang, hydrated]);
|
||||
|
||||
const value = useMemo<I18nContextType>(
|
||||
() => ({
|
||||
lang,
|
||||
setLang: (next: Language) => setLangState(next),
|
||||
t: (key: TranslationKey) => {
|
||||
const dict = translations[lang];
|
||||
const value = dict[key];
|
||||
if (typeof value === 'string') return value;
|
||||
const fallback = translations.de[key];
|
||||
return typeof fallback === 'string' ? fallback : String(key);
|
||||
},
|
||||
}),
|
||||
[lang]
|
||||
);
|
||||
|
||||
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
|
||||
}
|
||||
|
||||
export function useI18n() {
|
||||
const context = useContext(I18nContext);
|
||||
if (!context) throw new Error('useI18n must be used within I18nProvider');
|
||||
return context;
|
||||
}
|
||||
|
||||
export type { TranslationKey };
|
||||
Reference in New Issue
Block a user