Files
Nexumi/packages/shared/src/index.ts
2026-07-24 10:46:12 +02:00

1247 lines
72 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { z } from 'zod';
export * from './audit.js';
export * from './command-locales.js';
export * from './discord-localization.js';
export * from './phase2.js';
export * from './phase3.js';
export * from './phase4.js';
export * from './phase5.js';
export * from './phase6.js';
export * from './dashboard.js';
export * from './owner.js';
export * from './public.js';
export * from './premium.js';
export * from './components-v2.js';
export const LocaleSchema = z.enum(['de', 'en']);
export type Locale = z.infer<typeof LocaleSchema>;
export const GuildSettingsSchema = z.object({
guildId: z.string(),
locale: LocaleSchema.default('de'),
timezone: z.string().default('Europe/Berlin'),
moderationEnabled: z.boolean().default(true)
});
export const ModerationActionSchema = z.enum([
'BAN',
'UNBAN',
'KICK',
'TIMEOUT',
'UN_TIMEOUT',
'WARN_ADD',
'WARN_REMOVE',
'WARN_CLEAR',
'PURGE',
'LOCK',
'UNLOCK',
'SLOWMODE',
'NICK_SET',
'NICK_RESET',
'CASE_EDIT',
'CASE_DELETE',
'MODNOTE_ADD'
]);
export type ModerationAction = z.infer<typeof ModerationActionSchema>;
type Dictionary = Record<string, string>;
const de: Dictionary = {
'generic.noPermission': 'Du hast keine Berechtigung für diese Aktion.',
'generic.guildOnly': 'Dieser Command kann nur auf einem Server verwendet werden.',
'generic.botMissingPermission': 'Dem Bot fehlt die erforderliche Berechtigung ({permission}).',
'generic.invalidRegex': 'Ungültiges Regex-Muster.',
'generic.error': 'Es ist ein Fehler aufgetreten.',
'generic.maintenance': 'Nexumi ist derzeit im Wartungsmodus. Bitte versuche es später erneut.',
'generic.unknownCommand': 'Unbekannter Befehl.',
'generic.commandDisabled': 'Dieser Befehl ist auf diesem Server deaktiviert.',
'generic.commandChannelBlocked': 'Dieser Befehl ist in diesem Kanal nicht erlaubt.',
'generic.commandRoleBlocked': 'Du darfst diesen Befehl mit deinen Rollen nicht nutzen.',
'generic.commandCooldown': 'Bitte warte noch {seconds} Sekunden, bevor du diesen Befehl erneut nutzt.',
'generic.moduleDisabled': 'Dieses Modul ist auf diesem Server deaktiviert.',
'generic.userBlacklisted': 'Du darfst Nexumi derzeit nicht nutzen.',
'generic.guildBlacklisted': 'Dieser Server darf Nexumi derzeit nicht nutzen.',
'core.help.title': 'Nexumi Hilfe',
'core.help.subtitle': 'Commands nach Modul. Optional: `/help module:<name>`.',
'core.help.moduleLine': '**{module}**: {commands}',
'core.help.unknownModule': 'Unbekanntes Modul. Verfügbar: {modules}',
'core.help.footer': 'Mehr Infos: /info · Dashboard: {webui}',
'core.info.title': 'Nexumi',
'core.info.version': 'Version',
'core.info.uptime': 'Uptime',
'core.info.shard': 'Shard',
'core.info.guilds': 'Server (dieser Shard)',
'core.info.latency': 'API-Latenz',
'core.info.links': 'Links',
'core.info.link.dashboard': 'Dashboard',
'core.info.link.invite': 'Einladen',
'core.info.link.support': 'Support',
'core.info.link.status': 'Status',
'core.invite.title': 'Nexumi einladen',
'core.invite.body': 'Lade Nexumi mit diesem Link auf deinen Server ein:',
'core.support.title': 'Support',
'core.support.body': 'Tritt unserem Support-Server bei:',
'core.support.missing': 'Kein Support-Server konfiguriert. Bitte SUPPORT_SERVER_URL setzen.',
'core.module.core': 'Basis',
'core.module.moderation': 'Moderation',
'core.module.automod': 'Auto-Moderation',
'core.module.welcome': 'Willkommen',
'core.module.verification': 'Verifizierung',
'core.module.leveling': 'Leveling',
'core.module.economy': 'Economy',
'core.module.utility': 'Utility',
'core.module.fun': 'Fun',
'core.module.giveaways': 'Giveaways',
'core.module.tickets': 'Tickets',
'core.module.selfroles': 'Self-Roles',
'core.module.tags': 'Tags',
'core.module.starboard': 'Starboard',
'core.module.suggestions': 'Vorschläge',
'core.module.birthdays': 'Geburtstage',
'core.module.tempvoice': 'Temp-Voice',
'core.module.stats': 'Statistiken',
'core.module.feeds': 'Feeds',
'core.module.scheduler': 'Scheduler',
'core.module.guildbackup': 'Backup',
'gdpr.privacy.title': 'Datenschutz',
'gdpr.privacy.body':
'Nexumi speichert Discord-IDs und modulbezogene Daten (z. B. Verwarnungen, XP, Economy), soweit für den Betrieb nötig.',
'gdpr.privacy.link': 'Vollständige Datenschutzerklärung: {url}',
'gdpr.privacy.deleteHint': 'Persönliche Daten löschen: `/gdpr delete confirm:true`',
'gdpr.delete.needConfirm': 'Setze `confirm` auf `true`, um die Löschung zu bestätigen.',
'gdpr.delete.doneTitle': 'Daten gelöscht',
'gdpr.delete.doneBody':
'Gelöscht/anonymisiert: {warnings} Verwarnungen, {levels} Level-Einträge, {economy} Economy-Einträge, {cases} Cases anonymisiert.',
'moderation.success': 'Aktion erfolgreich ausgeführt.',
'moderation.success.case': 'Aktion erfolgreich ausgeführt (Fall #{caseNumber}).',
'moderation.duration.invalid': 'Ungültige Dauer. Nutze z. B. 30m, 12h oder 7d.',
'moderation.duration.invalidTimeout':
'Ungültige Timeout-Dauer. Nutze z. B. 30m oder 1d (max. 28 Tage).',
'moderation.hierarchy.self': 'Du kannst diese Aktion nicht auf dich selbst anwenden.',
'moderation.hierarchy.owner': 'Der Server-Owner kann nicht moderiert werden.',
'moderation.hierarchy.moderator':
'Du kannst keine Mitglieder moderieren, die gleich oder höher in der Rollenhierarchie stehen.',
'moderation.hierarchy.bot':
'Der Bot kann dieses Mitglied wegen der Rollenhierarchie oder fehlender Rechte nicht moderieren.',
'moderation.hierarchy.notMember': 'Dieses Mitglied ist nicht auf dem Server.',
'moderation.reason.none': 'Kein Grund angegeben',
'moderation.warning.created': 'Verwarnung erstellt: {warningId}',
'moderation.warning.none': 'Keine Verwarnungen vorhanden.',
'moderation.warning.defaultReason': 'Kein Grund',
'moderation.warning.notFound': 'Verwarnung nicht gefunden.',
'moderation.purge.done': '{deletedCount} Nachrichten gelöscht (Fall #{caseNumber}).',
'moderation.note.none': 'Keine Notizen vorhanden.',
'moderation.case.notFound': 'Fall nicht gefunden.',
'moderation.escalation.durationRequired':
'Für TIMEOUT-Eskalation ist eine Dauer erforderlich.',
'moderation.escalation.noneConfigured': 'Keine Eskalationsregeln konfiguriert.',
'moderation.escalation.saved':
'Eskalationsregel gespeichert: {warnCount} Verwarnungen => {action}{duration}.',
'moderation.escalation.removed':
'Eskalationsregel für {warnCount} Verwarnungen entfernt.',
'moderation.escalation.defaultReason': 'Auto-Eskalation bei {warnCount} Verwarnungen',
'moderation.escalation.skippedDuration':
'Eskalationsregel für {warnCount} Verwarnungen übersprungen (fehlende Dauer).',
'moderation.escalation.skippedPermission':
'Eskalationsregel für {warnCount} Verwarnungen übersprungen (fehlende Bot-Rechte).',
'moderation.escalation.appliedTimeout': 'Auto-Eskalation angewendet: Timeout ({durationMs} ms).',
'moderation.escalation.appliedKick': 'Auto-Eskalation angewendet: Kick.',
'moderation.escalation.appliedBan': 'Auto-Eskalation angewendet: Ban.',
'moderation.confirm.button.confirm': 'Bestätigen',
'moderation.confirm.button.cancel': 'Abbrechen',
'moderation.confirm.permanent': 'dauerhaft',
'moderation.confirm.ban':
'Ban bestätigen für <@{userId}>?\nGrund: {reason}\nDauer: {duration}',
'moderation.confirm.purge': '{amount} Nachrichten löschen? Diese Aktion kann nicht rückgängig gemacht werden.',
'moderation.confirm.caseDelete': 'Fall #{caseNumber} wirklich löschen?',
'moderation.confirm.expired': 'Bestätigung abgelaufen. Bitte den Befehl erneut ausführen.',
'moderation.confirm.cancelled': 'Aktion abgebrochen.',
'moderation.confirm.unauthorized': 'Nur der ausführende Moderator kann diese Bestätigung nutzen.',
'automod.status.header':
'**AutoMod enabled:** {enabled}\n**Anti-raid:** {antiRaid}\n**Anti-nuke:** {antiNuke}\n**Lockdown:** {lockdown}',
'automod.status.ruleLine': '- {type} → {action}',
'automod.status.noRules': 'Keine aktiven Regeln.',
'automod.status.yes': 'ja',
'automod.status.no': 'nein',
'welcome.notConfigured': 'Willkommensnachricht ist noch nicht konfiguriert.',
'welcome.invalidChannel': 'Der konfigurierte Willkommenskanal ist ungültig.',
'welcome.testSent': 'Test-Willkommensnachricht gesendet.',
'verification.disabled': 'Verifizierung ist deaktiviert.',
'verification.success': 'Du wurdest erfolgreich verifiziert.',
'verification.setupDone': 'Verifizierung konfiguriert.',
'verification.notConfigured': 'Verifizierung ist noch nicht eingerichtet. Nutze `/verify setup`.',
'verification.invalidChannel': 'Der Verifizierungskanal ist ungültig.',
'verification.panelPosted': 'Verifizierungs-Panel gepostet.',
'verification.panelMissingPermission':
'Dem Bot fehlen Rechte in diesem Kanal (Kanal sehen, Nachrichten senden, Embeds).',
'verification.panel.title': 'Verifizierung',
'verification.panel.description':
'Klicke auf den Button unten, um dich zu verifizieren und Zugang zum Server zu erhalten.',
'verification.panel.button': 'Verifizieren',
'verification.panel.buttonCaptcha': 'Verifizieren (Captcha)',
'verification.captchaLink': 'Öffne diesen Link zur Captcha-Verifizierung: {url}',
'verification.error.not_configured': 'Verifizierung ist nicht konfiguriert.',
'verification.error.member_not_found': 'Mitglied nicht gefunden.',
'verification.error.account_too_young': 'Dein Account ist zu jung für die Verifizierung.',
'verification.error.missing_permissions': 'Dem Bot fehlen Berechtigungen.',
'verification.error.invalid_role': 'Die Verifizierungsrolle ist ungültig.',
'verification.error.alt_detected':
'Verifizierung abgelehnt: möglicher Zweitaccount erkannt.',
'verification.error.generic': 'Verifizierung fehlgeschlagen.',
'verification.captcha.success': 'Captcha bestanden. Du wurdest verifiziert.',
'verification.captcha.invalid': 'Captcha ungültig oder abgelaufen.',
'verification.captcha.title': 'Nexumi Verifizierung',
'leveling.disabled': 'Leveling ist auf diesem Server deaktiviert.',
'leveling.rank.title': 'Rangkarte',
'leveling.leaderboard.title': 'Leaderboard',
'leveling.leaderboard.empty': 'Noch keine XP-Daten vorhanden.',
'leveling.leaderboard.entry': '{rank}. {user} — Level {level} ({xp} XP)',
'leveling.leaderboard.footer': 'Top {count} Mitglieder',
'leveling.levelUp.default': 'Glückwunsch {user}, du hast Level {level} erreicht!',
'leveling.xp.give.success': '{amount} XP an {user} vergeben. Neues Level: {level}.',
'leveling.xp.remove.success': '{amount} XP von {user} entfernt. Neues Level: {level}.',
'leveling.xp.reset.success': 'XP von {user} zurückgesetzt.',
'leveling.xp.invalidAmount': 'Der Betrag muss größer als 0 sein.',
'leveling.user.notFound': 'Nutzer nicht auf diesem Server gefunden.',
'economy.error.disabled': 'Das Economy-Modul ist auf diesem Server deaktiviert.',
'economy.error.insufficient': 'Du hast nicht genug Geld.',
'economy.error.cooldown': 'Cooldown aktiv. Versuche es in {seconds} Sekunden erneut.',
'economy.error.invalid_amount': 'Ungültiger Betrag.',
'economy.error.self_transfer': 'Du kannst dir nicht selbst Geld senden.',
'economy.error.not_found': 'Eintrag nicht gefunden.',
'economy.error.out_of_stock': 'Dieser Artikel ist ausverkauft.',
'economy.error.role_assign_failed': 'Kauf erfolgreich, aber die Rolle konnte nicht vergeben werden.',
'economy.error.game_in_progress': 'Du hast bereits ein laufendes Blackjack-Spiel.',
'economy.balance.result': 'Kontostand von {user}: {amount}',
'economy.daily.success': 'Tägliche Belohnung: {amount}. Neuer Kontostand: {balance}',
'economy.weekly.success': 'Wöchentliche Belohnung: {amount}. Neuer Kontostand: {balance}',
'economy.work.success': 'Du hast {amount} verdient. Neuer Kontostand: {balance}',
'economy.pay.success': 'Du hast {amount} an {user} gesendet. Dein Kontostand: {balance}',
'economy.gamble.win': 'Gewonnen! +{amount}. Kontostand: {balance}',
'economy.gamble.lose': 'Verloren! -{amount}. Kontostand: {balance}',
'economy.slots.win': '{reels}\nGewinn: {payout}. Kontostand: {balance}',
'economy.slots.lose': '{reels}\nKein Gewinn. Kontostand: {balance}',
'economy.coinflip.win': '{result} du hattest {choice} gewählt. Gewonnen! +{amount}. Kontostand: {balance}',
'economy.coinflip.lose': '{result} du hattest {choice} gewählt. Verloren! -{amount}. Kontostand: {balance}',
'economy.choice.heads': 'Kopf',
'economy.choice.tails': 'Zahl',
'economy.shop.empty': 'Der Shop ist leer.',
'economy.shop.noDescription': 'Keine Beschreibung',
'economy.shop.itemLine': '**{name}** (`{id}`) {price} | Bestand: {stock}\n{description}',
'economy.shop.purchased': 'Du hast **{name}** gekauft. Kontostand: {balance}',
'economy.inventory.empty': 'Dein Inventar ist leer.',
'economy.inventory.itemLine': '- {name} × {quantity}',
'economy.leaderboard.empty': 'Noch keine Einträge auf der Bestenliste.',
'economy.leaderboard.line': '{rank}. {user} {amount}',
'economy.admin.give': '{amount} an {user} vergeben. Neuer Kontostand: {balance}',
'economy.admin.remove': '{amount} von {user} entfernt. Neuer Kontostand: {balance}',
'economy.admin.reset': 'Economy-Daten von {user} zurückgesetzt.',
'economy.blackjack.title': 'Blackjack',
'economy.blackjack.hand': 'Einsatz: {bet}\n\n**Du:** {playerHand} ({playerTotal})\n**Dealer:** {dealerHand} ({dealerTotal})',
'economy.blackjack.button.hit': 'Hit',
'economy.blackjack.button.stand': 'Stand',
'economy.blackjack.outcome.win': 'Du gewinnst {bet}!',
'economy.blackjack.outcome.lose': 'Du verlierst deinen Einsatz.',
'economy.blackjack.outcome.push': 'Unentschieden Einsatz zurück.',
'economy.blackjack.outcome.blackjack': 'Blackjack! Du gewinnst {bet}!',
'economy.blackjack.expired': 'Dieses Blackjack-Spiel ist abgelaufen oder existiert nicht mehr.',
'economy.blackjack.notOwner': 'Nur der Spieler kann diese Buttons nutzen.',
'utility.error.invalid_when': 'Ungültiger Zeitpunkt. Nutze z. B. 30m oder ein ISO-Datum in der Zukunft.',
'utility.error.invalid_datetime': 'Ungültiges Datum/Uhrzeit.',
'utility.error.invalid_poll_options': 'Es werden 25 Optionen benötigt (kommagetrennt).',
'utility.error.channel_not_found': 'Kanal nicht gefunden.',
'utility.error.role_not_found': 'Rolle nicht gefunden.',
'utility.error.poll_not_found': 'Umfrage nicht gefunden.',
'utility.error.reminder_channel_unavailable': 'Erinnerungskanal nicht erreichbar.',
'utility.poll.created': 'Umfrage erstellt.',
'utility.poll.closed': 'Diese Umfrage ist geschlossen.',
'utility.poll.notFound': 'Umfrage nicht gefunden.',
'utility.poll.invalidOption': 'Ungültige Option.',
'utility.poll.alreadyVoted': 'Du hast bereits abgestimmt.',
'utility.poll.voted': 'Stimme für **{option}** gezählt.',
'utility.poll.votes': 'Stimmen',
'utility.poll.endsAt': 'Endet {time}',
'utility.poll.multiSelect': 'Mehrfachauswahl',
'utility.poll.multiSelectHint': 'Du kannst mehrere Optionen wählen.',
'utility.remindme.created': 'Erinnerung erstellt (ID: `{id}`).',
'utility.reminders.none': 'Keine Erinnerungen.',
'utility.reminders.line': '`{id}` — {time}: {content}',
'utility.reminders.deleted': 'Erinnerung gelöscht.',
'utility.reminders.notFound': 'Erinnerung nicht gefunden.',
'utility.afk.set': 'AFK gesetzt. Grund: {reason}',
'utility.afk.welcomeBack': 'Willkommen zurück! Du warst AFK: {reason}',
'utility.afk.mention': '{user} ist AFK: {reason}',
'utility.avatar.title': 'Avatar von {user}',
'utility.banner.title': 'Banner von {user}',
'utility.banner.none': 'Kein Banner gesetzt.',
'utility.emoji.noSource': 'Anhang oder URL erforderlich.',
'utility.emoji.added': 'Emoji {emoji} hinzugefügt.',
'utility.emoji.removed': 'Emoji entfernt.',
'utility.emoji.notFound': 'Emoji nicht gefunden.',
'utility.emoji.messageNotFound': 'Nachricht nicht gefunden.',
'utility.emoji.noEmojiInMessage': 'Kein Custom-Emoji in der Nachricht.',
'utility.emoji.stolen': 'Emoji {emoji} gestohlen.',
'utility.sticker.noSource': 'Anhang oder URL erforderlich.',
'utility.sticker.added': 'Sticker **{name}** hinzugefügt.',
'utility.embed.sent': 'Embed gesendet.',
'utility.embed.invalidColor': 'Ungültige Farbe. Nutze z. B. 6366F1.',
'utility.snipe.none': 'Nichts zum Anzeigen.',
'utility.snipe.disabled': 'Snipe ist auf diesem Server deaktiviert (Datenschutz).',
'utility.snipe.privacy':
'Datenschutz: Snipe speichert gelöschte/bearbeitete Nachrichten kurzzeitig (1 h) in Redis.',
'utility.snipe.deletedAt': 'Gelöschte Nachricht von {user}',
'utility.snipe.editedAt': 'Bearbeitete Nachricht von {user}',
'utility.translate.noContent': 'Kein übersetzbare Textinhalt.',
'utility.translate.failed': 'Übersetzung fehlgeschlagen.',
'utility.translate.result': '**Übersetzung:** {text}',
'fun.8ball.result': '**Frage:** {question}\n**Antwort:** {answer}',
'fun.dice.result': 'Würfel ({sides} Seiten): {rolls}',
'fun.flip.result': 'Die Münze zeigt: **{result}**',
'fun.rps.win': 'Du wähltest **{player}**, ich **{bot}**. Du gewinnst!',
'fun.rps.lose': 'Du wähltest **{player}**, ich **{bot}**. Du verlierst!',
'fun.rps.draw': 'Beide wählten **{player}**. Unentschieden!',
'fun.rps.choice.rock': 'Stein',
'fun.rps.choice.paper': 'Papier',
'fun.rps.choice.scissors': 'Schere',
'fun.choose.result': 'Ich wähle: **{choice}**',
'fun.choose.tooFew': 'Gib mindestens zwei Optionen an (kommagetrennt).',
'fun.trivia.title': 'Trivia',
'fun.trivia.question': '**{question}**\n\n{options}',
'fun.trivia.footer': 'Nur {user} kann antworten.',
'fun.trivia.correct': '✅ Richtig!',
'fun.trivia.incorrect': '❌ Falsch!',
'fun.trivia.notOwner': 'Nur der Initiator kann diese Frage beantworten.',
'fun.tictactoe.title': 'Tic-Tac-Toe',
'fun.tictactoe.board': '❌: {x} | ⭕: {o}\n\n{board}',
'fun.tictactoe.challenge': '{challenger} fordert {opponent} zu Tic-Tac-Toe heraus!',
'fun.tictactoe.turn': 'Am Zug: {user}',
'fun.tictactoe.won': '{user} gewinnt!',
'fun.tictactoe.draw': 'Unentschieden!',
'fun.tictactoe.invalidMove': 'Ungültiger Zug.',
'fun.connect4.title': 'Connect 4',
'fun.connect4.board': '🔴: {red} | 🟡: {yellow}\n\n{board}',
'fun.connect4.challenge': '{challenger} fordert {opponent} zu Connect 4 heraus!',
'fun.connect4.turn': 'Am Zug: {user}',
'fun.connect4.won': '{user} gewinnt!',
'fun.connect4.draw': 'Unentschieden!',
'fun.connect4.invalidMove': 'Diese Spalte ist voll.',
'fun.hangman.title': 'Hangman',
'fun.hangman.progress': 'Wort: {word}\nFehler: {wrong}/{max}\nGeraten: {guessed}',
'fun.hangman.won': 'Gewonnen! Das Wort war **{word}**.',
'fun.hangman.lost': 'Verloren! Das Wort war **{word}**.',
'fun.common.notParticipant': 'Du bist nicht an diesem Spiel beteiligt.',
'fun.common.expired': 'Dieses Spiel ist abgelaufen oder existiert nicht mehr.',
'fun.common.selfChallenge': 'Du kannst dich nicht selbst herausfordern.',
'fun.common.botChallenge': 'Du kannst keinen Bot herausfordern.',
'fun.media.disabled': 'Medien-Commands sind auf diesem Server deaktiviert.',
'fun.media.fetchFailed': 'Bild konnte nicht geladen werden. Versuche es später erneut.',
'fun.cat.title': 'Random Cat',
'fun.dog.title': 'Random Dog',
'starboard.notConfigured': 'Starboard ist noch nicht konfiguriert. Nutze `/starboard setup`.',
'starboard.invalidChannel':
'Der Starboard-Kanal ist ungültig oder dem Bot fehlen Rechte (Ansehen, Senden, Embeds).',
'starboard.setupDone': 'Starboard konfiguriert und aktiviert.',
'starboard.noContent': '*Kein Textinhalt*',
'starboard.field.stars': 'Sterne',
'starboard.field.source': 'Quelle',
'starboard.jumpLink': 'Zur Nachricht',
'starboard.statusLine':
'Starboard aktiv → Kanal {channel}, Emoji {emoji}, Schwelle {threshold}, Self-Star: {selfStar}',
'starboard.selfStarOn': 'erlaubt',
'starboard.selfStarOff': 'verboten',
'suggestion.created': 'Vorschlag eingereicht (ID: `{id}`).',
'suggestion.setupDone': 'Vorschlagssystem konfiguriert.',
'suggestion.staffUpdated': 'Vorschlag aktualisiert ({status}).',
'suggestion.votedUp': 'Upvote gezählt.',
'suggestion.votedDown': 'Downvote gezählt.',
'suggestion.threadName': 'Diskussion #{id}',
'suggestion.error.not_configured': 'Vorschläge sind nicht konfiguriert. Nutze `/suggestion setup`.',
'suggestion.error.invalid_channel': 'Der konfigurierte Kanal ist ungültig.',
'suggestion.error.not_found': 'Vorschlag nicht gefunden.',
'suggestion.error.not_open': 'Dieser Vorschlag ist nicht mehr offen.',
'suggestion.error.already_voted': 'Du hast bereits so abgestimmt.',
'suggestion.embed.title': 'Vorschlag #{id}',
'suggestion.embed.status': 'Status',
'suggestion.embed.votes': 'Stimmen',
'suggestion.embed.author': 'Von {author}',
'suggestion.embed.staffReason': 'Team-Begründung',
'suggestion.status.open': 'Offen',
'suggestion.status.approved': 'Angenommen',
'suggestion.status.denied': 'Abgelehnt',
'suggestion.status.considered': 'In Prüfung',
'suggestion.status.implemented': 'Umgesetzt',
'suggestion.dm.statusUpdate': 'Dein Vorschlag wurde aktualisiert: **{status}**\nBegründung: {reason}',
'giveaway.embed.title': 'Giveaway',
'giveaway.embed.prize': 'Preis',
'giveaway.embed.winners': 'Gewinner',
'giveaway.embed.entrants': 'Teilnehmer',
'giveaway.embed.host': 'Host',
'giveaway.embed.requirements': 'Anforderungen',
'giveaway.embed.active': 'Klicke auf **Join**, um teilzunehmen!',
'giveaway.embed.paused': '⏸️ Dieses Giveaway ist pausiert.',
'giveaway.embed.ended': 'Beendet',
'giveaway.embed.endedWinners': 'Gewinner: {winners}',
'giveaway.embed.endedNoWinners': 'Keine gültigen Teilnehmer.',
'giveaway.embed.endsAt': 'Endet {time}',
'giveaway.button.join': 'Teilnehmen',
'giveaway.requirement.role': 'Rolle: {role}',
'giveaway.requirement.level': 'Mindest-Level: {level}',
'giveaway.requirement.memberDays': 'Server-Mitglied seit mindestens {days} Tagen',
'giveaway.started': 'Giveaway gestartet (ID: `{id}`).',
'giveaway.ended': 'Giveaway beendet.',
'giveaway.rerolled': 'Neue Gewinner: {winners}',
'giveaway.deleted': 'Giveaway gelöscht.',
'giveaway.paused': 'Giveaway pausiert.',
'giveaway.resumed': 'Giveaway fortgesetzt.',
'giveaway.joined': 'Du nimmst am Giveaway teil!',
'giveaway.list.header': '**Aktive Giveaways**',
'giveaway.list.empty': 'Keine aktiven Giveaways.',
'giveaway.list.entrants': 'Teilnehmer',
'giveaway.dm.won': 'Glückwunsch! Du hast **{prize}** auf **{guild}** gewonnen!',
'giveaway.error.not_found': 'Giveaway nicht gefunden oder bereits beendet.',
'giveaway.error.already_ended': 'Dieses Giveaway ist bereits beendet.',
'giveaway.error.not_ended': 'Giveaway ist noch nicht beendet.',
'giveaway.error.paused': 'Dieses Giveaway ist pausiert.',
'giveaway.error.already_joined': 'Du nimmst bereits teil.',
'giveaway.error.member_not_found': 'Mitglied nicht gefunden.',
'giveaway.error.missing_role': 'Dir fehlt die erforderliche Rolle.',
'giveaway.error.insufficient_level': 'Dein Level ist zu niedrig.',
'giveaway.error.insufficient_member_days': 'Du bist noch nicht lange genug auf dem Server.',
'giveaway.error.invalid_duration': 'Ungültige Dauer. Nutze z. B. 1h, 2d.',
'giveaway.error.duration_too_short': 'Die Dauer muss mindestens 1 Minute betragen.',
'giveaway.error.invalid_channel': 'Ungültiger Kanal.',
'ticket.panel.defaultDescription': 'Wähle eine Kategorie, um ein Ticket zu öffnen.',
'ticket.panel.created': 'Ticket-Panel erstellt.',
'ticket.category.created': 'Kategorie **{name}** erstellt.',
'ticket.created': 'Dein Ticket wurde erstellt: {channel}',
'ticket.opened': '{user} hat ein Ticket geöffnet.',
'ticket.subjectLine': 'Betreff: {subject}',
'ticket.claimed': '{user} hat dieses Ticket übernommen.',
'ticket.userAdded': '{user} wurde zum Ticket hinzugefügt.',
'ticket.userRemoved': '{user} wurde aus dem Ticket entfernt.',
'ticket.renamed': 'Ticket umbenannt in **{name}**.',
'ticket.prioritySet': 'Priorität gesetzt: **{priority}**',
'ticket.closed': 'Ticket geschlossen von {user}.',
'ticket.closedWithReason': 'Ticket geschlossen von {user}.\nGrund: {reason}',
'ticket.close.success': 'Ticket geschlossen.',
'ticket.claim.success': 'Ticket übernommen.',
'ticket.add.success': '{user} hinzugefügt.',
'ticket.remove.success': '{user} entfernt.',
'ticket.rename.success': 'Ticket umbenannt in **{name}**.',
'ticket.priority.success': 'Priorität: **{priority}**',
'ticket.transcriptDm': 'Hier ist dein Ticket-Transkript.',
'ticket.ratePrompt': 'Wie zufrieden warst du mit dem Support? (15)',
'ticket.rated': 'Danke für deine Bewertung: {rating}/5',
'ticket.autoCloseReason': 'Automatisch wegen Inaktivität geschlossen',
'ticket.modal.subject': 'Betreff',
'ticket.log.closed': 'Ticket geschlossen',
'ticket.log.opener': 'Ersteller',
'ticket.log.closedBy': 'Geschlossen von',
'ticket.priority.LOW': 'Niedrig',
'ticket.priority.NORMAL': 'Normal',
'ticket.priority.HIGH': 'Hoch',
'ticket.priority.URGENT': 'Dringend',
'ticket.error.disabled': 'Das Ticket-System ist deaktiviert.',
'ticket.error.category_not_found': 'Kategorie nicht gefunden.',
'ticket.error.no_categories': 'Keine Ticket-Kategorien vorhanden.',
'ticket.error.not_in_ticket': 'Dieser Befehl kann nur in einem Ticket-Kanal genutzt werden.',
'ticket.error.not_staff': 'Du hast keine Berechtigung für diese Ticket-Aktion.',
'ticket.error.already_open': 'Du hast bereits ein offenes Ticket.',
'ticket.error.closed': 'Dieses Ticket ist bereits geschlossen.',
'ticket.error.already_claimed': 'Du hast dieses Ticket bereits übernommen.',
'ticket.error.cannot_remove_opener': 'Der Ticket-Ersteller kann nicht entfernt werden.',
'ticket.error.no_channel': 'Ticket-Kanal nicht gefunden.',
'ticket.error.invalid_name': 'Ungültiger Name.',
'ticket.error.invalid_channel': 'Ungültiger Kanal.',
'ticket.error.no_parent_channel': 'Kein Elternkanal für Thread-Tickets konfiguriert.',
'ticket.error.invalid_parent_channel': 'Der konfigurierte Elternkanal ist ungültig.',
'ticket.error.not_found': 'Ticket nicht gefunden.',
'ticket.error.already_rated': 'Du hast dieses Ticket bereits bewertet.',
'birthdays.error.invalidDate': 'Ungültiges Datum. Prüfe Tag, Monat und optionales Jahr.',
'birthdays.error.invalidTimezone': 'Ungültige Zeitzone. Nutze z. B. Europe/Berlin.',
'birthdays.setup.done': 'Geburtstags-Modul konfiguriert.',
'birthdays.set.success': 'Geburtstag gespeichert: {day}.{month}.{year}',
'birthdays.remove.none': 'Du hast keinen Geburtstag eingetragen.',
'birthdays.remove.success': 'Dein Geburtstag wurde entfernt.',
'birthdays.list.empty': 'Noch keine Geburtstage eingetragen.',
'birthdays.list.header': '**Geburtstage (sortiert nach nächstem Datum):**',
'birthdays.next.header': '**Nächste Geburtstage:**',
'birthdays.list.line': '{rank}. {user} — {date} (in {days} T.)',
'stats.notConfigured': 'Server-Statistiken sind noch nicht eingerichtet. Nutze `/stats setup`.',
'stats.setup.done': 'Statistik-Kanäle konfiguriert und aktualisiert.',
'stats.refresh.done': 'Statistik-Kanäle wurden aktualisiert.',
'stats.error.invalidSetup': 'Ungültige Eingabe für die Statistik-Konfiguration.',
'invites.view.empty': '{user} hat noch keine Invite-Statistiken.',
'invites.view.result':
'**Invite-Statistik für {user}**\nJoins: {joins} | Leaves: {leaves} | Fakes: {fakes} | Netto: {net}',
'invites.leaderboard.empty': 'Noch keine Invite-Daten vorhanden.',
'invites.leaderboard.header': '**Top Einlader:**',
'invites.leaderboard.line':
'{rank}. {user} — Joins: {joins}, Leaves: {leaves}, Fakes: {fakes}, Netto: {net}',
'tempvoice.error.disabled': 'Temp-Voice ist auf diesem Server deaktiviert.',
'tempvoice.error.invalidCategory': 'Ungültige Kategorie.',
'tempvoice.error.notOwner': 'Du bist nicht der Besitzer dieses Temp-Voice-Kanals.',
'tempvoice.error.notInChannel': 'Du musst in einem Temp-Voice-Kanal sein.',
'tempvoice.error.notFound': 'Temp-Voice-Kanal nicht gefunden.',
'tempvoice.error.invalidName': 'Ungültiger Kanalname.',
'tempvoice.error.invalidLimit': 'Limit muss zwischen 0 und 99 liegen.',
'tempvoice.setup.done': 'Temp-Voice konfiguriert.',
'tempvoice.rename.success': 'Kanal umbenannt in **{name}**.',
'tempvoice.limit.success': 'Nutzerlimit auf **{limit}** gesetzt (0 = unbegrenzt).',
'tempvoice.lock.success': 'Kanal gesperrt (Connect für @everyone entfernt).',
'tempvoice.hide.success': 'Kanal versteckt (ViewChannel für @everyone entfernt).',
'tempvoice.kick.self': 'Du kannst dich nicht selbst kicken.',
'tempvoice.kick.notInChannel': 'Dieser Nutzer ist nicht in deinem Kanal.',
'tempvoice.kick.success': '{user} wurde aus dem Kanal entfernt.',
'tempvoice.transfer.self': 'Du kannst den Kanal nicht an dich selbst übertragen.',
'tempvoice.transfer.notInChannel': 'Der neue Besitzer muss in deinem Kanal sein.',
'tempvoice.transfer.success': 'Kanal an {user} übertragen.',
'tempvoice.claim.ownerPresent': 'Der Besitzer ist noch im Kanal.',
'tempvoice.claim.success': 'Du bist jetzt Besitzer dieses Kanals.',
'tempvoice.panel.title': 'Temp-Voice Steuerung',
'tempvoice.panel.description': 'Verwalte **{channel}** mit den Buttons oder `/voice`-Commands.',
'tempvoice.panel.sent': 'Control-Panel wurde in den Voice-Kanal-Chat gesendet.',
'tempvoice.button.rename': 'Umbenennen',
'tempvoice.button.limit': 'Limit',
'tempvoice.button.lock': 'Sperren',
'tempvoice.button.hide': 'Verstecken',
'tempvoice.button.kick': 'Kicken',
'tempvoice.button.transfer': 'Übertragen',
'tempvoice.button.claim': 'Übernehmen',
'selfroles.panel.defaultDescription': 'Wähle unten deine Rollen aus.',
'selfroles.panel.rolesField': 'Rollen',
'selfroles.panel.temporaryFooter': 'Rollen sind temporär und laufen automatisch ab.',
'selfroles.panel.created': 'Self-Role-Panel erstellt (ID: `{id}`).',
'selfroles.panel.updated': 'Self-Role-Panel aktualisiert (ID: `{id}`).',
'selfroles.panel.deleted': 'Self-Role-Panel gelöscht.',
'selfroles.role.added': 'Rolle hinzugefügt.',
'selfroles.role.removed': 'Rolle entfernt.',
'selfroles.role.verified': 'Rolle vergeben.',
'selfroles.role.unchanged': 'Du hast diese Rolle bereits.',
'selfroles.error.not_found': 'Self-Role-Panel nicht gefunden.',
'selfroles.error.invalid_roles': 'Ungültiges Rollen-Format. Nutze roleId:label:emoji.',
'selfroles.error.too_many_roles': 'Maximal 25 Rollen pro Panel.',
'selfroles.error.invalid_channel': 'Ungültiger Kanal.',
'selfroles.error.invalid_duration': 'Ungültige Dauer. Nutze z. B. 1h, 2d.',
'selfroles.error.duration_too_short': 'Die Dauer muss mindestens 1 Minute betragen.',
'selfroles.error.duration_required': 'Für temporäre Panels ist eine Dauer erforderlich.',
'selfroles.error.reaction_emoji_required': 'Reaktions-Modus erfordert Emojis für alle Rollen.',
'selfroles.error.member_not_found': 'Mitglied nicht gefunden.',
'selfroles.error.role_not_in_panel': 'Diese Rolle gehört nicht zu diesem Panel.',
'selfroles.error.role_not_found': 'Rolle nicht gefunden.',
'selfroles.error.role_hierarchy': 'Die Rolle liegt über der höchsten Bot-Rolle.',
'selfroles.error.role_managed': 'Integrierte Rollen können nicht vergeben werden.',
'selfroles.error.bot_missing_permissions': 'Dem Bot fehlt die Berechtigung Rollen zu verwalten.',
'tag.created': 'Tag `{name}` erstellt.',
'tag.updated': 'Tag `{name}` aktualisiert.',
'tag.deleted': 'Tag gelöscht.',
'tag.list.header': '**Tags**',
'tag.list.empty': 'Keine Tags vorhanden.',
'tag.list.uses': 'Aufrufe',
'tag.info.name': 'Name: `{name}`',
'tag.info.type': 'Typ: {type}',
'tag.info.uses': 'Aufrufe: {count}',
'tag.info.trigger': 'Trigger: `{word}`',
'tag.info.noTrigger': 'Kein Auto-Responder.',
'tag.info.roles': 'Rollen: {roles}',
'tag.info.allRoles': 'Rollen: alle',
'tag.info.channels': 'Kanäle: {channels}',
'tag.info.allChannels': 'Kanäle: alle',
'tag.error.not_found': 'Tag nicht gefunden.',
'tag.error.not_allowed': 'Du darfst diesen Tag hier nicht nutzen.',
'tag.error.invalid_name': 'Ungültiger Tag-Name (132 Zeichen, a-z, 0-9, _, -).',
'tag.error.content_required': 'Text-Tags benötigen Inhalt.',
'tag.error.embed_required': 'Embed-Tags benötigen Titel oder Beschreibung.',
'tag.error.premium_limit': 'Tag-Limit für diese Premium-Stufe erreicht.',
'tag.error.premium_feature': 'Tags sind für diese Premium-Stufe deaktiviert.',
'guildbackup.description': 'Server-Backups erstellen und wiederherstellen',
'guildbackup.created': 'Backup **{name}** erstellt (ID: `{id}`).',
'guildbackup.deleted': 'Backup `{id}` gelöscht.',
'guildbackup.list.header': '**Server-Backups**',
'guildbackup.list.empty': 'Keine Backups vorhanden.',
'guildbackup.list.line': '• `{id}` — **{name}** ({date})',
'guildbackup.info.id': 'ID: `{id}`',
'guildbackup.info.name': 'Name: **{name}**',
'guildbackup.info.created': 'Erstellt: {date}',
'guildbackup.info.createdBy': 'Erstellt von: <@{userId}>',
'guildbackup.info.roles': 'Rollen: {count}',
'guildbackup.info.channels': 'Kanäle: {count}',
'guildbackup.info.limitations':
'Hinweis: Backups enthalten keine Nachrichten. Beim Restore werden IDs nicht übernommen; fehlende Rollen/Kanäle werden nach Name ergänzt, bestehende nicht gelöscht.',
'guildbackup.confirm.delete': 'Backup `{id}` wirklich löschen?',
'guildbackup.confirm.restore.step1':
'Backup `{id}` wiederherstellen? Es werden fehlende Rollen/Kanäle ergänzt und Overwrites best-effort angewendet. Bestehende Kanäle werden **nicht** gelöscht.',
'guildbackup.confirm.restore.step2':
'**Letzte Bestätigung:** Backup `{id}` jetzt wiederherstellen? Diese Aktion kann nicht rückgängig gemacht werden.',
'guildbackup.confirm.button.confirm': 'Bestätigen',
'guildbackup.confirm.button.cancel': 'Abbrechen',
'guildbackup.confirm.expired': 'Diese Bestätigung ist abgelaufen.',
'guildbackup.confirm.unauthorized': 'Nur der auslösende Nutzer kann das bestätigen.',
'guildbackup.confirm.cancelled': 'Abgebrochen.',
'guildbackup.restore.done':
'Backup `{id}` wiederhergestellt. Rollen: {rolesCreated} neu, {rolesSkipped} übersprungen. Kanäle: {channelsCreated} neu, {channelsUpdated} aktualisiert. Overwrites: {overwritesApplied}.',
'guildbackup.restore.queued':
'Großes Backup `{id}` — Restore wurde in die Warteschlange gestellt und läuft im Hintergrund.',
'guildbackup.error.not_found': 'Backup nicht gefunden.',
'guildbackup.error.invalid_payload': 'Backup-Daten sind ungültig oder veraltet.',
'guildbackup.error.invalid_name': 'Ungültiger Backup-Name (1100 Zeichen).',
'guildbackup.error.owner_only': 'Nur der Server-Owner kann Backups wiederherstellen.',
'guildbackup.error.bot_missing_permissions':
'Dem Bot fehlen Manage Roles oder Manage Channels für einen Restore.',
'guildbackup.error.premium_limit': 'Backup-Limit für diese Premium-Stufe erreicht.',
'guildbackup.error.premium_feature': 'Backups sind für diese Premium-Stufe deaktiviert.',
'feeds.added': 'Feed `{id}` ({type}) hinzugefügt.',
'feeds.removed': 'Feed `{id}` entfernt.',
'feeds.list.header': '**Social Feeds**',
'feeds.list.empty': 'Keine Feeds konfiguriert.',
'feeds.list.line': '`{id}` — {type}: {source} → {channel}',
'feeds.test.posted': 'Test erfolgreich: {count} Nachricht(en) gesendet.',
'feeds.test.noItems': 'Keine neuen Einträge gefunden.',
'feeds.test.failed': 'Abruf fehlgeschlagen: {reason}',
'feeds.test.skipped': 'Feed-Test übersprungen.',
'feeds.test.twitchSkipped':
'Twitch-Feed übersprungen: TWITCH_CLIENT_ID und TWITCH_CLIENT_SECRET fehlen in der Bot-Konfiguration.',
'feeds.error.not_found': 'Feed nicht gefunden.',
'feeds.error.premium_limit': 'Feed-Limit für diese Premium-Stufe erreicht.',
'feeds.error.premium_feature': 'Feeds sind für diese Premium-Stufe deaktiviert.',
'scheduler.created': 'Geplante Nachricht erstellt (ID: `{id}`). {timing}',
'scheduler.created.at': 'Sendung: {time}',
'scheduler.created.cron': 'Wiederholung: `{cron}`',
'scheduler.deleted': 'Geplante Nachricht gelöscht.',
'scheduler.announce.sent': 'Ankündigung gesendet.',
'scheduler.list.header': '**Geplante Nachrichten**',
'scheduler.list.empty': 'Keine geplanten Nachrichten.',
'scheduler.list.line': '`{id}` — {channel} — {timing} — {preview}',
'scheduler.list.cron': 'Cron `{cron}`',
'scheduler.list.embedPreview': '[Embed]',
'scheduler.list.componentsPreview': '[Components V2]',
'scheduler.error.missing_schedule_time': 'Gib `when` oder `cron` an.',
'scheduler.error.invalid_when': 'Ungültige Zeit. Nutze z. B. 1h, 2d oder ein ISO-Datum.',
'scheduler.error.invalid_cron': 'Ungültiger Cron-Ausdruck (56 Felder).',
'scheduler.error.invalid_channel': 'Ungültiger Kanal.',
'scheduler.error.channel_unavailable': 'Der Bot kann in diesem Kanal nicht senden.',
'scheduler.error.not_found': 'Geplante Nachricht nicht gefunden.',
'scheduler.error.content_required': 'Inhalt oder Embed-Titel/Beschreibung erforderlich.',
'componentsV2.error.notFound': 'Diese Komponenten-Nachricht ist nicht mehr verfügbar.',
'componentsV2.error.actionMissing': 'Für diese Interaktion ist keine Aktion konfiguriert.',
'componentsV2.error.botMissingManageRoles': 'Dem Bot fehlt die Berechtigung Rollen zu verwalten.',
'componentsV2.error.roleNotFound': 'Rolle nicht gefunden.',
'componentsV2.error.roleManaged': 'Integrierte Rollen können nicht vergeben werden.',
'componentsV2.error.roleHierarchy': 'Die Rolle liegt über der höchsten Bot-Rolle.',
'componentsV2.error.noRolesSelected': 'Keine Rollen ausgewählt.',
'componentsV2.role.assigned': 'Rolle vergeben.',
'componentsV2.role.removed': 'Rolle entfernt.',
'componentsV2.role.assignedSelected': 'Ausgewählte Rollen vergeben.',
'utility.embed.components.sent': 'Components-V2-Nachricht gesendet.',
'utility.embed.components.dashboardHint':
'Für den vollen Builder inkl. Interaktionen: {url}',
'utility.embed.components.missingText': 'Text ist erforderlich.',
'tag.error.components_required': 'Components-V2-Tags benötigen Komponenten.'
};
const en: Dictionary = {
'generic.noPermission': 'You do not have permission for this action.',
'generic.guildOnly': 'This command can only be used in a server.',
'generic.botMissingPermission': 'Bot lacks required permission ({permission}).',
'generic.invalidRegex': 'Invalid regex pattern.',
'generic.error': 'An error occurred.',
'generic.maintenance': 'Nexumi is currently in maintenance mode. Please try again later.',
'generic.unknownCommand': 'Unknown command.',
'generic.commandDisabled': 'This command is disabled on this server.',
'generic.commandChannelBlocked': 'This command is not allowed in this channel.',
'generic.commandRoleBlocked': 'You are not allowed to use this command with your roles.',
'generic.commandCooldown': 'Please wait {seconds} more seconds before using this command again.',
'generic.moduleDisabled': 'This module is disabled on this server.',
'generic.userBlacklisted': 'You are not allowed to use Nexumi right now.',
'generic.guildBlacklisted': 'This server is not allowed to use Nexumi right now.',
'core.help.title': 'Nexumi Help',
'core.help.subtitle': 'Commands by module. Optional: `/help module:<name>`.',
'core.help.moduleLine': '**{module}**: {commands}',
'core.help.unknownModule': 'Unknown module. Available: {modules}',
'core.help.footer': 'More: /info · Dashboard: {webui}',
'core.info.title': 'Nexumi',
'core.info.version': 'Version',
'core.info.uptime': 'Uptime',
'core.info.shard': 'Shard',
'core.info.guilds': 'Servers (this shard)',
'core.info.latency': 'API latency',
'core.info.links': 'Links',
'core.info.link.dashboard': 'Dashboard',
'core.info.link.invite': 'Invite',
'core.info.link.support': 'Support',
'core.info.link.status': 'Status',
'core.invite.title': 'Invite Nexumi',
'core.invite.body': 'Invite Nexumi to your server with this link:',
'core.support.title': 'Support',
'core.support.body': 'Join our support server:',
'core.support.missing': 'No support server configured. Set SUPPORT_SERVER_URL.',
'core.module.core': 'Core',
'core.module.moderation': 'Moderation',
'core.module.automod': 'Auto-Moderation',
'core.module.welcome': 'Welcome',
'core.module.verification': 'Verification',
'core.module.leveling': 'Leveling',
'core.module.economy': 'Economy',
'core.module.utility': 'Utility',
'core.module.fun': 'Fun',
'core.module.giveaways': 'Giveaways',
'core.module.tickets': 'Tickets',
'core.module.selfroles': 'Self-Roles',
'core.module.tags': 'Tags',
'core.module.starboard': 'Starboard',
'core.module.suggestions': 'Suggestions',
'core.module.birthdays': 'Birthdays',
'core.module.tempvoice': 'Temp Voice',
'core.module.stats': 'Stats',
'core.module.feeds': 'Feeds',
'core.module.scheduler': 'Scheduler',
'core.module.guildbackup': 'Backup',
'gdpr.privacy.title': 'Privacy',
'gdpr.privacy.body':
'Nexumi stores Discord IDs and module-related data (e.g. warnings, XP, economy) as needed to operate.',
'gdpr.privacy.link': 'Full privacy policy: {url}',
'gdpr.privacy.deleteHint': 'Delete personal data: `/gdpr delete confirm:true`',
'gdpr.delete.needConfirm': 'Set `confirm` to `true` to confirm deletion.',
'gdpr.delete.doneTitle': 'Data deleted',
'gdpr.delete.doneBody':
'Deleted/anonymized: {warnings} warnings, {levels} level rows, {economy} economy rows, {cases} cases anonymized.',
'moderation.success': 'Action executed successfully.',
'moderation.success.case': 'Action executed successfully (case #{caseNumber}).',
'moderation.duration.invalid': 'Invalid duration. Use e.g. 30m, 12h, or 7d.',
'moderation.duration.invalidTimeout':
'Invalid timeout duration. Use e.g. 30m or 1d (max 28 days).',
'moderation.hierarchy.self': 'You cannot apply this action to yourself.',
'moderation.hierarchy.owner': 'The server owner cannot be moderated.',
'moderation.hierarchy.moderator':
'You cannot moderate members with equal or higher roles.',
'moderation.hierarchy.bot':
'The bot cannot moderate this member due to role hierarchy or missing permissions.',
'moderation.hierarchy.notMember': 'That member is not on this server.',
'moderation.reason.none': 'No reason provided',
'moderation.warning.created': 'Warning created: {warningId}',
'moderation.warning.none': 'No warnings.',
'moderation.warning.defaultReason': 'No reason',
'moderation.warning.notFound': 'Warning not found.',
'moderation.purge.done': 'Deleted {deletedCount} messages (case #{caseNumber}).',
'moderation.note.none': 'No notes.',
'moderation.case.notFound': 'Case not found.',
'moderation.escalation.durationRequired': 'Duration is required for TIMEOUT escalation.',
'moderation.escalation.noneConfigured': 'No escalation rules configured.',
'moderation.escalation.saved':
'Escalation rule saved: {warnCount} warnings => {action}{duration}.',
'moderation.escalation.removed': 'Escalation rule removed for {warnCount} warnings.',
'moderation.escalation.defaultReason': 'Auto escalation at {warnCount} warnings',
'moderation.escalation.skippedDuration':
'Escalation rule for {warnCount} warns skipped (missing duration).',
'moderation.escalation.skippedPermission':
'Escalation rule for {warnCount} warns skipped (missing bot permissions).',
'moderation.escalation.appliedTimeout': 'Auto escalation applied: timeout ({durationMs} ms).',
'moderation.escalation.appliedKick': 'Auto escalation applied: kick.',
'moderation.escalation.appliedBan': 'Auto escalation applied: ban.',
'moderation.confirm.button.confirm': 'Confirm',
'moderation.confirm.button.cancel': 'Cancel',
'moderation.confirm.permanent': 'permanent',
'moderation.confirm.ban':
'Confirm ban for <@{userId}>?\nReason: {reason}\nDuration: {duration}',
'moderation.confirm.purge':
'Delete {amount} messages? This action cannot be undone.',
'moderation.confirm.caseDelete': 'Really delete case #{caseNumber}?',
'moderation.confirm.expired': 'Confirmation expired. Please run the command again.',
'moderation.confirm.cancelled': 'Action cancelled.',
'moderation.confirm.unauthorized': 'Only the moderator who started this action can confirm it.',
'automod.status.header':
'**AutoMod enabled:** {enabled}\n**Anti-raid:** {antiRaid}\n**Anti-nuke:** {antiNuke}\n**Lockdown:** {lockdown}',
'automod.status.ruleLine': '- {type} → {action}',
'automod.status.noRules': 'No active rules.',
'automod.status.yes': 'yes',
'automod.status.no': 'no',
'welcome.notConfigured': 'Welcome message is not configured yet.',
'welcome.invalidChannel': 'The configured welcome channel is invalid.',
'welcome.testSent': 'Test welcome message sent.',
'verification.disabled': 'Verification is disabled.',
'verification.success': 'You have been verified successfully.',
'verification.setupDone': 'Verification configured.',
'verification.notConfigured': 'Verification is not set up yet. Use `/verify setup`.',
'verification.invalidChannel': 'The verification channel is invalid.',
'verification.panelPosted': 'Verification panel posted.',
'verification.panelMissingPermission':
'The bot lacks permissions in that channel (View Channel, Send Messages, Embed Links).',
'verification.panel.title': 'Verification',
'verification.panel.description':
'Click the button below to verify and gain access to the server.',
'verification.panel.button': 'Verify',
'verification.panel.buttonCaptcha': 'Verify (Captcha)',
'verification.captchaLink': 'Open this link to complete captcha verification: {url}',
'verification.error.not_configured': 'Verification is not configured.',
'verification.error.member_not_found': 'Member not found.',
'verification.error.account_too_young': 'Your account is too young to verify.',
'verification.error.missing_permissions': 'Bot lacks permissions.',
'verification.error.invalid_role': 'Verification role is invalid.',
'verification.error.alt_detected':
'Verification denied: possible alternate account detected.',
'verification.error.generic': 'Verification failed.',
'verification.captcha.success': 'Captcha passed. You have been verified.',
'verification.captcha.invalid': 'Captcha invalid or expired.',
'verification.captcha.title': 'Nexumi Verification',
'leveling.disabled': 'Leveling is disabled on this server.',
'leveling.rank.title': 'Rank card',
'leveling.leaderboard.title': 'Leaderboard',
'leveling.leaderboard.empty': 'No XP data yet.',
'leveling.leaderboard.entry': '{rank}. {user} — Level {level} ({xp} XP)',
'leveling.leaderboard.footer': 'Top {count} members',
'leveling.levelUp.default': 'Congratulations {user}, you reached level {level}!',
'leveling.xp.give.success': 'Gave {amount} XP to {user}. New level: {level}.',
'leveling.xp.remove.success': 'Removed {amount} XP from {user}. New level: {level}.',
'leveling.xp.reset.success': 'Reset XP for {user}.',
'leveling.xp.invalidAmount': 'Amount must be greater than 0.',
'leveling.user.notFound': 'User not found on this server.',
'economy.error.disabled': 'The economy module is disabled on this server.',
'economy.error.insufficient': 'You do not have enough money.',
'economy.error.cooldown': 'Cooldown active. Try again in {seconds} seconds.',
'economy.error.invalid_amount': 'Invalid amount.',
'economy.error.self_transfer': 'You cannot send money to yourself.',
'economy.error.not_found': 'Entry not found.',
'economy.error.out_of_stock': 'This item is out of stock.',
'economy.error.role_assign_failed': 'Purchase succeeded, but the role could not be assigned.',
'economy.error.game_in_progress': 'You already have an active blackjack game.',
'economy.balance.result': 'Balance of {user}: {amount}',
'economy.daily.success': 'Daily reward: {amount}. New balance: {balance}',
'economy.weekly.success': 'Weekly reward: {amount}. New balance: {balance}',
'economy.work.success': 'You earned {amount}. New balance: {balance}',
'economy.pay.success': 'You sent {amount} to {user}. Your balance: {balance}',
'economy.gamble.win': 'You won! +{amount}. Balance: {balance}',
'economy.gamble.lose': 'You lost! -{amount}. Balance: {balance}',
'economy.slots.win': '{reels}\nWinnings: {payout}. Balance: {balance}',
'economy.slots.lose': '{reels}\nNo win. Balance: {balance}',
'economy.coinflip.win': '{result} you picked {choice}. You won! +{amount}. Balance: {balance}',
'economy.coinflip.lose': '{result} you picked {choice}. You lost! -{amount}. Balance: {balance}',
'economy.choice.heads': 'Heads',
'economy.choice.tails': 'Tails',
'economy.shop.empty': 'The shop is empty.',
'economy.shop.noDescription': 'No description',
'economy.shop.itemLine': '**{name}** (`{id}`) {price} | Stock: {stock}\n{description}',
'economy.shop.purchased': 'You purchased **{name}**. Balance: {balance}',
'economy.inventory.empty': 'Your inventory is empty.',
'economy.inventory.itemLine': '- {name} × {quantity}',
'economy.leaderboard.empty': 'No leaderboard entries yet.',
'economy.leaderboard.line': '{rank}. {user} {amount}',
'economy.admin.give': 'Gave {amount} to {user}. New balance: {balance}',
'economy.admin.remove': 'Removed {amount} from {user}. New balance: {balance}',
'economy.admin.reset': 'Reset economy data for {user}.',
'economy.blackjack.title': 'Blackjack',
'economy.blackjack.hand': 'Bet: {bet}\n\n**You:** {playerHand} ({playerTotal})\n**Dealer:** {dealerHand} ({dealerTotal})',
'economy.blackjack.button.hit': 'Hit',
'economy.blackjack.button.stand': 'Stand',
'economy.blackjack.outcome.win': 'You win {bet}!',
'economy.blackjack.outcome.lose': 'You lose your bet.',
'economy.blackjack.outcome.push': 'Push bet returned.',
'economy.blackjack.outcome.blackjack': 'Blackjack! You win {bet}!',
'economy.blackjack.expired': 'This blackjack game expired or no longer exists.',
'economy.blackjack.notOwner': 'Only the player can use these buttons.',
'utility.error.invalid_when': 'Invalid time. Use e.g. 30m or a future ISO datetime.',
'utility.error.invalid_datetime': 'Invalid date/time.',
'utility.error.invalid_poll_options': 'Provide 25 comma-separated options.',
'utility.error.channel_not_found': 'Channel not found.',
'utility.error.role_not_found': 'Role not found.',
'utility.error.poll_not_found': 'Poll not found.',
'utility.error.reminder_channel_unavailable': 'Reminder channel unavailable.',
'utility.poll.created': 'Poll created.',
'utility.poll.closed': 'This poll is closed.',
'utility.poll.notFound': 'Poll not found.',
'utility.poll.invalidOption': 'Invalid option.',
'utility.poll.alreadyVoted': 'You already voted.',
'utility.poll.voted': 'Vote counted for **{option}**.',
'utility.poll.votes': 'votes',
'utility.poll.endsAt': 'Ends {time}',
'utility.poll.multiSelect': 'Multi-select',
'utility.poll.multiSelectHint': 'You may select multiple options.',
'utility.remindme.created': 'Reminder created (ID: `{id}`).',
'utility.reminders.none': 'No reminders.',
'utility.reminders.line': '`{id}` — {time}: {content}',
'utility.reminders.deleted': 'Reminder deleted.',
'utility.reminders.notFound': 'Reminder not found.',
'utility.afk.set': 'AFK set. Reason: {reason}',
'utility.afk.welcomeBack': 'Welcome back! You were AFK: {reason}',
'utility.afk.mention': '{user} is AFK: {reason}',
'utility.avatar.title': 'Avatar of {user}',
'utility.banner.title': 'Banner of {user}',
'utility.banner.none': 'No banner set.',
'utility.emoji.noSource': 'Attachment or URL required.',
'utility.emoji.added': 'Added emoji {emoji}.',
'utility.emoji.removed': 'Emoji removed.',
'utility.emoji.notFound': 'Emoji not found.',
'utility.emoji.messageNotFound': 'Message not found.',
'utility.emoji.noEmojiInMessage': 'No custom emoji in message.',
'utility.emoji.stolen': 'Stole emoji {emoji}.',
'utility.sticker.noSource': 'Attachment or URL required.',
'utility.sticker.added': 'Added sticker **{name}**.',
'utility.embed.sent': 'Embed sent.',
'utility.embed.invalidColor': 'Invalid color. Use e.g. 6366F1.',
'utility.snipe.none': 'Nothing to show.',
'utility.snipe.disabled': 'Snipe is disabled on this server (privacy).',
'utility.snipe.privacy':
'Privacy: Snipe stores deleted/edited messages briefly (1 h) in Redis.',
'utility.snipe.deletedAt': 'Deleted message by {user}',
'utility.snipe.editedAt': 'Edited message by {user}',
'utility.translate.noContent': 'No translatable text content.',
'utility.translate.failed': 'Translation failed.',
'utility.translate.result': '**Translation:** {text}',
'fun.8ball.result': '**Question:** {question}\n**Answer:** {answer}',
'fun.dice.result': 'Dice ({sides} sides): {rolls}',
'fun.flip.result': 'The coin shows: **{result}**',
'fun.rps.win': 'You picked **{player}**, I picked **{bot}**. You win!',
'fun.rps.lose': 'You picked **{player}**, I picked **{bot}**. You lose!',
'fun.rps.draw': 'Both picked **{player}**. Draw!',
'fun.rps.choice.rock': 'Rock',
'fun.rps.choice.paper': 'Paper',
'fun.rps.choice.scissors': 'Scissors',
'fun.choose.result': 'I choose: **{choice}**',
'fun.choose.tooFew': 'Provide at least two comma-separated options.',
'fun.trivia.title': 'Trivia',
'fun.trivia.question': '**{question}**\n\n{options}',
'fun.trivia.footer': 'Only {user} can answer.',
'fun.trivia.correct': '✅ Correct!',
'fun.trivia.incorrect': '❌ Wrong!',
'fun.trivia.notOwner': 'Only the initiator can answer this question.',
'fun.tictactoe.title': 'Tic-Tac-Toe',
'fun.tictactoe.board': '❌: {x} | ⭕: {o}\n\n{board}',
'fun.tictactoe.challenge': '{challenger} challenges {opponent} to Tic-Tac-Toe!',
'fun.tictactoe.turn': 'Turn: {user}',
'fun.tictactoe.won': '{user} wins!',
'fun.tictactoe.draw': 'Draw!',
'fun.tictactoe.invalidMove': 'Invalid move.',
'fun.connect4.title': 'Connect 4',
'fun.connect4.board': '🔴: {red} | 🟡: {yellow}\n\n{board}',
'fun.connect4.challenge': '{challenger} challenges {opponent} to Connect 4!',
'fun.connect4.turn': 'Turn: {user}',
'fun.connect4.won': '{user} wins!',
'fun.connect4.draw': 'Draw!',
'fun.connect4.invalidMove': 'This column is full.',
'fun.hangman.title': 'Hangman',
'fun.hangman.progress': 'Word: {word}\nWrong: {wrong}/{max}\nGuessed: {guessed}',
'fun.hangman.won': 'You won! The word was **{word}**.',
'fun.hangman.lost': 'You lost! The word was **{word}**.',
'fun.common.notParticipant': 'You are not a participant in this game.',
'fun.common.expired': 'This game expired or no longer exists.',
'fun.common.selfChallenge': 'You cannot challenge yourself.',
'fun.common.botChallenge': 'You cannot challenge a bot.',
'fun.media.disabled': 'Media commands are disabled on this server.',
'fun.media.fetchFailed': 'Could not load image. Try again later.',
'fun.cat.title': 'Random Cat',
'fun.dog.title': 'Random Dog',
'starboard.notConfigured': 'Starboard is not configured yet. Use `/starboard setup`.',
'starboard.invalidChannel':
'The starboard channel is invalid or the bot lacks permissions (View, Send, Embed Links).',
'starboard.setupDone': 'Starboard configured and enabled.',
'starboard.noContent': '*No text content*',
'starboard.field.stars': 'Stars',
'starboard.field.source': 'Source',
'starboard.jumpLink': 'Jump to message',
'starboard.statusLine':
'Starboard active → channel {channel}, emoji {emoji}, threshold {threshold}, self-star: {selfStar}',
'starboard.selfStarOn': 'allowed',
'starboard.selfStarOff': 'denied',
'suggestion.created': 'Suggestion submitted (ID: `{id}`).',
'suggestion.setupDone': 'Suggestion system configured.',
'suggestion.staffUpdated': 'Suggestion updated ({status}).',
'suggestion.votedUp': 'Upvote recorded.',
'suggestion.votedDown': 'Downvote recorded.',
'suggestion.threadName': 'Discussion #{id}',
'suggestion.error.not_configured': 'Suggestions are not configured. Use `/suggestion setup`.',
'suggestion.error.invalid_channel': 'The configured channel is invalid.',
'suggestion.error.not_found': 'Suggestion not found.',
'suggestion.error.not_open': 'This suggestion is no longer open.',
'suggestion.error.already_voted': 'You already voted this way.',
'suggestion.embed.title': 'Suggestion #{id}',
'suggestion.embed.status': 'Status',
'suggestion.embed.votes': 'Votes',
'suggestion.embed.author': 'By {author}',
'suggestion.embed.staffReason': 'Staff reason',
'suggestion.status.open': 'Open',
'suggestion.status.approved': 'Approved',
'suggestion.status.denied': 'Denied',
'suggestion.status.considered': 'Under consideration',
'suggestion.status.implemented': 'Implemented',
'suggestion.dm.statusUpdate': 'Your suggestion was updated: **{status}**\nReason: {reason}',
'giveaway.embed.title': 'Giveaway',
'giveaway.embed.prize': 'Prize',
'giveaway.embed.winners': 'Winners',
'giveaway.embed.entrants': 'Entrants',
'giveaway.embed.host': 'Host',
'giveaway.embed.requirements': 'Requirements',
'giveaway.embed.active': 'Click **Join** to enter!',
'giveaway.embed.paused': '⏸️ This giveaway is paused.',
'giveaway.embed.ended': 'Ended',
'giveaway.embed.endedWinners': 'Winners: {winners}',
'giveaway.embed.endedNoWinners': 'No valid entrants.',
'giveaway.embed.endsAt': 'Ends {time}',
'giveaway.button.join': 'Join',
'giveaway.requirement.role': 'Role: {role}',
'giveaway.requirement.level': 'Minimum level: {level}',
'giveaway.requirement.memberDays': 'Member for at least {days} days',
'giveaway.started': 'Giveaway started (ID: `{id}`).',
'giveaway.ended': 'Giveaway ended.',
'giveaway.rerolled': 'New winners: {winners}',
'giveaway.deleted': 'Giveaway deleted.',
'giveaway.paused': 'Giveaway paused.',
'giveaway.resumed': 'Giveaway resumed.',
'giveaway.joined': 'You entered the giveaway!',
'giveaway.list.header': '**Active giveaways**',
'giveaway.list.empty': 'No active giveaways.',
'giveaway.list.entrants': 'entrants',
'giveaway.dm.won': 'Congratulations! You won **{prize}** on **{guild}**!',
'giveaway.error.not_found': 'Giveaway not found or already ended.',
'giveaway.error.already_ended': 'This giveaway has already ended.',
'giveaway.error.not_ended': 'Giveaway has not ended yet.',
'giveaway.error.paused': 'This giveaway is paused.',
'giveaway.error.already_joined': 'You already entered.',
'giveaway.error.member_not_found': 'Member not found.',
'giveaway.error.missing_role': 'You are missing the required role.',
'giveaway.error.insufficient_level': 'Your level is too low.',
'giveaway.error.insufficient_member_days': 'You have not been on the server long enough.',
'giveaway.error.invalid_duration': 'Invalid duration. Use e.g. 1h, 2d.',
'giveaway.error.duration_too_short': 'Duration must be at least 1 minute.',
'giveaway.error.invalid_channel': 'Invalid channel.',
'ticket.panel.defaultDescription': 'Select a category to open a ticket.',
'ticket.panel.created': 'Ticket panel created.',
'ticket.category.created': 'Category **{name}** created.',
'ticket.created': 'Your ticket was created: {channel}',
'ticket.opened': '{user} opened a ticket.',
'ticket.subjectLine': 'Subject: {subject}',
'ticket.claimed': '{user} claimed this ticket.',
'ticket.userAdded': '{user} was added to the ticket.',
'ticket.userRemoved': '{user} was removed from the ticket.',
'ticket.renamed': 'Ticket renamed to **{name}**.',
'ticket.prioritySet': 'Priority set to **{priority}**',
'ticket.closed': 'Ticket closed by {user}.',
'ticket.closedWithReason': 'Ticket closed by {user}.\nReason: {reason}',
'ticket.close.success': 'Ticket closed.',
'ticket.claim.success': 'Ticket claimed.',
'ticket.add.success': 'Added {user}.',
'ticket.remove.success': 'Removed {user}.',
'ticket.rename.success': 'Ticket renamed to **{name}**.',
'ticket.priority.success': 'Priority: **{priority}**',
'ticket.transcriptDm': 'Here is your ticket transcript.',
'ticket.ratePrompt': 'How satisfied were you with support? (15)',
'ticket.rated': 'Thanks for your rating: {rating}/5',
'ticket.autoCloseReason': 'Auto-closed due to inactivity',
'ticket.modal.subject': 'Subject',
'ticket.log.closed': 'Ticket closed',
'ticket.log.opener': 'Opener',
'ticket.log.closedBy': 'Closed by',
'ticket.priority.LOW': 'Low',
'ticket.priority.NORMAL': 'Normal',
'ticket.priority.HIGH': 'High',
'ticket.priority.URGENT': 'Urgent',
'ticket.error.disabled': 'The ticket system is disabled.',
'ticket.error.category_not_found': 'Category not found.',
'ticket.error.no_categories': 'No ticket categories configured.',
'ticket.error.not_in_ticket': 'This command can only be used inside a ticket channel.',
'ticket.error.not_staff': 'You do not have permission for this ticket action.',
'ticket.error.already_open': 'You already have an open ticket.',
'ticket.error.closed': 'This ticket is already closed.',
'ticket.error.already_claimed': 'You already claimed this ticket.',
'ticket.error.cannot_remove_opener': 'The ticket opener cannot be removed.',
'ticket.error.no_channel': 'Ticket channel not found.',
'ticket.error.invalid_name': 'Invalid name.',
'ticket.error.invalid_channel': 'Invalid channel.',
'ticket.error.no_parent_channel': 'No parent channel configured for thread tickets.',
'ticket.error.invalid_parent_channel': 'The configured parent channel is invalid.',
'ticket.error.not_found': 'Ticket not found.',
'ticket.error.already_rated': 'You already rated this ticket.',
'birthdays.error.invalidDate': 'Invalid date. Check day, month, and optional year.',
'birthdays.error.invalidTimezone': 'Invalid timezone. Use e.g. Europe/Berlin.',
'birthdays.setup.done': 'Birthday module configured.',
'birthdays.set.success': 'Birthday saved: {day}/{month}/{year}',
'birthdays.remove.none': 'You have not set a birthday.',
'birthdays.remove.success': 'Your birthday was removed.',
'birthdays.list.empty': 'No birthdays registered yet.',
'birthdays.list.header': '**Birthdays (sorted by next date):**',
'birthdays.next.header': '**Upcoming birthdays:**',
'birthdays.list.line': '{rank}. {user} — {date} (in {days} d)',
'stats.notConfigured': 'Server statistics are not configured yet. Use `/stats setup`.',
'stats.setup.done': 'Stats channels configured and updated.',
'stats.refresh.done': 'Stats channels have been refreshed.',
'stats.error.invalidSetup': 'Invalid input for stats configuration.',
'invites.view.empty': '{user} has no invite statistics yet.',
'invites.view.result':
'**Invite stats for {user}**\nJoins: {joins} | Leaves: {leaves} | Fakes: {fakes} | Net: {net}',
'invites.leaderboard.empty': 'No invite data available yet.',
'invites.leaderboard.header': '**Top inviters:**',
'invites.leaderboard.line':
'{rank}. {user} — Joins: {joins}, Leaves: {leaves}, Fakes: {fakes}, Net: {net}',
'tempvoice.error.disabled': 'Temp voice is disabled on this server.',
'tempvoice.error.invalidCategory': 'Invalid category.',
'tempvoice.error.notOwner': 'You are not the owner of this temp voice channel.',
'tempvoice.error.notInChannel': 'You must be in a temp voice channel.',
'tempvoice.error.notFound': 'Temp voice channel not found.',
'tempvoice.error.invalidName': 'Invalid channel name.',
'tempvoice.error.invalidLimit': 'Limit must be between 0 and 99.',
'tempvoice.setup.done': 'Temp voice configured.',
'tempvoice.rename.success': 'Channel renamed to **{name}**.',
'tempvoice.limit.success': 'User limit set to **{limit}** (0 = unlimited).',
'tempvoice.lock.success': 'Channel locked (Connect denied for @everyone).',
'tempvoice.hide.success': 'Channel hidden (ViewChannel denied for @everyone).',
'tempvoice.kick.self': 'You cannot kick yourself.',
'tempvoice.kick.notInChannel': 'That user is not in your channel.',
'tempvoice.kick.success': 'Removed {user} from the channel.',
'tempvoice.transfer.self': 'You cannot transfer the channel to yourself.',
'tempvoice.transfer.notInChannel': 'The new owner must be in your channel.',
'tempvoice.transfer.success': 'Channel transferred to {user}.',
'tempvoice.claim.ownerPresent': 'The owner is still in the channel.',
'tempvoice.claim.success': 'You are now the owner of this channel.',
'tempvoice.panel.title': 'Temp Voice Control',
'tempvoice.panel.description': 'Manage **{channel}** with the buttons below or `/voice` commands.',
'tempvoice.panel.sent': 'Control panel posted in the voice channel chat.',
'tempvoice.button.rename': 'Rename',
'tempvoice.button.limit': 'Limit',
'tempvoice.button.lock': 'Lock',
'tempvoice.button.hide': 'Hide',
'tempvoice.button.kick': 'Kick',
'tempvoice.button.transfer': 'Transfer',
'tempvoice.button.claim': 'Claim',
'selfroles.panel.defaultDescription': 'Choose your roles below.',
'selfroles.panel.rolesField': 'Roles',
'selfroles.panel.temporaryFooter': 'Roles are temporary and expire automatically.',
'selfroles.panel.created': 'Self-role panel created (ID: `{id}`).',
'selfroles.panel.updated': 'Self-role panel updated (ID: `{id}`).',
'selfroles.panel.deleted': 'Self-role panel deleted.',
'selfroles.role.added': 'Role added.',
'selfroles.role.removed': 'Role removed.',
'selfroles.role.verified': 'Role granted.',
'selfroles.role.unchanged': 'You already have this role.',
'selfroles.error.not_found': 'Self-role panel not found.',
'selfroles.error.invalid_roles': 'Invalid roles format. Use roleId:label:emoji.',
'selfroles.error.too_many_roles': 'Maximum 25 roles per panel.',
'selfroles.error.invalid_channel': 'Invalid channel.',
'selfroles.error.invalid_duration': 'Invalid duration. Use e.g. 1h, 2d.',
'selfroles.error.duration_too_short': 'Duration must be at least 1 minute.',
'selfroles.error.duration_required': 'Temporary panels require a duration.',
'selfroles.error.reaction_emoji_required': 'Reaction mode requires emojis for all roles.',
'selfroles.error.member_not_found': 'Member not found.',
'selfroles.error.role_not_in_panel': 'This role is not part of this panel.',
'selfroles.error.role_not_found': 'Role not found.',
'selfroles.error.role_hierarchy': 'The role is above the bot\'s highest role.',
'selfroles.error.role_managed': 'Managed roles cannot be assigned.',
'selfroles.error.bot_missing_permissions': 'The bot lacks Manage Roles permission.',
'tag.created': 'Tag `{name}` created.',
'tag.updated': 'Tag `{name}` updated.',
'tag.deleted': 'Tag deleted.',
'tag.list.header': '**Tags**',
'tag.list.empty': 'No tags configured.',
'tag.list.uses': 'uses',
'tag.info.name': 'Name: `{name}`',
'tag.info.type': 'Type: {type}',
'tag.info.uses': 'Uses: {count}',
'tag.info.trigger': 'Trigger: `{word}`',
'tag.info.noTrigger': 'No auto-responder.',
'tag.info.roles': 'Roles: {roles}',
'tag.info.allRoles': 'Roles: all',
'tag.info.channels': 'Channels: {channels}',
'tag.info.allChannels': 'Channels: all',
'tag.error.not_found': 'Tag not found.',
'tag.error.not_allowed': 'You are not allowed to use this tag here.',
'tag.error.invalid_name': 'Invalid tag name (132 chars, a-z, 0-9, _, -).',
'tag.error.content_required': 'Text tags require content.',
'tag.error.embed_required': 'Embed tags require a title or description.',
'tag.error.premium_limit': 'Tag limit reached for this premium tier.',
'tag.error.premium_feature': 'Tags are disabled for this premium tier.',
'guildbackup.description': 'Create and restore server backups',
'guildbackup.created': 'Created backup **{name}** (ID: `{id}`).',
'guildbackup.deleted': 'Deleted backup `{id}`.',
'guildbackup.list.header': '**Server backups**',
'guildbackup.list.empty': 'No backups found.',
'guildbackup.list.line': '• `{id}` — **{name}** ({date})',
'guildbackup.info.id': 'ID: `{id}`',
'guildbackup.info.name': 'Name: **{name}**',
'guildbackup.info.created': 'Created: {date}',
'guildbackup.info.createdBy': 'Created by: <@{userId}>',
'guildbackup.info.roles': 'Roles: {count}',
'guildbackup.info.channels': 'Channels: {count}',
'guildbackup.info.limitations':
'Note: Backups do not include messages. Restore cannot preserve IDs; missing roles/channels are added by name, existing channels are not deleted.',
'guildbackup.confirm.delete': 'Really delete backup `{id}`?',
'guildbackup.confirm.restore.step1':
'Restore backup `{id}`? Missing roles/channels will be added and overwrites applied best-effort. Existing channels will **not** be deleted.',
'guildbackup.confirm.restore.step2':
'**Final confirmation:** Restore backup `{id}` now? This cannot be undone.',
'guildbackup.confirm.button.confirm': 'Confirm',
'guildbackup.confirm.button.cancel': 'Cancel',
'guildbackup.confirm.expired': 'This confirmation has expired.',
'guildbackup.confirm.unauthorized': 'Only the user who started this action can confirm.',
'guildbackup.confirm.cancelled': 'Cancelled.',
'guildbackup.restore.done':
'Restored backup `{id}`. Roles: {rolesCreated} created, {rolesSkipped} skipped. Channels: {channelsCreated} created, {channelsUpdated} updated. Overwrites: {overwritesApplied}.',
'guildbackup.restore.queued':
'Large backup `{id}` — restore queued and will run in the background.',
'guildbackup.error.not_found': 'Backup not found.',
'guildbackup.error.invalid_payload': 'Backup data is invalid or outdated.',
'guildbackup.error.invalid_name': 'Invalid backup name (1100 characters).',
'guildbackup.error.owner_only': 'Only the server owner can restore backups.',
'guildbackup.error.bot_missing_permissions':
'The bot is missing Manage Roles or Manage Channels for a restore.',
'guildbackup.error.premium_limit': 'Backup limit reached for this premium tier.',
'guildbackup.error.premium_feature': 'Backups are disabled for this premium tier.',
'feeds.added': 'Added feed `{id}` ({type}).',
'feeds.removed': 'Removed feed `{id}`.',
'feeds.list.header': '**Social Feeds**',
'feeds.list.empty': 'No feeds configured.',
'feeds.list.line': '`{id}` — {type}: {source} → {channel}',
'feeds.test.posted': 'Test successful: sent {count} message(s).',
'feeds.test.noItems': 'No new items found.',
'feeds.test.failed': 'Fetch failed: {reason}',
'feeds.test.skipped': 'Feed test skipped.',
'feeds.test.twitchSkipped':
'Twitch feed skipped: TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET are missing in the bot configuration.',
'feeds.error.not_found': 'Feed not found.',
'feeds.error.premium_limit': 'Feed limit reached for this premium tier.',
'feeds.error.premium_feature': 'Feeds are disabled for this premium tier.',
'scheduler.created': 'Scheduled message created (ID: `{id}`). {timing}',
'scheduler.created.at': 'Sends at: {time}',
'scheduler.created.cron': 'Repeats: `{cron}`',
'scheduler.deleted': 'Scheduled message deleted.',
'scheduler.announce.sent': 'Announcement sent.',
'scheduler.list.header': '**Scheduled messages**',
'scheduler.list.empty': 'No scheduled messages.',
'scheduler.list.line': '`{id}` — {channel} — {timing} — {preview}',
'scheduler.list.cron': 'Cron `{cron}`',
'scheduler.list.embedPreview': '[Embed]',
'scheduler.list.componentsPreview': '[Components V2]',
'scheduler.error.missing_schedule_time': 'Provide `when` or `cron`.',
'scheduler.error.invalid_when': 'Invalid time. Use e.g. 1h, 2d, or an ISO date.',
'scheduler.error.invalid_cron': 'Invalid cron expression (56 fields).',
'scheduler.error.invalid_channel': 'Invalid channel.',
'scheduler.error.channel_unavailable': 'The bot cannot send messages in that channel.',
'scheduler.error.not_found': 'Scheduled message not found.',
'scheduler.error.content_required': 'Content or embed title/description is required.',
'componentsV2.error.notFound': 'This component message is no longer available.',
'componentsV2.error.actionMissing': 'No action is configured for this interaction.',
'componentsV2.error.botMissingManageRoles': 'The bot lacks Manage Roles permission.',
'componentsV2.error.roleNotFound': 'Role not found.',
'componentsV2.error.roleManaged': 'Managed roles cannot be assigned.',
'componentsV2.error.roleHierarchy': 'The role is above the bot\'s highest role.',
'componentsV2.error.noRolesSelected': 'No roles selected.',
'componentsV2.role.assigned': 'Role assigned.',
'componentsV2.role.removed': 'Role removed.',
'componentsV2.role.assignedSelected': 'Selected roles assigned.',
'utility.embed.components.sent': 'Components V2 message sent.',
'utility.embed.components.dashboardHint':
'For the full builder including interactions: {url}',
'utility.embed.components.missingText': 'Text is required.',
'tag.error.components_required': 'Components V2 tags require components.'
};
const locales: Record<Locale, Dictionary> = { de, en };
export function t(locale: Locale, key: string): string {
return locales[locale][key] ?? key;
}
export function tf(locale: Locale, key: string, vars: Record<string, string | number>): string {
const base = t(locale, key);
return Object.entries(vars).reduce(
(acc, [name, value]) => acc.replaceAll(`{${name}}`, String(value)),
base
);
}