386 lines
12 KiB
TypeScript
386 lines
12 KiB
TypeScript
'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 };
|