fix(t-paliad-116): event_deadlines i18n follow-up — title_de backfill + notes_en

Two adjacent i18n leaks in /tools/fristenrechner "Was kommt nach…" mode,
same pattern as t-paliad-112's deadline_rules fix but on event_deadlines:

A) title_de empty for all 70 rows. The DTO already falls back to title
   silently, so DE locale rendered English titles ("Statement of Defence",
   "Decision of the EPO", …). Backfilled via mig 035 with UPC RoP DE
   terminology that matches the trigger_events.name_de translations from
   mig 033, so the picker label and the deadline row read the same.

B) notes column carries English text on rows 50, 52, 70 (DE-named column
   was DE-only in spec, but seeds slipped EN strings through). Mig 036
   adds a parallel notes_en column following the t-112 mig 032 pattern,
   copies the existing English into notes_en, and replaces notes with
   proper DE for those three rows.

Render path:
- service: select notes_en, plumb through EventDeadlineResult.NotesEN
- frontend: getLang() === "en" ? (notesEN || notes) : notes (mirrors the
  proceeding-tree timeline branch already in fristenrechner.ts)
This commit is contained in:
m
2026-05-04 17:03:58 +02:00
parent df2b2114df
commit 4e1213fbd1
6 changed files with 165 additions and 5 deletions

View File

