# Plan: Mehrsprachige E-Mail-Templates für uLoad ## Executive Summary Implementierung eines Systems für deutsche und englische E-Mail-Templates mit automatischer Spracherkennung basierend auf Nutzerpräferenzen. --- ## 🎯 Ziele 1. **Primär**: Deutsche und englische E-Mail-Templates 2. **Automatische Sprachauswahl** basierend auf Nutzerpräferenz 3. **Fallback**: Englisch als Standard 4. **Erweiterbar**: Einfach neue Sprachen hinzufügen --- ## 📊 Ansätze (von einfach zu komplex) ### Ansatz 1: PocketBase Collection-Erweiterung (⭐ Empfohlen) **Konzept**: Nutzer-Sprachpräferenz in der users Collection speichern #### Implementation: 1. **Users Collection erweitern**: ```javascript // Neues Feld in users collection { name: "language", type: "select", values: ["de", "en"], default: "de" } ``` 2. **Custom E-Mail-Service erstellen**: ```typescript // src/lib/server/email-service.ts import { pb } from '$lib/pocketbase'; export async function sendLocalizedEmail( userId: string, type: 'verification' | 'reset' | 'change', data: any ) { // Nutzer-Sprache abrufen const user = await pb.collection('users').getOne(userId); const lang = user.language || 'de'; // Template basierend auf Sprache wählen const template = getTemplate(type, lang); // E-Mail senden await sendEmail({ to: user.email, subject: template.subject, html: template.body(data) }); } ``` **Vorteile**: - ✅ Einfache Implementation - ✅ Nutzer kann Sprache in Settings ändern - ✅ Funktioniert mit PocketBase **Nachteile**: - ❌ Erfordert Custom Email Handler - ❌ PocketBase Standard-Mails umgehen --- ### Ansatz 2: Browser-Sprache + IP-Geolocation **Konzept**: Automatische Spracherkennung ohne Nutzer-Input #### Implementation: ```typescript // src/routes/register/+page.server.ts export const actions = { register: async ({ request, getClientAddress }) => { const clientIp = getClientAddress(); const acceptLanguage = request.headers.get('accept-language'); // Sprache ermitteln const language = detectLanguage(acceptLanguage, clientIp); // Bei User-Erstellung speichern await pb.collection('users').create({ email, password, language // 'de' oder 'en' }); } }; function detectLanguage(acceptLanguage: string, ip: string): string { // 1. Browser-Sprache prüfen if (acceptLanguage?.startsWith('de')) return 'de'; if (acceptLanguage?.startsWith('en')) return 'en'; // 2. IP-Geolocation (optional) // const country = await getCountryFromIP(ip); // if (['DE', 'AT', 'CH'].includes(country)) return 'de'; // 3. Fallback return 'en'; } ``` **Vorteile**: - ✅ Automatisch ohne Nutzer-Aktion - ✅ Nutzerfreundlich **Nachteile**: - ❌ Nicht immer akkurat - ❌ Zusätzliche Geolocation-API nötig --- ### Ansatz 3: Custom PocketBase Extension (Advanced) **Konzept**: PocketBase mit Go erweitern für native Unterstützung #### Struktur: ```go // pb_hooks/email_localization.go package main import ( "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/core" ) func main() { app := pocketbase.New() app.OnRecordBeforeCreateRequest("users").Add(func(e *core.RecordCreateEvent) error { // Sprache aus Request Headers lang := e.HttpContext.Request().Header.Get("Accept-Language") e.Record.Set("language", parseLanguage(lang)) return nil }) app.OnMailerBeforeRecordVerificationSend().Add(func(e *core.MailerRecordEvent) error { user := e.Record lang := user.GetString("language") // Template basierend auf Sprache if lang == "de" { e.Message.Subject = "Bestätige deine E-Mail" e.Message.HTML = germanTemplate(e.Record, e.Token) } else { e.Message.Subject = "Verify your email" e.Message.HTML = englishTemplate(e.Record, e.Token) } return nil }) app.Start() } ``` **Vorteile**: - ✅ Native PocketBase Integration - ✅ Alle Standard-Features bleiben - ✅ Beste Performance **Nachteile**: - ❌ Komplexe Implementation - ❌ Go-Kenntnisse erforderlich - ❌ Custom PocketBase Build --- ## 🚀 Empfohlene Lösung: Hybrid-Ansatz ### Phase 1: Quick Win (1-2 Tage) 1. **User Collection erweitern**: ```sql ALTER TABLE users ADD COLUMN language TEXT DEFAULT 'de'; ``` 2. **Settings-Page Update**: ```svelte ``` 3. **Template-Struktur**: ``` docs/mail/templates/ ├── de/ │ ├── verification.html │ ├── reset.html │ └── change.html └── en/ ├── verification.html ├── reset.html └── change.html ``` ### Phase 2: Custom Email Handler (3-5 Tage) ```typescript // src/lib/server/email/index.ts import { readFileSync } from 'fs'; import { compile } from 'handlebars'; export class EmailService { private templates: Map = new Map(); constructor() { this.loadTemplates(); } private loadTemplates() { const languages = ['de', 'en']; const types = ['verification', 'reset', 'change']; for (const lang of languages) { for (const type of types) { const path = `./templates/${lang}/${type}.html`; const template = readFileSync(path, 'utf-8'); const compiled = compile(template); this.templates.set(`${lang}-${type}`, compiled); } } } async send(userId: string, type: string, data: any) { const user = await pb.collection('users').getOne(userId); const lang = user.language || 'de'; const template = this.templates.get(`${lang}-${type}`); if (!template) throw new Error('Template not found'); const html = template({ ...data, user, appUrl: PUBLIC_APP_URL, currentYear: new Date().getFullYear() }); await this.sendViaBrevo({ to: user.email, subject: this.getSubject(lang, type), html }); } private getSubject(lang: string, type: string): string { const subjects = { 'de-verification': 'Bestätige deine E-Mail für uLoad 🔗', 'en-verification': 'Verify your email for uLoad 🔗', 'de-reset': 'Passwort zurücksetzen für uLoad 🔐', 'en-reset': 'Reset your password for uLoad 🔐' // ... }; return subjects[`${lang}-${type}`]; } } ``` ### Phase 3: API Routes (1 Tag) ```typescript // src/routes/api/auth/verify/+server.ts import { EmailService } from '$lib/server/email'; export async function POST({ request }) { const { userId, token } = await request.json(); const emailService = new EmailService(); await emailService.send(userId, 'verification', { token }); return json({ success: true }); } ``` --- ## 🌍 Sprach-Detection Strategien ### 1. Bei Registrierung: ```typescript // Prioritäten: 1. URL-Parameter: ?lang=de 2. Browser Accept-Language Header 3. IP-Geolocation 4. Default: de (für DACH-Region) ``` ### 2. Implementation: ```typescript export function detectUserLanguage(request: Request): 'de' | 'en' { // 1. Check URL param const url = new URL(request.url); const urlLang = url.searchParams.get('lang'); if (urlLang === 'de' || urlLang === 'en') return urlLang; // 2. Check browser language const acceptLang = request.headers.get('accept-language') || ''; const browserLang = acceptLang.split(',')[0].split('-')[0].toLowerCase(); if (browserLang === 'de') return 'de'; if (browserLang === 'en') return 'en'; // 3. Check cookie (if user changed language before) const cookies = parseCookies(request.headers.get('cookie')); if (cookies.lang === 'de' || cookies.lang === 'en') return cookies.lang; // 4. Default return 'de'; // DACH-focused app } ``` --- ## 📁 Datei-Struktur ``` src/ ├── lib/ │ ├── server/ │ │ ├── email/ │ │ │ ├── index.ts # EmailService class │ │ │ ├── templates.ts # Template loader │ │ │ └── brevo.ts # Brevo API wrapper │ │ └── i18n/ │ │ ├── detector.ts # Language detection │ │ └── translations.ts # Email translations │ └── stores/ │ └── language.ts # Client-side language store ├── routes/ │ ├── api/ │ │ ├── user/language/+server.ts # Update language preference │ │ └── email/send/+server.ts # Custom email sender │ └── (app)/ │ └── settings/ │ └── +page.svelte # Language selector └── hooks.server.ts # Language detection on request ``` --- ## 🔄 Migration Plan ### Woche 1: - [ ] Users Collection um `language` Feld erweitern - [ ] Settings-Page mit Sprachauswahl - [ ] Deutsche & englische Templates erstellen ### Woche 2: - [ ] EmailService Klasse implementieren - [ ] API Routes für Custom Emails - [ ] Auto-Detection bei Registrierung ### Woche 3: - [ ] Testing mit verschiedenen Sprachen - [ ] Fallback-Mechanismen - [ ] Documentation --- ## 💡 Quick Start (Minimal Version) Für einen schnellen Start ohne große Änderungen: ### 1. Zwei PocketBase Instanzen: ```bash # Deutsche Version PB_LANG=de ./pocketbase serve --http=127.0.0.1:8090 # Englische Version PB_LANG=en ./pocketbase serve --http=127.0.0.1:8091 ``` ### 2. Nginx Proxy: ```nginx server { listen 80; server_name api.ulo.ad; # Route basierend auf Accept-Language if ($http_accept_language ~* "^de") { proxy_pass http://127.0.0.1:8090; } proxy_pass http://127.0.0.1:8091; # Default EN } ``` **Vorteil**: Keine Code-Änderungen nötig **Nachteil**: Doppelte Wartung --- ## 🎨 Template Management ### Option 1: Handlebars Templates ```handlebars

