Files
breakpilot-lehrer/school-service/internal/notifications/templates.go
T
Benjamin Admin 89011d64f7
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 49s
CI / test-go-edu-search (push) Successful in 45s
CI / test-python-klausur (push) Failing after 3m41s
CI / test-python-agent-core (push) Successful in 38s
CI / test-nodejs-website (push) Successful in 49s
gofmt notification files
2026-05-22 18:14:15 +02:00

256 lines
11 KiB
Go
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.
package notifications
import (
"fmt"
"strings"
)
// Vars are the placeholder values rendered into a template string.
// The fields here must match the {{…}} markers in templates below.
type Vars struct {
Title string
Date string // YYYY-MM-DD
DatePretty string // e.g. "Donnerstag, 15. Oktober"
ClassName string // empty = whole school
TeacherName string
}
// Render picks the right template based on event type, audience, lead-day
// bucket and language, then substitutes the {{var}} placeholders.
//
// lead is grouped into three buckets:
//
// 0 → "today"
// 1 → "tomorrow"
// >=2 → "in X days" with X = lead value
//
// Falls back through (lang → de) and (event_type → "andere") so we never
// fail to render even with custom rule combos.
func Render(eventType, audience string, lead int, lang string, v Vars) (subject, body string) {
bucket := bucketFor(lead)
lc := strings.ToLower(lang)
if _, ok := templates[lc]; !ok {
lc = "de"
}
t := lookup(lc, eventType, audience, bucket)
subject = substitute(t.Subject, lead, v)
body = substitute(t.Body, lead, v)
return subject, body
}
func bucketFor(lead int) string {
switch {
case lead <= 0:
return "today"
case lead == 1:
return "tomorrow"
default:
return "days"
}
}
// lookup walks the templates map applying the (lang → de) and
// (event_type → andere) fallbacks. The bucket is guaranteed to exist for
// every audience because we always define today/tomorrow/days in the de
// baseline.
func lookup(lang, eventType, audience, bucket string) tmpl {
byEvent, ok := templates[lang][eventType]
if !ok {
byEvent = templates[lang]["andere"]
}
byAudience, ok := byEvent[audience]
if !ok {
byAudience = byEvent["parents"]
}
t, ok := byAudience[bucket]
if !ok {
t = byAudience["days"]
}
return t
}
func substitute(s string, lead int, v Vars) string {
classSuffix := ""
if v.ClassName != "" {
classSuffix = " (" + v.ClassName + ")"
}
repl := strings.NewReplacer(
"{{title}}", v.Title,
"{{date}}", v.Date,
"{{date_pretty}}", v.DatePretty,
"{{class_name}}", v.ClassName,
"{{class_suffix}}", classSuffix,
"{{teacher_name}}", v.TeacherName,
"{{lead}}", fmt.Sprintf("%d", lead),
)
return repl.Replace(s)
}
type tmpl struct {
Subject string
Body string
}
// templates[lang][eventType][audience][bucket] → tmpl
//
// Only the eight parent languages we ship subject-i18n.ts for are covered.
// Custom event_types fall back to the "andere" branch. Custom languages
// fall back to "de". This keeps the file under 500 LOC; if more locales
// are needed, split into one file per language.
var templates = map[string]map[string]map[string]map[string]tmpl{
"de": deTemplates(),
"en": enTemplates(),
"tr": trTemplates(),
"ar": arTemplates(),
"uk": ukTemplates(),
"ru": ruTemplates(),
"pl": plTemplates(),
"fr": frTemplates(),
}
func deTemplates() map[string]map[string]map[string]tmpl {
// All event types share the same template family; we only vary by audience
// and bucket. Specialise where wording really differs (e.g. fortbildung
// is school-free, schulfeier invites attendance).
parentToday := tmpl{
Subject: "Heute: {{title}}{{class_suffix}}",
Body: "Liebe Eltern, heute findet {{title}} statt{{class_suffix}}. Datum: {{date_pretty}}.",
}
parentTomorrow := tmpl{
Subject: "Morgen: {{title}}{{class_suffix}}",
Body: "Liebe Eltern, morgen ({{date_pretty}}) findet {{title}} statt{{class_suffix}}.",
}
parentDays := tmpl{
Subject: "In {{lead}} Tagen: {{title}}{{class_suffix}}",
Body: "Liebe Eltern, in {{lead}} Tagen ({{date_pretty}}) findet {{title}} statt{{class_suffix}}.",
}
studentToday := tmpl{
Subject: "Heute: {{title}}",
Body: "Heute ist {{title}}{{class_suffix}}.",
}
studentTomorrow := tmpl{
Subject: "Morgen: {{title}}",
Body: "Morgen ({{date_pretty}}) ist {{title}}{{class_suffix}}.",
}
studentDays := tmpl{
Subject: "In {{lead}} Tagen: {{title}}",
Body: "In {{lead}} Tagen ({{date_pretty}}) ist {{title}}{{class_suffix}}.",
}
bucket := func(today, tomorrow, days tmpl) map[string]tmpl {
return map[string]tmpl{"today": today, "tomorrow": tomorrow, "days": days}
}
universal := map[string]map[string]tmpl{
"parents": bucket(parentToday, parentTomorrow, parentDays),
"students": bucket(studentToday, studentTomorrow, studentDays),
}
return map[string]map[string]map[string]tmpl{
"fortbildung": universal,
"schulfeier": universal,
"klassenfahrt": universal,
"projekttag": universal,
"eltern_info": universal,
"andere": universal,
}
}
// The non-DE templates use the same structure; only the strings change.
// Defined in a single helper that takes the translation map and reuses the
// DE family glue.
func makeFamily(
pT, pTm, pD, sT, sTm, sD tmpl,
) map[string]map[string]map[string]tmpl {
bucket := func(today, tomorrow, days tmpl) map[string]tmpl {
return map[string]tmpl{"today": today, "tomorrow": tomorrow, "days": days}
}
universal := map[string]map[string]tmpl{
"parents": bucket(pT, pTm, pD),
"students": bucket(sT, sTm, sD),
}
return map[string]map[string]map[string]tmpl{
"fortbildung": universal,
"schulfeier": universal,
"klassenfahrt": universal,
"projekttag": universal,
"eltern_info": universal,
"andere": universal,
}
}
func enTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"Today: {{title}}{{class_suffix}}", "Dear parents, today {{title}} takes place{{class_suffix}}. Date: {{date_pretty}}."},
tmpl{"Tomorrow: {{title}}{{class_suffix}}", "Dear parents, tomorrow ({{date_pretty}}) {{title}} takes place{{class_suffix}}."},
tmpl{"In {{lead}} days: {{title}}{{class_suffix}}", "Dear parents, in {{lead}} days ({{date_pretty}}) {{title}} takes place{{class_suffix}}."},
tmpl{"Today: {{title}}", "Today is {{title}}{{class_suffix}}."},
tmpl{"Tomorrow: {{title}}", "Tomorrow ({{date_pretty}}) is {{title}}{{class_suffix}}."},
tmpl{"In {{lead}} days: {{title}}", "In {{lead}} days ({{date_pretty}}) is {{title}}{{class_suffix}}."},
)
}
func trTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"Bugün: {{title}}{{class_suffix}}", "Sayın veliler, bugün {{title}} gerçekleşiyor{{class_suffix}}. Tarih: {{date_pretty}}."},
tmpl{"Yarın: {{title}}{{class_suffix}}", "Sayın veliler, yarın ({{date_pretty}}) {{title}} gerçekleşiyor{{class_suffix}}."},
tmpl{"{{lead}} gün sonra: {{title}}{{class_suffix}}", "Sayın veliler, {{lead}} gün sonra ({{date_pretty}}) {{title}} gerçekleşiyor{{class_suffix}}."},
tmpl{"Bugün: {{title}}", "Bugün {{title}}{{class_suffix}}."},
tmpl{"Yarın: {{title}}", "Yarın ({{date_pretty}}) {{title}}{{class_suffix}}."},
tmpl{"{{lead}} gün sonra: {{title}}", "{{lead}} gün sonra ({{date_pretty}}) {{title}}{{class_suffix}}."},
)
}
func arTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"اليوم: {{title}}{{class_suffix}}", "أعزائي أولياء الأمور، اليوم يقام {{title}}{{class_suffix}}. التاريخ: {{date_pretty}}."},
tmpl{"غدًا: {{title}}{{class_suffix}}", "أعزائي أولياء الأمور، غدًا ({{date_pretty}}) يقام {{title}}{{class_suffix}}."},
tmpl{"بعد {{lead}} أيام: {{title}}{{class_suffix}}", "أعزائي أولياء الأمور، بعد {{lead}} أيام ({{date_pretty}}) يقام {{title}}{{class_suffix}}."},
tmpl{"اليوم: {{title}}", "اليوم {{title}}{{class_suffix}}."},
tmpl{"غدًا: {{title}}", "غدًا ({{date_pretty}}) {{title}}{{class_suffix}}."},
tmpl{"بعد {{lead}} أيام: {{title}}", "بعد {{lead}} أيام ({{date_pretty}}) {{title}}{{class_suffix}}."},
)
}
func ukTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"Сьогодні: {{title}}{{class_suffix}}", "Шановні батьки, сьогодні відбудеться {{title}}{{class_suffix}}. Дата: {{date_pretty}}."},
tmpl{"Завтра: {{title}}{{class_suffix}}", "Шановні батьки, завтра ({{date_pretty}}) відбудеться {{title}}{{class_suffix}}."},
tmpl{"Через {{lead}} днів: {{title}}{{class_suffix}}", "Шановні батьки, через {{lead}} днів ({{date_pretty}}) відбудеться {{title}}{{class_suffix}}."},
tmpl{"Сьогодні: {{title}}", "Сьогодні {{title}}{{class_suffix}}."},
tmpl{"Завтра: {{title}}", "Завтра ({{date_pretty}}) {{title}}{{class_suffix}}."},
tmpl{"Через {{lead}} днів: {{title}}", "Через {{lead}} днів ({{date_pretty}}) {{title}}{{class_suffix}}."},
)
}
func ruTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"Сегодня: {{title}}{{class_suffix}}", "Уважаемые родители, сегодня состоится {{title}}{{class_suffix}}. Дата: {{date_pretty}}."},
tmpl{"Завтра: {{title}}{{class_suffix}}", "Уважаемые родители, завтра ({{date_pretty}}) состоится {{title}}{{class_suffix}}."},
tmpl{"Через {{lead}} дней: {{title}}{{class_suffix}}", "Уважаемые родители, через {{lead}} дней ({{date_pretty}}) состоится {{title}}{{class_suffix}}."},
tmpl{"Сегодня: {{title}}", "Сегодня {{title}}{{class_suffix}}."},
tmpl{"Завтра: {{title}}", "Завтра ({{date_pretty}}) {{title}}{{class_suffix}}."},
tmpl{"Через {{lead}} дней: {{title}}", "Через {{lead}} дней ({{date_pretty}}) {{title}}{{class_suffix}}."},
)
}
func plTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"Dzisiaj: {{title}}{{class_suffix}}", "Drodzy rodzice, dzisiaj odbywa się {{title}}{{class_suffix}}. Data: {{date_pretty}}."},
tmpl{"Jutro: {{title}}{{class_suffix}}", "Drodzy rodzice, jutro ({{date_pretty}}) odbywa się {{title}}{{class_suffix}}."},
tmpl{"Za {{lead}} dni: {{title}}{{class_suffix}}", "Drodzy rodzice, za {{lead}} dni ({{date_pretty}}) odbywa się {{title}}{{class_suffix}}."},
tmpl{"Dzisiaj: {{title}}", "Dzisiaj {{title}}{{class_suffix}}."},
tmpl{"Jutro: {{title}}", "Jutro ({{date_pretty}}) {{title}}{{class_suffix}}."},
tmpl{"Za {{lead}} dni: {{title}}", "Za {{lead}} dni ({{date_pretty}}) {{title}}{{class_suffix}}."},
)
}
func frTemplates() map[string]map[string]map[string]tmpl {
return makeFamily(
tmpl{"Aujourd'hui : {{title}}{{class_suffix}}", "Chers parents, aujourd'hui a lieu {{title}}{{class_suffix}}. Date : {{date_pretty}}."},
tmpl{"Demain : {{title}}{{class_suffix}}", "Chers parents, demain ({{date_pretty}}) a lieu {{title}}{{class_suffix}}."},
tmpl{"Dans {{lead}} jours : {{title}}{{class_suffix}}", "Chers parents, dans {{lead}} jours ({{date_pretty}}) a lieu {{title}}{{class_suffix}}."},
tmpl{"Aujourd'hui : {{title}}", "Aujourd'hui c'est {{title}}{{class_suffix}}."},
tmpl{"Demain : {{title}}", "Demain ({{date_pretty}}) c'est {{title}}{{class_suffix}}."},
tmpl{"Dans {{lead}} jours : {{title}}", "Dans {{lead}} jours ({{date_pretty}}) c'est {{title}}{{class_suffix}}."},
)
}