Files
kooltab-backoffice/apps/dashboard/src/lib/i18n.tsx
T
2026-10-08 00:25:30 +02:00

367 lines
16 KiB
TypeScript

import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
type Lang = 'en' | 'nl';
const translations: Record<Lang, Record<string, string>> = {
en: {
'nav.overview': 'Overview',
'nav.tabs': 'Open Tabs',
'nav.reminders': 'Reminders',
'nav.users': 'Users',
'nav.commands': 'Command History',
'nav.settings': 'Settings',
'nav.logout': 'Logout',
'tabs.title': 'Open Tabs',
'tabs.count': '{count} open tab',
'tabs.count_plural': '{count} open tabs',
'tabs.empty': 'No open tabs',
'tabs.empty_desc': 'Open tabs will appear here when the terminal syncs.',
'tabs.customer': 'Customer',
'tabs.amount': 'Amount',
'tabs.items': 'Items',
'tabs.version': 'Version',
'tabs.reported': 'Last Reported',
'tabs.close': 'Close',
'tabs.close_title': "Close {name}'s tab?",
'tabs.close_desc': 'Send a close request to the terminal.',
'tabs.payment': 'Payment method',
'tabs.cash': 'Cash',
'tabs.payconiq': 'Payconiq',
'tabs.pending_payment': 'Payment pending',
'tabs.select_payment': 'Please select a payment method',
'tabs.send': 'Send close request',
'tabs.sending': 'Sending...',
'tabs.cancel': 'Cancel',
'tabs.planned_title': 'Planned Customers',
'tabs.planned_desc': 'These names are synced to the terminal. It will create tabs for these customers automatically.',
'tabs.planned_add': 'Add customer name...',
'tabs.planned_empty': 'No planned customers. Add names above and they\'ll sync to the terminal.',
'import.button': 'Import from Excel',
'import.title': 'Import customers',
'import.description': 'Upload the customer list. Every name becomes a planned customer and gets a tab on the terminal.',
'import.choose_file': 'Choose a file',
'import.empty_file': 'The file contains no rows.',
'import.unreadable': 'This file could not be read. Use an .xlsx, .xls or .csv file.',
'import.name_column': 'Name (or first name)',
'import.last_name_column': 'Last name',
'import.email_column': 'Email',
'import.column_none': '— none —',
'import.has_header': 'First row contains column headers',
'import.summary': '{count} customers found, {emails} with an email address.',
'import.warnings': '{skipped} rows without a name skipped, {invalid} invalid email addresses ignored.',
'import.email': 'Email',
'import.more': 'and {count} more…',
'import.mode': 'Existing customers',
'import.mode_merge': 'Keep customers that are not in the file',
'import.mode_replace': 'Remove customers that are not in the file (their open tabs on the terminal are kept)',
'import.submit': 'Import {count} customers',
'import.importing': 'Importing...',
'import.done': 'Import complete',
'import.result': '{created} added, {updated} updated, {unchanged} unchanged, {removed} removed.',
'import.failed': 'Import failed',
'users.title': 'Users',
'users.count': '{count} user',
'users.count_plural': '{count} users',
'users.empty': 'No users',
'users.empty_desc': 'Add users who can manage tabs from this dashboard.',
'users.add': 'Add User',
'users.name': 'Name',
'users.email': 'Email',
'users.role': 'Role',
'users.created': 'Created',
'users.add_title': 'Add User',
'users.add_desc': 'Create a new user who can access the dashboard.',
'users.password': 'Password',
'users.creating': 'Creating...',
'users.create': 'Create User',
'commands.title': 'Command History',
'commands.count': '{count} command',
'commands.count_plural': '{count} commands',
'commands.empty': 'No commands found',
'commands.tab': 'Tab',
'commands.requested': 'Requested By',
'commands.payment': 'Payment',
'commands.created': 'Created',
'commands.status': 'Status',
'commands.processed': 'Processed',
'commands.result': 'Result',
'commands.all': 'All',
'commands.previous': 'Previous',
'commands.next': 'Next',
'commands.page': 'Page {page} of {total}',
'settings.title': 'Settings',
'settings.desc': 'Venue and device management',
'settings.venue': 'Venue',
'settings.device': 'Device',
'settings.paired': 'Paired as "{name}" — last seen {time}',
'settings.not_paired': 'No device paired yet',
'settings.connected': 'Connected',
'settings.since': 'Since {date}',
'settings.pair_desc': 'Pair a bar terminal to start receiving tabs.',
'settings.pair': 'Pair Device',
'settings.repair': 'Re-pair Device',
'settings.pair_title': 'Pair a Device',
'settings.pair_instructions': 'Enter this code on the bar terminal to connect it to your venue.',
'settings.pair_code': 'Registration Code',
'settings.pair_expires': 'Expires at {time}',
'settings.pair_step1': 'Open the bar terminal app',
'settings.pair_step2': 'Select your venue from the list',
'settings.pair_step3': 'Enter this registration code',
'settings.pair_step4': 'Give the terminal a name',
'settings.pair_step5': 'Complete registration',
'settings.copy': 'Copy Code',
'settings.close': 'Close',
'settings.api': 'API',
'settings.base_url': 'Base URL',
'settings.swagger': 'Swagger',
'overview.title': 'Overview',
'overview.desc': 'Real-time overview of your venue',
'overview.open_tabs': 'Open Tabs',
'overview.device': 'Device',
'overview.pending': 'Pending Commands',
'overview.recent': 'Recent Commands',
'overview.no_commands': 'No commands yet',
'overview.not_connected': 'Not connected',
'overview.last_seen': 'Last seen {time}',
'common.refresh': 'Refresh',
'common.loading': 'Loading...',
'common.error': 'Something went wrong',
'common.confirm': 'Confirm',
'common.cancel': 'Cancel',
'common.processing': 'Processing...',
'common.copied': 'Copied',
'auth.login': 'Sign in',
'auth.logging_in': 'Signing in...',
'auth.email': 'Email',
'auth.password': 'Password',
'auth.invalid': 'Invalid email or password',
'lang.en': 'English',
'lang.nl': 'Nederlands',
'lang.label': 'Language',
'reminders.title': 'Payment Reminders',
'reminders.desc': 'Send payment reminders to customers with open tabs',
'reminders.candidates': 'Eligible for reminder',
'reminders.candidates_desc': 'These customers have open tabs and an email address on file. Reminders will be sent via Resend.',
'reminders.no_candidates': 'No eligible customers',
'reminders.no_candidates_desc': 'Make sure planned customers have email addresses set.',
'reminders.send': 'Send Reminders',
'reminders.sending': 'Sending...',
'reminders.result': 'Sent {sent} of {total} reminder(s)',
'reminders.sent_title': 'Reminders Sent',
'reminders.zero_sent': 'No reminders were sent. Check that Resend is configured.',
'reminders.success': 'Successfully sent {sent} reminder email(s)',
'reminders.history': 'Sent Reminders',
'reminders.history_empty': 'No reminders have been sent yet',
'reminders.customer': 'Customer',
'reminders.email': 'Email',
'reminders.amount': 'Amount',
'reminders.sent_at': 'Sent At',
'reminders.status': 'Status',
'tabs.email': 'Email (optional)',
'tabs.planned_email_hint': 'Add email to enable payment reminders',
},
nl: {
'nav.overview': 'Overzicht',
'nav.tabs': 'Open Tabs',
'nav.reminders': 'Herinneringen',
'nav.users': 'Gebruikers',
'nav.commands': 'Opdrachten',
'nav.settings': 'Instellingen',
'nav.logout': 'Uitloggen',
'tabs.title': 'Open Tabs',
'tabs.count': '{count} open tab',
'tabs.count_plural': '{count} open tabs',
'tabs.empty': 'Geen open tabs',
'tabs.empty_desc': 'Open tabs verschijnen hier wanneer de terminal synchroniseert.',
'tabs.customer': 'Klant',
'tabs.amount': 'Bedrag',
'tabs.items': 'Artikelen',
'tabs.version': 'Versie',
'tabs.reported': 'Laatst gemeld',
'tabs.close': 'Sluiten',
'tabs.close_title': 'Sluit {name}\'s tab?',
'tabs.close_desc': 'Stuur een sluitverzoek naar de terminal.',
'tabs.payment': 'Betaalmethode',
'tabs.cash': 'Contant',
'tabs.payconiq': 'Payconiq',
'tabs.pending_payment': 'Betaling in behandeling',
'tabs.select_payment': 'Selecteer een betaalmethode',
'tabs.send': 'Sluitverzoek versturen',
'tabs.sending': 'Bezig met verzenden...',
'tabs.cancel': 'Annuleren',
'tabs.planned_title': 'Geplande Klanten',
'tabs.planned_desc': 'Deze namen worden gesynchroniseerd met de terminal. Deze maakt automatisch tabs aan voor deze klanten.',
'tabs.planned_add': 'Klantnaam toevoegen...',
'tabs.planned_empty': 'Geen geplande klanten. Voeg namen toe en ze worden gesynchroniseerd met de terminal.',
'import.button': 'Importeren uit Excel',
'import.title': 'Klanten importeren',
'import.description': 'Upload de klantenlijst. Elke naam wordt een geplande klant en krijgt een rekening op de terminal.',
'import.choose_file': 'Kies een bestand',
'import.empty_file': 'Het bestand bevat geen rijen.',
'import.unreadable': 'Dit bestand kon niet gelezen worden. Gebruik een .xlsx-, .xls- of .csv-bestand.',
'import.name_column': 'Naam (of voornaam)',
'import.last_name_column': 'Achternaam',
'import.email_column': 'E-mail',
'import.column_none': '— geen —',
'import.has_header': 'De eerste rij bevat kolomnamen',
'import.summary': '{count} klanten gevonden, {emails} met een e-mailadres.',
'import.warnings': '{skipped} rijen zonder naam overgeslagen, {invalid} ongeldige e-mailadressen genegeerd.',
'import.email': 'E-mail',
'import.more': 'en nog {count}…',
'import.mode': 'Bestaande klanten',
'import.mode_merge': 'Klanten die niet in het bestand staan behouden',
'import.mode_replace': 'Klanten die niet in het bestand staan verwijderen (hun open rekeningen op de terminal blijven)',
'import.submit': '{count} klanten importeren',
'import.importing': 'Bezig met importeren...',
'import.done': 'Import voltooid',
'import.result': '{created} toegevoegd, {updated} bijgewerkt, {unchanged} ongewijzigd, {removed} verwijderd.',
'import.failed': 'Importeren mislukt',
'users.title': 'Gebruikers',
'users.count': '{count} gebruiker',
'users.count_plural': '{count} gebruikers',
'users.empty': 'Geen gebruikers',
'users.empty_desc': 'Voeg gebruikers toe die tabs kunnen beheren vanaf dit dashboard.',
'users.add': 'Gebruiker toevoegen',
'users.name': 'Naam',
'users.email': 'E-mail',
'users.role': 'Rol',
'users.created': 'Aangemaakt',
'users.add_title': 'Gebruiker toevoegen',
'users.add_desc': 'Maak een nieuwe gebruiker aan die toegang heeft tot het dashboard.',
'users.password': 'Wachtwoord',
'users.creating': 'Bezig...',
'users.create': 'Gebruiker aanmaken',
'commands.title': 'Opdrachtgeschiedenis',
'commands.count': '{count} opdracht',
'commands.count_plural': '{count} opdrachten',
'commands.empty': 'Geen opdrachten gevonden',
'commands.tab': 'Tab',
'commands.requested': 'Aangevraagd door',
'commands.payment': 'Betaling',
'commands.created': 'Aangemaakt',
'commands.status': 'Status',
'commands.processed': 'Verwerkt',
'commands.result': 'Resultaat',
'commands.all': 'Alle',
'commands.previous': 'Vorige',
'commands.next': 'Volgende',
'commands.page': 'Pagina {page} van {total}',
'settings.title': 'Instellingen',
'settings.desc': 'Locatie- en apparaatbeheer',
'settings.venue': 'Locatie',
'settings.device': 'Apparaat',
'settings.paired': 'Gekoppeld als "{name}" — laatst gezien {time}',
'settings.not_paired': 'Nog geen apparaat gekoppeld',
'settings.connected': 'Verbonden',
'settings.since': 'Sinds {date}',
'settings.pair_desc': 'Koppel een barterminal om tabs te ontvangen.',
'settings.pair': 'Apparaat koppelen',
'settings.repair': 'Opnieuw koppelen',
'settings.pair_title': 'Apparaat koppelen',
'settings.pair_instructions': 'Voer deze code in op de barterminal om deze te verbinden met uw locatie.',
'settings.pair_code': 'Registratiecode',
'settings.pair_expires': 'Verloopt om {time}',
'settings.pair_step1': 'Open de barterminal app',
'settings.pair_step2': 'Selecteer uw locatie uit de lijst',
'settings.pair_step3': 'Voer deze registratiecode in',
'settings.pair_step4': 'Geef de terminal een naam',
'settings.pair_step5': 'Voltooi de registratie',
'settings.copy': 'Code kopiëren',
'settings.close': 'Sluiten',
'settings.api': 'API',
'settings.base_url': 'Basis-URL',
'settings.swagger': 'Swagger',
'overview.title': 'Overzicht',
'overview.desc': 'Real-time overzicht van uw locatie',
'overview.open_tabs': 'Open Tabs',
'overview.device': 'Apparaat',
'overview.pending': 'Openstaande opdrachten',
'overview.recent': 'Recente opdrachten',
'overview.no_commands': 'Nog geen opdrachten',
'overview.not_connected': 'Niet verbonden',
'overview.last_seen': 'Laatst gezien {time}',
'common.refresh': 'Verversen',
'common.loading': 'Laden...',
'common.error': 'Er is iets misgegaan',
'common.confirm': 'Bevestigen',
'common.cancel': 'Annuleren',
'common.processing': 'Bezig...',
'common.copied': 'Gekopieerd',
'auth.login': 'Inloggen',
'auth.logging_in': 'Bezig met inloggen...',
'auth.email': 'E-mail',
'auth.password': 'Wachtwoord',
'auth.invalid': 'Ongeldig e-mailadres of wachtwoord',
'lang.en': 'English',
'lang.nl': 'Nederlands',
'lang.label': 'Taal',
'reminders.title': 'Betalingsherinneringen',
'reminders.desc': 'Stuur betalingsherinneringen naar klanten met openstaande tabs',
'reminders.candidates': 'In aanmerking voor herinnering',
'reminders.candidates_desc': 'Deze klanten hebben openstaande tabs en een e-mailadres. Herinneringen worden verzonden via Resend.',
'reminders.no_candidates': 'Geen geschikte klanten',
'reminders.no_candidates_desc': 'Zorg dat geplande klanten een e-mailadres hebben.',
'reminders.send': 'Verstuur herinneringen',
'reminders.sending': 'Bezig met versturen...',
'reminders.result': '{sent} van {total} herinnering(en) verzonden',
'reminders.sent_title': 'Herinneringen verzonden',
'reminders.zero_sent': 'Geen herinneringen verzonden. Controleer of Resend is geconfigureerd.',
'reminders.success': '{sent} herinnering(en) succesvol verzonden',
'reminders.history': 'Verzonden herinneringen',
'reminders.history_empty': 'Nog geen herinneringen verzonden',
'reminders.customer': 'Klant',
'reminders.email': 'E-mail',
'reminders.amount': 'Bedrag',
'reminders.sent_at': 'Verzonden op',
'reminders.status': 'Status',
'tabs.email': 'E-mail (optioneel)',
'tabs.planned_email_hint': 'Voeg e-mail toe om betalingsherinneringen in te schakelen',
},
};
interface LangContextType {
lang: Lang;
setLang: (lang: Lang) => void;
t: (key: string, params?: Record<string, string | number>) => string;
}
const LangContext = createContext<LangContextType>({
lang: 'en',
setLang: () => {},
t: (key) => key,
});
export function LanguageProvider({ children }: { children: ReactNode }) {
const [lang, setLangState] = useState<Lang>('en');
useEffect(() => {
const stored = localStorage.getItem('kooltap-lang') as Lang | null;
if (stored === 'en' || stored === 'nl') setLangState(stored);
}, []);
function setLang(l: Lang) {
setLangState(l);
localStorage.setItem('kooltap-lang', l);
}
function t(key: string, params?: Record<string, string | number>): string {
let s = translations[lang][key];
if (!s) s = translations['en'][key] || key;
if (params) {
for (const [k, v] of Object.entries(params)) {
s = s.replace(`{${k}}`, String(v));
}
}
return s;
}
return (
<LangContext.Provider value={{ lang, setLang, t }}>
{children}
</LangContext.Provider>
);
}
export function useLang() {
return useContext(LangContext);
}