Willkommen {{user.name}}!

Bitte bestätige deine E-Mail:

Bestätigen ``` ### Option 2: React Email (Modern) ```tsx // emails/Verification.tsx export function VerificationEmail({ user, token, lang }) { const t = translations[lang]; return ( {t.welcome} {user.name}! {t.pleaseVerify} ); } ``` ### Option 3: JSON-basierte Templates ```json { "de": { "verification": { "subject": "Bestätige deine E-Mail", "heading": "Willkommen {name}!", "body": "Bitte bestätige deine E-Mail-Adresse.", "button": "E-Mail bestätigen" } }, "en": { "verification": { "subject": "Verify your email", "heading": "Welcome {name}!", "body": "Please verify your email address.", "button": "Verify Email" } } } ``` --- ## 🚦 Entscheidungsmatrix | Kriterium | Ansatz 1 | Ansatz 2 | Ansatz 3 | | -------------- | ---------- | -------- | ---------- | | Aufwand | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | Flexibilität | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | Wartbarkeit | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | | Performance | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | Zukunftssicher | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | **Empfehlung**: Starte mit Ansatz 1, plane für Ansatz 3 --- ## ✅ Nächste Schritte 1. **Sofort (Tag 1)**: - [ ] Language Feld zu users Collection hinzufügen - [ ] Settings-Page um Sprachauswahl erweitern 2. **Kurzfristig (Woche 1)**: - [ ] Templates in DE/EN erstellen - [ ] Template-Loader implementieren 3. **Mittelfristig (Woche 2-3)**: - [ ] EmailService mit Sprach-Support - [ ] Auto-Detection bei Registration 4. **Langfristig (Monat 2)**: - [ ] Weitere Sprachen (FR, ES, IT) - [ ] A/B Testing für Templates - [ ] Analytics pro Sprache --- ## 📚 Ressourcen - [PocketBase Hooks Documentation](https://pocketbase.io/docs/hooks) - [Accept-Language Parser](https://www.npmjs.com/package/accept-language-parser) - [React Email i18n](https://react.email/docs/integrations/i18n) - [Handlebars i18n Helper](https://github.com/handlebars-lang/handlebars.js/issues/1646) --- _Erstellt: 15. Januar 2025_ _Status: Bereit zur Diskussion_ _Priorität: Mittel_ _Geschätzter Aufwand: 5-10 Tage für vollständige Implementation_