@@ -480,6 +480,7 @@ interface EventDeadlineResult {
durationUnit: string;
timing: string;
notes?: string;
notesEN?: string;
ruleCodes: string[];
dueDate: string;
originalDueDate: string;
@@ -630,8 +631,9 @@ function renderEventResults(data: EventCalculateResponse) {
const composite = d.isComposite && d.compositeNote
? `<div class="event-result-composite" title="${escAttr(d.compositeNote)}">${escHtml(t("deadlines.event.composite.label"))} ${escHtml(d.compositeNote)}</div>`
: "";
const notes = d.notes
? `<div class="event-result-notes">${escHtml(d.notes)}</div>`
const noteText = getLang() === "en" ? (d.notesEN || d.notes) : d.notes;
const notes = noteText
? `<div class="event-result-notes">${escHtml(noteText)}</div>`
: "";
return `<li class="event-result-row">
<div class="event-result-header">

View File

@@ -0,0 +1,4 @@
-- Reverses 035_event_deadlines_title_de_backfill. Resets title_de back to
-- the empty default so a re-run of the up-migration backfills cleanly.
UPDATE paliad.event_deadlines SET title_de = '' WHERE title_de <> '';

View File

@@ -0,0 +1,90 @@
-- t-paliad-116 A: backfill paliad.event_deadlines.title_de for the
-- "Was kommt nach…" Fristenrechner mode.
--
-- 028 imported 70 deadlines from youpc data.deadlines with English `title`
-- populated and `title_de` defaulted to '' (NOT NULL). The DTO's
-- deadlineTitle() helper already prefers title_de when locale=de and
-- falls back to title when title_de is empty — so populating title_de
-- here is a pure data fix with no code changes for this leak.
--
-- Translations follow UPC RoP DE terminology (Klageerwiderung, Replik,
-- Duplik, Erwiderung, Berufungsschrift, Berufungsbegründung,
-- Anschlussberufungsschrift, Verletzungswiderklage, Nichtigkeitswiderklage,
-- Mängelbeseitigung, etc.) and stay consistent with the trigger_events
-- backfill from migration 033 so the picker label and the deadline row
-- read the same German.
UPDATE paliad.event_deadlines SET title_de = CASE id
WHEN 1 THEN 'Vorgängige Einrede'
WHEN 2 THEN 'Klageerwiderung'
WHEN 3 THEN 'Erwiderung auf die Nichtigkeitswiderklage und Replik auf die Klageerwiderung'
WHEN 4 THEN 'Replik auf die Klageerwiderung'
WHEN 5 THEN 'Duplik zur Replik auf die Klageerwiderung'
WHEN 6 THEN 'Antrag auf Patentänderung'
WHEN 7 THEN 'Replik auf die Erwiderung zur Widerklage, Duplik zur Replik auf die Klageerwiderung, Erwiderung auf den Patentänderungsantrag'
WHEN 8 THEN 'Duplik zur Replik, Replik auf die Erwiderung zum Patentänderungsantrag'
WHEN 9 THEN 'Erwiderung auf den Antrag auf Patentänderung'
WHEN 10 THEN 'Replik auf die Erwiderung zum Patentänderungsantrag'
WHEN 11 THEN 'Duplik zur Replik auf die Erwiderung zum Patentänderungsantrag'
WHEN 12 THEN 'Einreichung von Übersetzungen von Schriftstücken'
WHEN 13 THEN 'Erwiderung auf die Nichtigkeitsklage'
WHEN 14 THEN 'Replik auf die Erwiderung zur Nichtigkeitsklage, Erwiderung auf den Patentänderungsantrag, Erwiderung auf die Verletzungswiderklage'
WHEN 15 THEN 'Duplik zur Replik auf die Erwiderung zur Nichtigkeitsklage'
WHEN 16 THEN 'Erwiderung auf die Verletzungswiderklage'
WHEN 17 THEN 'Replik auf die Erwiderung zur Verletzungswiderklage'
WHEN 18 THEN 'Duplik zur Replik auf die Erwiderung zur Verletzungswiderklage, Duplik zur Replik auf die Erwiderung zum Patentänderungsantrag'
WHEN 19 THEN 'Erwiderung auf die negative Feststellungsklage'
WHEN 20 THEN 'Replik auf die Erwiderung zur negativen Feststellungsklage'
WHEN 21 THEN 'Duplik zur Replik auf die Erwiderung zur negativen Feststellungsklage'
WHEN 22 THEN 'Antrag auf Aufhebung oder Änderung einer Entscheidung des Amtes'
WHEN 23 THEN 'Antrag auf Simultanübersetzung'
WHEN 24 THEN 'Antrag auf Folgemaßnahmen aus einer rechtskräftigen Validitätsentscheidung'
WHEN 25 THEN 'Erwiderung auf den Antrag auf Schadensersatzbemessung'
WHEN 26 THEN 'Replik auf die Erwiderung zum Antrag auf Schadensersatzbemessung'
WHEN 27 THEN 'Duplik zur Replik auf die Erwiderung zum Antrag auf Schadensersatzbemessung'
WHEN 28 THEN 'Erwiderung auf den Antrag auf Rechnungslegung'
WHEN 29 THEN 'Replik auf die Erwiderung zum Antrag auf Rechnungslegung'
WHEN 30 THEN 'Duplik zur Replik auf die Erwiderung zum Antrag auf Rechnungslegung'
WHEN 31 THEN 'Berufungsschrift gegen eine in Regel 220.1(a) und (b) genannte Entscheidung'
WHEN 32 THEN 'Berufungsschrift gegen eine in Regel 220.1(c) genannte Anordnung oder eine in Regel 220.2 oder 221.3 genannte Entscheidung'
WHEN 33 THEN 'Berufungsbegründung gegen eine in Regel 220.1(a) und (b) genannte Entscheidung'
WHEN 34 THEN 'Berufungsbegründung gegen eine in Regel 220.1(c) genannte Anordnung oder eine in Regel 220.2 oder 221.3 genannte Entscheidung'
WHEN 35 THEN 'Anfechtung einer Entscheidung über die Verwerfung der Berufung als unzulässig'
WHEN 36 THEN 'Berufungserwiderung (auf die Berufung nach Regel 224.2(a))'
WHEN 37 THEN 'Berufungserwiderung (auf die Berufung nach Regel 224.2(b))'
WHEN 38 THEN 'Anschlussberufungsschrift (zur Berufung nach Regel 224.2(a))'
WHEN 39 THEN 'Anschlussberufungsschrift (zur Berufung nach Regel 224.2(b))'
WHEN 40 THEN 'Erwiderung auf eine Anschlussberufungsschrift (zur Berufung nach Regel 224.2(a))'
WHEN 41 THEN 'Erwiderung auf eine Anschlussberufungsschrift (zur Berufung nach Regel 224.2(b))'
WHEN 42 THEN 'Antrag auf Wiederaufnahme (schwerwiegender Verfahrensmangel)'
WHEN 43 THEN 'Antrag auf Wiederaufnahme (Straftat)'
WHEN 44 THEN 'Antrag auf Überprüfung einer verfahrensleitenden Anordnung'
WHEN 45 THEN 'Mängelbeseitigung / Zahlung'
WHEN 46 THEN 'Mängelbeseitigung / Einreichung schriftlicher Stellungnahme'
WHEN 47 THEN 'Mängelbeseitigung / Zahlung'
WHEN 48 THEN 'Mängelbeseitigung / Zahlung'
WHEN 49 THEN 'Antrag auf Überprüfung der Beweissicherungsanordnung'
WHEN 50 THEN 'Beginn des Hauptsacheverfahrens'
WHEN 51 THEN 'Mängelbeseitigung / Zahlung'
WHEN 52 THEN 'Beginn des Hauptsacheverfahrens'
WHEN 53 THEN 'Berufung (Anordnungen & mit Zulassung)'
WHEN 54 THEN 'Antrag auf Ermessensüberprüfung'
WHEN 55 THEN 'Antrag auf Berufungszulassung gegen Kostenentscheidungen'
WHEN 56 THEN 'Mängelbeseitigung / Zahlung'
WHEN 57 THEN 'Mängelbeseitigung / Zahlung'
WHEN 58 THEN 'Antrag auf Verweisung an die Zentralkammer'
WHEN 59 THEN 'Antrag auf Aufhebung einer Entscheidung des EPA, mit der ein Antrag auf einheitliche Wirkung zurückgewiesen wurde'
WHEN 60 THEN 'Mitteilung über Beauftragung eines Dolmetschers auf Kosten der Partei'
WHEN 61 THEN 'Verletzungswiderklage'
WHEN 62 THEN 'Antrag auf Patentänderung'
WHEN 63 THEN 'Nichtigkeitswiderklage'
WHEN 64 THEN 'Antrag auf Kostenentscheidung'
WHEN 65 THEN 'Berufungsschrift gegen eine in Regel 220.1(a) und (b) genannte Entscheidung'
WHEN 66 THEN 'Berufungsbegründung gegen eine in Regel 220.1(a) und (b) genannte Entscheidung'
WHEN 67 THEN 'Erneuerung der Schutzschrift'
WHEN 68 THEN 'Berichtigung von Entscheidungen und Anordnungen'
WHEN 69 THEN 'Klärung von Übersetzungsfragen'
WHEN 70 THEN 'Antrag auf Vertraulichkeit gegenüber der Öffentlichkeit'
ELSE title_de
END
WHERE id BETWEEN 1 AND 70;

View File

@@ -0,0 +1,13 @@
-- Reverses 036_event_deadlines_notes_en. Restores the original English
-- notes for rows 50/52/70 from notes_en, then drops the column.
UPDATE paliad.event_deadlines SET notes = CASE id
WHEN 50 THEN 'Or 20 Working days, whichever is longer'
WHEN 52 THEN 'Or 20 Working days, whichever is longer'
WHEN 70 THEN 'The 262.2 application can also be filed for opponent submissions to protect confidential information disclosed by the opponent.'
ELSE notes
END
WHERE id IN (50, 52, 70);
ALTER TABLE paliad.event_deadlines
DROP COLUMN IF EXISTS notes_en;

View File

@@ -0,0 +1,44 @@
-- t-paliad-116 B: paliad.event_deadlines.notes_en column + DE/EN swap
-- for the three rows that ship English text in the (DE-named) notes
-- column.
--
-- The DTO has long emitted `notes` directly into the EN render, so the
-- pattern matches t-112 mig 032 for deadline_rules: add a parallel
-- notes_en column, render path prefers _en when locale=EN and falls
-- back to notes (DE) when NULL.
--
-- Three rows currently leak English into the DE locale (ids 50, 52, 70):
-- 50, 52: "Or 20 Working days, whichever is longer"
-- 70: "The 262.2 application can also be filed for opponent
-- submissions to protect confidential information disclosed
-- by the opponent."
-- For those rows we (1) copy the existing English string verbatim into
-- notes_en, and (2) overwrite notes with the proper DE translation so
-- the DE locale stops leaking.
ALTER TABLE paliad.event_deadlines
ADD COLUMN IF NOT EXISTS notes_en text;
COMMENT ON COLUMN paliad.event_deadlines.notes_en IS
'English translation of notes. Render path prefers this column when '
'the active locale is EN; falls back to notes (DE) when NULL.';
-- Step 1: preserve current English text in notes_en for the three rows
-- that have it.
UPDATE paliad.event_deadlines SET notes_en = CASE id
WHEN 50 THEN 'Or 20 Working days, whichever is longer'
WHEN 52 THEN 'Or 20 Working days, whichever is longer'
WHEN 70 THEN 'The 262.2 application can also be filed for opponent submissions to protect confidential information disclosed by the opponent.'
ELSE notes_en
END
WHERE id IN (50, 52, 70);
-- Step 2: replace the leaking English text in the DE-named notes column
-- with proper DE translations.
UPDATE paliad.event_deadlines SET notes = CASE id
WHEN 50 THEN 'Oder 20 Werktage, je nachdem, was länger ist'
WHEN 52 THEN 'Oder 20 Werktage, je nachdem, was länger ist'
WHEN 70 THEN 'Der Antrag nach Regel 262.2 kann auch für Schriftsätze der einsprechenden Partei gestellt werden, um vertrauliche Informationen zu schützen, die von der einsprechenden Partei offengelegt wurden.'
ELSE notes
END
WHERE id IN (50, 52, 70);

View File

@@ -53,8 +53,8 @@ func (s *EventDeadlineService) ListTriggerEvents(ctx context.Context) ([]Trigger
}
// EventDeadlineResult is one computed deadline returned to the UI.
// Bilingual title + the rule codes attached to the deadline + the
// computed due date (after weekend/holiday rollover).
// Bilingual title + bilingual notes + the rule codes attached to the
// deadline + the computed due date (after weekend/holiday rollover).
type EventDeadlineResult struct {
ID int64 `json:"id"`
Title string `json:"title"`
@@ -63,6 +63,7 @@ type EventDeadlineResult struct {
DurationUnit string `json:"durationUnit"`
Timing string `json:"timing"`
Notes string `json:"notes,omitempty"`
NotesEN string `json:"notesEN,omitempty"`
RuleCodes []string `json:"ruleCodes"`
DueDate string `json:"dueDate"` // YYYY-MM-DD, after holiday/weekend adjust
OriginalDueDate string `json:"originalDueDate"` // YYYY-MM-DD, before adjust
@@ -103,7 +104,7 @@ func (s *EventDeadlineService) Calculate(ctx context.Context, triggerEventID int
var rows []eventDeadlineRow
err = s.db.SelectContext(ctx, &rows, `
SELECT id, title, title_de, duration_value, duration_unit, timing,
notes, alt_duration_value, alt_duration_unit, combine_op
notes, notes_en, alt_duration_value, alt_duration_unit, combine_op
FROM paliad.event_deadlines
WHERE trigger_event_id = $1 AND is_active = true
ORDER BY id`, triggerEventID)
@@ -167,6 +168,10 @@ func (s *EventDeadlineService) Calculate(ctx context.Context, triggerEventID int
}
}
notesEN := ""
if r.NotesEN != nil {
notesEN = *r.NotesEN
}
results = append(results, EventDeadlineResult{
ID: r.ID,
Title: r.Title,
@@ -175,6 +180,7 @@ func (s *EventDeadlineService) Calculate(ctx context.Context, triggerEventID int
DurationUnit: r.DurationUnit,
Timing: r.Timing,
Notes: r.Notes,
NotesEN: notesEN,
RuleCodes: codes[r.ID],
DueDate: picked.Format("2006-01-02"),
OriginalDueDate: original.Format("2006-01-02"),
@@ -259,6 +265,7 @@ type eventDeadlineRow struct {
DurationUnit string `db:"duration_unit"`
Timing string `db:"timing"`
Notes string `db:"notes"`
NotesEN *string `db:"notes_en"`
AltDurationValue *int `db:"alt_duration_value"`
AltDurationUnit *string `db:"alt_duration_unit"`
CombineOp *string `db:"combine_op"`