Show preset wish names in the current UI language

Preset wishes were created with `name: t(preset.labelKey)`, so the
translated string was persisted as the wish's name. A wish created while
the UI was Swedish stayed "Jul" in English forever -- the Swedish text
was the data, so no display-layer change could fix it.

Wish names are user-editable free text, so `name` cannot simply be
translated at render time. Migration 007 adds a nullable
`leave_wish.name_message_key` recording where a name came from: preset
wishes store the i18n key alongside the translated literal, and the UI
renders the key when present.

The key is cleared server-side the moment `name` actually changes,
compared against the stored value rather than trusting the caller, so no
client can forget to clear it and silently revert a user's own name to a
preset label.

An unknown key (a stale row, a renamed key) falls back to the stored
literal via an isTranslationKey guard, so a bad key can never crash the
page or leak a raw key to the user. Rows predating this column keep
displaying their stored name.

_FULL_SCHEMA in db/connection.py is updated in step with the migration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
This commit is contained in:
2026-09-14 22:32:54 +02:00
parent b03462066e
commit dc4c0a5edd
20 changed files with 668 additions and 17 deletions
@@ -26,6 +26,7 @@ class SQLiteLeaveWishRepository(LeaveWishRepository):
workspace_id=UUID(row["workspace_id"]),
child_id=UUID(row["child_id"]) if row["child_id"] is not None else None,
name=row["name"],
name_message_key=row["name_message_key"],
window_start_month=row["window_start_month"],
window_start_day=row["window_start_day"],
window_end_month=row["window_end_month"],
@@ -54,18 +55,19 @@ class SQLiteLeaveWishRepository(LeaveWishRepository):
await self._conn.execute(
"""
INSERT INTO leave_wish
(id, workspace_id, child_id, name,
(id, workspace_id, child_id, name, name_message_key,
window_start_month, window_start_day, window_end_month, window_end_day,
target_weeks, recurrence, first_year, last_year,
priority, parent_mode, parent_personal_data_uuid, goal_override,
window_mode, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
str(item.id),
str(item.workspace_id),
str(item.child_id) if item.child_id is not None else None,
item.name,
item.name_message_key,
item.window_start_month,
item.window_start_day,
item.window_end_month,
@@ -109,7 +111,7 @@ class SQLiteLeaveWishRepository(LeaveWishRepository):
cursor = await self._conn.execute(
"""
UPDATE leave_wish
SET child_id = ?, name = ?,
SET child_id = ?, name = ?, name_message_key = ?,
window_start_month = ?, window_start_day = ?,
window_end_month = ?, window_end_day = ?,
target_weeks = ?, recurrence = ?, first_year = ?, last_year = ?,
@@ -120,6 +122,7 @@ class SQLiteLeaveWishRepository(LeaveWishRepository):
(
str(item.child_id) if item.child_id is not None else None,
item.name,
item.name_message_key,
item.window_start_month,
item.window_start_day,
item.window_end_month,