feat: configurable trip reminders, admin full access, and enhanced audit logging

- Add configurable trip reminder days (1, 3, 9 or custom up to 30) settable by trip owner
- Grant administrators full access to edit, archive, delete, view and list all trips
- Show trip owner email in audit logs and docker logs when admin edits/deletes another user's trip
- Show target user email in audit logs when admin edits or deletes a user account
- Use email instead of username in all notifications (Discord/Slack/email) to avoid ambiguity
- Grey out notification event toggles when no SMTP/webhook is configured
- Grey out trip reminder selector when notifications are disabled
- Skip local admin account creation when OIDC_ONLY=true with OIDC configured
- Conditional scheduler logging: show disabled reason or active reminder count
- Log per-owner reminder creation/update in docker logs
- Demote 401/403 HTTP errors to DEBUG log level to reduce noise
- Hide edit/archive/delete buttons for non-owner invited users on trip cards
- Fix literal "0" rendering on trip cards from SQLite numeric is_owner field
- Add missing translation keys across all 14 language files

Made-with: Cursor
This commit is contained in:
Andrei Brebene
2026-03-31 16:42:37 +03:00
parent 9b2f083e4b
commit 7522f396e7
31 changed files with 378 additions and 83 deletions
+9
View File
@@ -30,6 +30,13 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'كلمة المرور',
'common.saving': 'جارٍ الحفظ...',
'common.saved': 'تم الحفظ',
'trips.reminder': 'تذكير',
'trips.reminderNone': 'بدون',
'trips.reminderDay': 'يوم',
'trips.reminderDays': 'أيام',
'trips.reminderCustom': 'مخصص',
'trips.reminderDaysBefore': 'أيام قبل المغادرة',
'trips.reminderDisabledHint': 'تذكيرات الرحلة معطلة. قم بتفعيلها من الإدارة > الإعدادات > الإشعارات.',
'common.update': 'تحديث',
'common.change': 'تغيير',
'common.uploading': 'جارٍ الرفع...',
@@ -165,6 +172,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'أحداث الإشعارات',
'admin.notifications.eventsHint': 'اختر الأحداث التي تُفعّل الإشعارات لجميع المستخدمين.',
'admin.notifications.configureFirst': 'قم بتكوين إعدادات SMTP أو Webhook أدناه أولاً، ثم قم بتفعيل الأحداث.',
'admin.notifications.save': 'حفظ إعدادات الإشعارات',
'admin.notifications.saved': 'تم حفظ إعدادات الإشعارات',
'admin.notifications.testWebhook': 'إرسال webhook تجريبي',
@@ -173,6 +181,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'البريد والإشعارات',
'admin.smtp.hint': 'تكوين SMTP لإرسال إشعارات البريد الإلكتروني.',
'admin.smtp.testButton': 'إرسال بريد تجريبي',
'admin.webhook.hint': 'إرسال الإشعارات إلى webhook خارجي (Discord، Slack، إلخ).',
'admin.smtp.testSuccess': 'تم إرسال البريد التجريبي بنجاح',
'admin.smtp.testFailed': 'فشل إرسال البريد التجريبي',
'dayplan.icsTooltip': 'تصدير التقويم (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const br: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'Senha',
'common.saving': 'Salvando...',
'common.saved': 'Salvo',
'trips.reminder': 'Lembrete',
'trips.reminderNone': 'Nenhum',
'trips.reminderDay': 'dia',
'trips.reminderDays': 'dias',
'trips.reminderCustom': 'Personalizado',
'trips.reminderDaysBefore': 'dias antes da partida',
'trips.reminderDisabledHint': 'Os lembretes de viagem estão desativados. Ative-os em Admin > Configurações > Notificações.',
'common.update': 'Atualizar',
'common.change': 'Alterar',
'common.uploading': 'Enviando…',
@@ -160,6 +167,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Eventos de notificação',
'admin.notifications.eventsHint': 'Escolha quais eventos acionam notificações para todos os usuários.',
'admin.notifications.configureFirst': 'Configure primeiro as configurações SMTP ou webhook abaixo, depois ative os eventos.',
'admin.notifications.save': 'Salvar configurações de notificação',
'admin.notifications.saved': 'Configurações de notificação salvas',
'admin.notifications.testWebhook': 'Enviar webhook de teste',
@@ -168,6 +176,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'E-mail e notificações',
'admin.smtp.hint': 'Configuração SMTP para envio de notificações por e-mail.',
'admin.smtp.testButton': 'Enviar e-mail de teste',
'admin.webhook.hint': 'Enviar notificações para um webhook externo (Discord, Slack, etc.).',
'admin.smtp.testSuccess': 'E-mail de teste enviado com sucesso',
'admin.smtp.testFailed': 'Falha ao enviar e-mail de teste',
'dayplan.icsTooltip': 'Exportar calendário (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'Heslo',
'common.saving': 'Ukládání...',
'common.saved': 'Uloženo',
'trips.reminder': 'Připomínka',
'trips.reminderNone': 'Žádná',
'trips.reminderDay': 'den',
'trips.reminderDays': 'dní',
'trips.reminderCustom': 'Vlastní',
'trips.reminderDaysBefore': 'dní před odjezdem',
'trips.reminderDisabledHint': 'Připomínky výletů jsou zakázány. Povolte je v Správa > Nastavení > Oznámení.',
'common.update': 'Aktualizovat',
'common.change': 'Změnit',
'common.uploading': 'Nahrávání…',
@@ -246,6 +253,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Události oznámení',
'admin.notifications.eventsHint': 'Vyberte, které události spouštějí oznámení pro všechny uživatele.',
'admin.notifications.configureFirst': 'Nejprve nakonfigurujte nastavení SMTP nebo webhooku níže, poté povolte události.',
'admin.notifications.save': 'Uložit nastavení oznámení',
'admin.notifications.saved': 'Nastavení oznámení uloženo',
'admin.notifications.testWebhook': 'Odeslat testovací webhook',
@@ -254,6 +262,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'E-mail a oznámení',
'admin.smtp.hint': 'Konfigurace SMTP pro odesílání e-mailových oznámení.',
'admin.smtp.testButton': 'Odeslat testovací e-mail',
'admin.webhook.hint': 'Odesílat oznámení na externí webhook (Discord, Slack atd.).',
'admin.smtp.testSuccess': 'Testovací e-mail byl úspěšně odeslán',
'admin.smtp.testFailed': 'Odeslání testovacího e-mailu se nezdařilo',
'dayplan.icsTooltip': 'Exportovat kalendář (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'Passwort',
'common.saving': 'Speichern...',
'common.saved': 'Gespeichert',
'trips.reminder': 'Erinnerung',
'trips.reminderNone': 'Keine',
'trips.reminderDay': 'Tag',
'trips.reminderDays': 'Tage',
'trips.reminderCustom': 'Benutzerdefiniert',
'trips.reminderDaysBefore': 'Tage vor Abreise',
'trips.reminderDisabledHint': 'Reiseerinnerungen sind deaktiviert. Aktivieren Sie sie unter Admin > Einstellungen > Benachrichtigungen.',
'common.update': 'Aktualisieren',
'common.change': 'Ändern',
'common.uploading': 'Hochladen…',
@@ -160,6 +167,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Benachrichtigungsereignisse',
'admin.notifications.eventsHint': 'Wähle, welche Ereignisse Benachrichtigungen für alle Benutzer auslösen.',
'admin.notifications.configureFirst': 'Konfiguriere zuerst die SMTP- oder Webhook-Einstellungen unten, dann aktiviere die Events.',
'admin.notifications.save': 'Benachrichtigungseinstellungen speichern',
'admin.notifications.saved': 'Benachrichtigungseinstellungen gespeichert',
'admin.notifications.testWebhook': 'Test-Webhook senden',
@@ -168,6 +176,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'E-Mail & Benachrichtigungen',
'admin.smtp.hint': 'SMTP-Konfiguration zum Versenden von E-Mail-Benachrichtigungen.',
'admin.smtp.testButton': 'Test-E-Mail senden',
'admin.webhook.hint': 'Benachrichtigungen an einen externen Webhook senden (Discord, Slack usw.).',
'admin.smtp.testSuccess': 'Test-E-Mail erfolgreich gesendet',
'admin.smtp.testFailed': 'Test-E-Mail fehlgeschlagen',
'dayplan.icsTooltip': 'Kalender exportieren (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const en: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'Password',
'common.saving': 'Saving...',
'common.saved': 'Saved',
'trips.reminder': 'Reminder',
'trips.reminderNone': 'None',
'trips.reminderDay': 'day',
'trips.reminderDays': 'days',
'trips.reminderCustom': 'Custom',
'trips.reminderDaysBefore': 'days before departure',
'trips.reminderDisabledHint': 'Trip reminders are disabled. Enable them in Admin > Settings > Notifications.',
'common.update': 'Update',
'common.change': 'Change',
'common.uploading': 'Uploading…',
@@ -157,6 +164,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Notification Events',
'admin.notifications.eventsHint': 'Choose which events trigger notifications for all users.',
'admin.notifications.configureFirst': 'Configure the SMTP or webhook settings below first, then enable events.',
'admin.notifications.save': 'Save notification settings',
'admin.notifications.saved': 'Notification settings saved',
'admin.notifications.testWebhook': 'Send test webhook',
@@ -165,6 +173,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'Email & Notifications',
'admin.smtp.hint': 'SMTP configuration for sending email notifications.',
'admin.smtp.testButton': 'Send test email',
'admin.webhook.hint': 'Send notifications to an external webhook (Discord, Slack, etc.).',
'admin.smtp.testSuccess': 'Test email sent successfully',
'admin.smtp.testFailed': 'Test email failed',
'settings.notificationsDisabled': 'Notifications are not configured. Ask an admin to enable email or webhook notifications.',
+9
View File
@@ -26,6 +26,13 @@ const es: Record<string, string> = {
'common.password': 'Contraseña',
'common.saving': 'Guardando...',
'common.saved': 'Guardado',
'trips.reminder': 'Recordatorio',
'trips.reminderNone': 'Ninguno',
'trips.reminderDay': 'día',
'trips.reminderDays': 'días',
'trips.reminderCustom': 'Personalizado',
'trips.reminderDaysBefore': 'días antes de la salida',
'trips.reminderDisabledHint': 'Los recordatorios de viaje están desactivados. Actívalos en Admin > Configuración > Notificaciones.',
'common.update': 'Actualizar',
'common.change': 'Cambiar',
'common.uploading': 'Subiendo…',
@@ -161,6 +168,7 @@ const es: Record<string, string> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Eventos de notificación',
'admin.notifications.eventsHint': 'Elige qué eventos activan notificaciones para todos los usuarios.',
'admin.notifications.configureFirst': 'Configura primero los ajustes SMTP o webhook a continuación, luego activa los eventos.',
'admin.notifications.save': 'Guardar configuración de notificaciones',
'admin.notifications.saved': 'Configuración de notificaciones guardada',
'admin.notifications.testWebhook': 'Enviar webhook de prueba',
@@ -169,6 +177,7 @@ const es: Record<string, string> = {
'admin.smtp.title': 'Correo y notificaciones',
'admin.smtp.hint': 'Configuración SMTP para el envío de notificaciones por correo.',
'admin.smtp.testButton': 'Enviar correo de prueba',
'admin.webhook.hint': 'Enviar notificaciones a un webhook externo (Discord, Slack, etc.).',
'admin.smtp.testSuccess': 'Correo de prueba enviado correctamente',
'admin.smtp.testFailed': 'Error al enviar correo de prueba',
'dayplan.icsTooltip': 'Exportar calendario (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const fr: Record<string, string> = {
'common.password': 'Mot de passe',
'common.saving': 'Enregistrement…',
'common.saved': 'Enregistré',
'trips.reminder': 'Rappel',
'trips.reminderNone': 'Aucun',
'trips.reminderDay': 'jour',
'trips.reminderDays': 'jours',
'trips.reminderCustom': 'Personnalisé',
'trips.reminderDaysBefore': 'jours avant le départ',
'trips.reminderDisabledHint': 'Les rappels de voyage sont désactivés. Activez-les dans Admin > Paramètres > Notifications.',
'common.update': 'Mettre à jour',
'common.change': 'Modifier',
'common.uploading': 'Import en cours…',
@@ -160,6 +167,7 @@ const fr: Record<string, string> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Événements de notification',
'admin.notifications.eventsHint': 'Choisissez quels événements déclenchent des notifications pour tous les utilisateurs.',
'admin.notifications.configureFirst': 'Configurez d\'abord les paramètres SMTP ou webhook ci-dessous, puis activez les événements.',
'admin.notifications.save': 'Enregistrer les paramètres de notification',
'admin.notifications.saved': 'Paramètres de notification enregistrés',
'admin.notifications.testWebhook': 'Envoyer un webhook de test',
@@ -168,6 +176,7 @@ const fr: Record<string, string> = {
'admin.smtp.title': 'E-mail et notifications',
'admin.smtp.hint': 'Configuration SMTP pour l\'envoi des notifications par e-mail.',
'admin.smtp.testButton': 'Envoyer un e-mail de test',
'admin.webhook.hint': 'Envoyer des notifications vers un webhook externe (Discord, Slack, etc.).',
'admin.smtp.testSuccess': 'E-mail de test envoyé avec succès',
'admin.smtp.testFailed': 'Échec de l\'e-mail de test',
'dayplan.icsTooltip': 'Exporter le calendrier (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'Jelszó',
'common.saving': 'Mentés...',
'common.saved': 'Mentve',
'trips.reminder': 'Emlékeztető',
'trips.reminderNone': 'Nincs',
'trips.reminderDay': 'nap',
'trips.reminderDays': 'nap',
'trips.reminderCustom': 'Egyéni',
'trips.reminderDaysBefore': 'nappal indulás előtt',
'trips.reminderDisabledHint': 'Az utazási emlékeztetők ki vannak kapcsolva. Kapcsold be az Admin > Beállítások > Értesítések menüben.',
'common.update': 'Frissítés',
'common.change': 'Módosítás',
'common.uploading': 'Feltöltés…',
@@ -245,6 +252,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Értesítési események',
'admin.notifications.eventsHint': 'Válaszd ki, mely események indítsanak értesítéseket minden felhasználó számára.',
'admin.notifications.configureFirst': 'Először konfiguráld az SMTP vagy webhook beállításokat lent, majd engedélyezd az eseményeket.',
'admin.notifications.save': 'Értesítési beállítások mentése',
'admin.notifications.saved': 'Értesítési beállítások mentve',
'admin.notifications.testWebhook': 'Teszt webhook küldése',
@@ -253,6 +261,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'E-mail és értesítések',
'admin.smtp.hint': 'SMTP konfiguráció e-mail értesítések küldéséhez.',
'admin.smtp.testButton': 'Teszt e-mail küldése',
'admin.webhook.hint': 'Értesítések küldése külső webhookra (Discord, Slack stb.).',
'admin.smtp.testSuccess': 'Teszt e-mail sikeresen elküldve',
'admin.smtp.testFailed': 'Teszt e-mail küldése sikertelen',
'dayplan.icsTooltip': 'Naptár exportálása (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const it: Record<string, string | { name: string; category: string }[]> = {
'common.password': 'Password',
'common.saving': 'Salvataggio...',
'common.saved': 'Salvato',
'trips.reminder': 'Promemoria',
'trips.reminderNone': 'Nessuno',
'trips.reminderDay': 'giorno',
'trips.reminderDays': 'giorni',
'trips.reminderCustom': 'Personalizzato',
'trips.reminderDaysBefore': 'giorni prima della partenza',
'trips.reminderDisabledHint': 'I promemoria dei viaggi sono disabilitati. Abilitali in Admin > Impostazioni > Notifiche.',
'common.update': 'Aggiorna',
'common.change': 'Cambia',
'common.uploading': 'Caricamento…',
@@ -245,6 +252,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Eventi di notifica',
'admin.notifications.eventsHint': 'Scegli quali eventi attivano le notifiche per tutti gli utenti.',
'admin.notifications.configureFirst': 'Configura prima le impostazioni SMTP o webhook qui sotto, poi abilita gli eventi.',
'admin.notifications.save': 'Salva impostazioni notifiche',
'admin.notifications.saved': 'Impostazioni notifiche salvate',
'admin.notifications.testWebhook': 'Invia webhook di test',
@@ -253,6 +261,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
'admin.smtp.title': 'Email e notifiche',
'admin.smtp.hint': 'Configurazione SMTP per l\'invio delle notifiche via e-mail.',
'admin.smtp.testButton': 'Invia email di prova',
'admin.webhook.hint': 'Invia notifiche a un webhook esterno (Discord, Slack, ecc.).',
'admin.smtp.testSuccess': 'Email di prova inviata con successo',
'admin.smtp.testFailed': 'Invio email di prova fallito',
'dayplan.icsTooltip': 'Esporta calendario (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const nl: Record<string, string> = {
'common.password': 'Wachtwoord',
'common.saving': 'Opslaan...',
'common.saved': 'Opgeslagen',
'trips.reminder': 'Herinnering',
'trips.reminderNone': 'Geen',
'trips.reminderDay': 'dag',
'trips.reminderDays': 'dagen',
'trips.reminderCustom': 'Aangepast',
'trips.reminderDaysBefore': 'dagen voor vertrek',
'trips.reminderDisabledHint': 'Reisherinneringen zijn uitgeschakeld. Schakel ze in via Admin > Instellingen > Meldingen.',
'common.update': 'Bijwerken',
'common.change': 'Wijzigen',
'common.uploading': 'Uploaden…',
@@ -160,6 +167,7 @@ const nl: Record<string, string> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'Meldingsgebeurtenissen',
'admin.notifications.eventsHint': 'Kies welke gebeurtenissen meldingen activeren voor alle gebruikers.',
'admin.notifications.configureFirst': 'Configureer eerst de SMTP- of webhook-instellingen hieronder en schakel dan de events in.',
'admin.notifications.save': 'Meldingsinstellingen opslaan',
'admin.notifications.saved': 'Meldingsinstellingen opgeslagen',
'admin.notifications.testWebhook': 'Testwebhook verzenden',
@@ -168,6 +176,7 @@ const nl: Record<string, string> = {
'admin.smtp.title': 'E-mail en meldingen',
'admin.smtp.hint': 'SMTP-configuratie voor het verzenden van e-mailmeldingen.',
'admin.smtp.testButton': 'Test-e-mail verzenden',
'admin.webhook.hint': 'Meldingen verzenden naar een externe webhook (Discord, Slack, enz.).',
'admin.smtp.testSuccess': 'Test-e-mail succesvol verzonden',
'admin.smtp.testFailed': 'Test-e-mail mislukt',
'dayplan.icsTooltip': 'Kalender exporteren (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const ru: Record<string, string> = {
'common.password': 'Пароль',
'common.saving': 'Сохранение...',
'common.saved': 'Сохранено',
'trips.reminder': 'Напоминание',
'trips.reminderNone': 'Нет',
'trips.reminderDay': 'день',
'trips.reminderDays': 'дней',
'trips.reminderCustom': 'Другое',
'trips.reminderDaysBefore': 'дней до отъезда',
'trips.reminderDisabledHint': 'Напоминания о поездках отключены. Включите их в Админ > Настройки > Уведомления.',
'common.update': 'Обновить',
'common.change': 'Изменить',
'common.uploading': 'Загрузка…',
@@ -160,6 +167,7 @@ const ru: Record<string, string> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': 'События уведомлений',
'admin.notifications.eventsHint': 'Выберите, какие события вызывают уведомления для всех пользователей.',
'admin.notifications.configureFirst': 'Сначала настройте SMTP или webhook ниже, затем включите события.',
'admin.notifications.save': 'Сохранить настройки уведомлений',
'admin.notifications.saved': 'Настройки уведомлений сохранены',
'admin.notifications.testWebhook': 'Отправить тестовый вебхук',
@@ -168,6 +176,7 @@ const ru: Record<string, string> = {
'admin.smtp.title': 'Почта и уведомления',
'admin.smtp.hint': 'Конфигурация SMTP для отправки уведомлений по электронной почте.',
'admin.smtp.testButton': 'Отправить тестовое письмо',
'admin.webhook.hint': 'Отправлять уведомления через внешний webhook (Discord, Slack и т.д.).',
'admin.smtp.testSuccess': 'Тестовое письмо успешно отправлено',
'admin.smtp.testFailed': 'Ошибка отправки тестового письма',
'dayplan.icsTooltip': 'Экспорт календаря (ICS)',
+9
View File
@@ -26,6 +26,13 @@ const zh: Record<string, string> = {
'common.password': '密码',
'common.saving': '保存中...',
'common.saved': '已保存',
'trips.reminder': '提醒',
'trips.reminderNone': '无',
'trips.reminderDay': '天',
'trips.reminderDays': '天',
'trips.reminderCustom': '自定义',
'trips.reminderDaysBefore': '天前提醒',
'trips.reminderDisabledHint': '旅行提醒已禁用。请在管理 > 设置 > 通知中启用。',
'common.update': '更新',
'common.change': '修改',
'common.uploading': '上传中…',
@@ -160,6 +167,7 @@ const zh: Record<string, string> = {
'admin.notifications.webhook': 'Webhook',
'admin.notifications.events': '通知事件',
'admin.notifications.eventsHint': '选择哪些事件为所有用户触发通知。',
'admin.notifications.configureFirst': '请先在下方配置 SMTP 或 Webhook,然后启用事件。',
'admin.notifications.save': '保存通知设置',
'admin.notifications.saved': '通知设置已保存',
'admin.notifications.testWebhook': '发送测试 Webhook',
@@ -168,6 +176,7 @@ const zh: Record<string, string> = {
'admin.smtp.title': '邮件与通知',
'admin.smtp.hint': '用于发送电子邮件通知的 SMTP 配置。',
'admin.smtp.testButton': '发送测试邮件',
'admin.webhook.hint': '向外部 Webhook 发送通知(Discord、Slack 等)。',
'admin.smtp.testSuccess': '测试邮件发送成功',
'admin.smtp.testFailed': '测试邮件发送失败',
'dayplan.icsTooltip': '导出日历 (ICS)',