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:
@@ -37,6 +37,7 @@ class LeaveWishService:
|
||||
target_weeks: int,
|
||||
first_year: int,
|
||||
child_id: UUID | None = None,
|
||||
name_message_key: str | None = None,
|
||||
recurrence: str = "once",
|
||||
last_year: int | None = None,
|
||||
priority: int = 1,
|
||||
@@ -65,6 +66,7 @@ class LeaveWishService:
|
||||
workspace_id=workspace_id,
|
||||
child_id=child_id,
|
||||
name=name,
|
||||
name_message_key=name_message_key,
|
||||
window_start_month=window_start_month,
|
||||
window_start_day=window_start_day,
|
||||
window_end_month=window_end_month,
|
||||
@@ -97,6 +99,7 @@ class LeaveWishService:
|
||||
leave_wish_id: UUID,
|
||||
child_id: UUID | None = None,
|
||||
name: str | None = None,
|
||||
name_message_key: str | None = None,
|
||||
window_start_month: int | None = None,
|
||||
window_start_day: int | None = None,
|
||||
window_end_month: int | None = None,
|
||||
@@ -114,21 +117,35 @@ class LeaveWishService:
|
||||
_clear_last_year: bool = False,
|
||||
_clear_parent_personal_data_uuid: bool = False,
|
||||
_clear_goal_override: bool = False,
|
||||
_clear_name_message_key: bool = False,
|
||||
) -> LeaveWish:
|
||||
"""Partial update — only non-None fields are applied.
|
||||
|
||||
`_clear_*` flags exist because a nullable field's "no change" and
|
||||
"set to None" both look like `None` on the wire; the router passes
|
||||
them based on whether the field was present in the request payload.
|
||||
|
||||
`name_message_key` is additionally enforced here rather than left to
|
||||
callers: whenever `name` actually changes, the key is force-cleared
|
||||
regardless of what was passed for it, since a caller (frontend bug,
|
||||
future API client, ...) forgetting to clear it would silently make a
|
||||
user's edited name revert to a stale preset label.
|
||||
"""
|
||||
entity = await self.get(leave_wish_id)
|
||||
original_name = entity.name
|
||||
|
||||
if _clear_child_id:
|
||||
entity.child_id = None
|
||||
elif child_id is not None:
|
||||
entity.child_id = child_id
|
||||
if _clear_name_message_key:
|
||||
entity.name_message_key = None
|
||||
elif name_message_key is not None:
|
||||
entity.name_message_key = name_message_key
|
||||
if name is not None:
|
||||
entity.name = name
|
||||
if name != original_name:
|
||||
entity.name_message_key = None
|
||||
if window_start_month is not None:
|
||||
entity.window_start_month = window_start_month
|
||||
if window_start_day is not None:
|
||||
|
||||
Reference in New Issue
Block a user