feat: Centralize i18n keys and migrate usages#42
Conversation
Consolidates translation strings into centralized namespaces (common, validation, form) and migrates UI code to use these parameterized keys to eliminate duplication and improve consistency.
Updates locale files to include the new keys and adjusts German tone to use informal "du". Benefits: unified success/error messages with an {item} parameter, centralized validation messages, reusable form labels/placeholders, and simpler translation maintenance.
WalkthroughThis PR centralizes translation keys across the application by introducing standardized categories (validation, form, common) and updating components to use these reusable keys instead of component-specific ones. The localization files are reorganized to consolidate CRUD operations, form fields, and validation messages. Functionality remains unchanged—only translation key structure and references are modified. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Areas requiring extra attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
components/preset-selector.tsx (1)
89-89: Consider using a more appropriate item label for CRUD messages.All toast messages use
t("preset.create")as the item parameter, which translates to "New Preset" in English. This works for creation, but reads awkwardly for updates and deletions:
- ✓ "New Preset created successfully"
- ✗ "New Preset updated successfully"
- ✗ "New Preset deleted successfully"
Consider using a more generic label like a new key
preset.title(translating to "Preset") for consistency across all operations.Example fix:
-toast.success(t("common.created", { item: t("preset.create") })); +toast.success(t("common.created", { item: t("preset.title") }));Apply similar changes for update and delete operations. You would need to add
"title": "Preset"to thepresetsection in bothen.jsonandde.json.Also applies to: 93-93, 107-107, 111-111, 126-126, 151-151, 161-161, 164-164
components/external-sync-manage-dialog.tsx (1)
274-276: Consider using a more generic item label for CRUD messages.All CRUD toast messages use
t("externalSync.syncTypeCustom")as the item parameter, which translates to "Custom Calendar". While this is more appropriate than some alternatives, it may still be confusing since these operations apply to all external sync types (iCloud, Google, Custom):
- "Custom Calendar updated successfully" (even when updating an iCloud sync)
Consider adding a new generic key like
externalSync.title(translating to "External Calendar") for consistency across all sync types.Example:
-toast.success(t("common.created", { item: t("externalSync.syncTypeCustom") })); +toast.success(t("common.created", { item: t("externalSync.title") }));Add to both locale files:
"externalSync": { "title": "External Calendar", // or "External Sync" ... }Also applies to: 281-284, 288-290, 325-327, 330-333, 337-339, 400-402, 405-408, 412-414, 486-488, 491-494, 506-508, 570-572, 576-581, 586-588
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
.github/copilot-instructions.md(1 hunks)app/page.tsx(2 hunks)components/calendar-dialog.tsx(3 hunks)components/delete-calendar-dialog.tsx(1 hunks)components/external-sync-manage-dialog.tsx(14 hunks)components/manage-password-dialog.tsx(4 hunks)components/password-dialog.tsx(4 hunks)components/preset-edit-dialog.tsx(3 hunks)components/preset-selector.tsx(5 hunks)components/shift-form-fields.tsx(2 hunks)components/sync-notification-dialog.tsx(2 hunks)hooks/useCalendars.ts(4 hunks)hooks/useNotes.ts(3 hunks)hooks/useShifts.ts(3 hunks)messages/de.json(7 hunks)messages/en.json(6 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
{app/**/*.tsx,components/**/*.tsx,hooks/*.ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
{app/**/*.tsx,components/**/*.tsx,hooks/*.ts}: Uselib/password-cache.tsutilities:getCachedPassword(calendarId),verifyAndCachePassword(calendarId, password),setCachedPassword(calendarId, password), andremoveCachedPassword(calendarId)for password management
Password verification is asynchronous - always awaitverifyAndCachePassword(calendarId, password)before executing mutations
Files:
components/delete-calendar-dialog.tsxcomponents/preset-edit-dialog.tsxhooks/useShifts.tscomponents/sync-notification-dialog.tsxcomponents/preset-selector.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxhooks/useNotes.tshooks/useCalendars.tscomponents/external-sync-manage-dialog.tsxapp/page.tsxcomponents/shift-form-fields.tsx
components/**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
components/**/*.tsx: UseformatDateToLocal()for converting dates to YYYY-MM-DD format in UI components
UsePRESET_COLORSarray with hex format (#3b82f6) for color selection, and apply 20% opacity for backgrounds using format${color}20
Files:
components/delete-calendar-dialog.tsxcomponents/preset-edit-dialog.tsxcomponents/sync-notification-dialog.tsxcomponents/preset-selector.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxcomponents/external-sync-manage-dialog.tsxcomponents/shift-form-fields.tsx
{app/**/*.tsx,components/**/*.tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
{app/**/*.tsx,components/**/*.tsx}: UseuseTranslations()from next-intl for all user-facing text in components
Usedelocale from date-fns whenlocale === "de", otherwise useenUS
All components must support real-time updates via Server-Sent Events (SSE) by listening to relevant SSE events (shift, preset, note, sync-log updates)
Use silent refresh patternfetchData(false)in SSE event handlers to update component data without showing loading states
Use counter-based refresh trigger props (e.g.,syncLogRefreshTrigger) to manage component re-fetches without causing UI flashing or blinking
Never require password input twice - the integrated 'Currently Locked' form is sufficient for locked calendars
Files:
components/delete-calendar-dialog.tsxcomponents/preset-edit-dialog.tsxcomponents/sync-notification-dialog.tsxcomponents/preset-selector.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxcomponents/external-sync-manage-dialog.tsxapp/page.tsxcomponents/shift-form-fields.tsx
components/*[Cc]alendar*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
components/*[Cc]alendar*.tsx: Left-click on calendar cell toggles shift with selected preset; right-click opens note dialog and prevents default context menu; toggle logic: delete if exists, create if not
Use<StickyNote>icon indicator for calendar days with notes
Files:
components/delete-calendar-dialog.tsxcomponents/calendar-dialog.tsx
hooks/use*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
hooks/use*.ts: All data-fetching hooks (useShifts,usePresets,useNotes) must automatically callgetCachedPassword(calendarId)before each fetch and append password as query parameter if present
All data-fetching hooks must return empty arrays on 401 responses for graceful degradation without crashing the UI
Files:
hooks/useShifts.tshooks/useNotes.tshooks/useCalendars.ts
{app/api/**/*.ts,hooks/*.ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Log all API errors with
console.error()for debugging
Files:
hooks/useShifts.tshooks/useNotes.tshooks/useCalendars.ts
messages/*.json
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
All translation keys in
messages/{de,en}.jsonmust be actively used in components; remove unused keys to keep translation files clean
Files:
messages/en.jsonmessages/de.json
app/page.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
app/page.tsx: UseuseStatefor shifts, presets, notes, and calendars state;useEffectfor data fetching on calendar/date changes;useRouter().replace()for URL state sync
UsestatsRefreshTriggercounter for tracking mutations and triggering re-fetches of statistics
Use separate mobile calendar selector withshowMobileCalendarDialogfor mobile UI
After successful password entry in locked calendar, callhandlePasswordSuccess(password)which triggersrefetchShifts(),refetchPresets(),refetchNotes(),fetchExternalSyncs(),fetchSyncErrorStatus(), and incrementsetStatsRefreshTrigger
Files:
app/page.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to messages/*.json : All translation keys in `messages/{de,en}.json` must be actively used in components; remove unused keys to keep translation files clean
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to {app/**/*.tsx,components/**/*.tsx,hooks/*.ts} : Use `lib/password-cache.ts` utilities: `getCachedPassword(calendarId)`, `verifyAndCachePassword(calendarId, password)`, `setCachedPassword(calendarId, password)`, and `removeCachedPassword(calendarId)` for password management
Applied to files:
components/delete-calendar-dialog.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxhooks/useCalendars.tsapp/page.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to {app/**/*.tsx,components/**/*.tsx} : Never require password input twice - the integrated 'Currently Locked' form is sufficient for locked calendars
Applied to files:
components/delete-calendar-dialog.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxhooks/useCalendars.tsapp/page.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to app/page.tsx : After successful password entry in locked calendar, call `handlePasswordSuccess(password)` which triggers `refetchShifts()`, `refetchPresets()`, `refetchNotes()`, `fetchExternalSyncs()`, `fetchSyncErrorStatus()`, and increment `setStatsRefreshTrigger`
Applied to files:
components/delete-calendar-dialog.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxhooks/useCalendars.tscomponents/external-sync-manage-dialog.tsxapp/page.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to {app/**/*.tsx,components/**/*.tsx,hooks/*.ts} : Password verification is asynchronous - always await `verifyAndCachePassword(calendarId, password)` before executing mutations
Applied to files:
components/delete-calendar-dialog.tsxcomponents/password-dialog.tsxcomponents/calendar-dialog.tsxcomponents/manage-password-dialog.tsxhooks/useCalendars.tsapp/page.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to app/api/calendars/route.ts : Calendar list (GET `/api/calendars`) never requires password - this allows calendar switching regardless of lock status
Applied to files:
components/delete-calendar-dialog.tsxhooks/useCalendars.ts
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to hooks/use*.ts : All data-fetching hooks (`useShifts`, `usePresets`, `useNotes`) must automatically call `getCachedPassword(calendarId)` before each fetch and append password as query parameter if present
Applied to files:
components/delete-calendar-dialog.tsxhooks/useCalendars.ts
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/*[Ss]hift*Dialog*.tsx : Shift dialog must have `saveAsPreset` enabled by default
Applied to files:
components/preset-edit-dialog.tsxcomponents/preset-selector.tsxcomponents/password-dialog.tsxcomponents/shift-form-fields.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/**/*.tsx : Use `PRESET_COLORS` array with hex format (`#3b82f6`) for color selection, and apply 20% opacity for backgrounds using format `${color}20`
Applied to files:
components/preset-edit-dialog.tsxcomponents/preset-selector.tsxcomponents/calendar-dialog.tsxcomponents/external-sync-manage-dialog.tsxcomponents/shift-form-fields.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to {app/**/*.tsx,components/**/*.tsx} : Use `useTranslations()` from next-intl for all user-facing text in components
Applied to files:
components/preset-edit-dialog.tsxcomponents/sync-notification-dialog.tsxcomponents/preset-selector.tsxcomponents/calendar-dialog.tsx.github/copilot-instructions.mdapp/page.tsxcomponents/shift-form-fields.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/**/[!ui/]*.tsx : All dialogs must follow the unified design pattern with gradient backgrounds, consistent padding (`p-6`, `px-6`, `pb-6`), border styling with reduced opacity (`border-border/50`), backdrop blur effects, and gradient text for titles
Applied to files:
components/preset-edit-dialog.tsxcomponents/sync-notification-dialog.tsxcomponents/password-dialog.tsxcomponents/external-sync-manage-dialog.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/*[Cc]alendar*.tsx : Left-click on calendar cell toggles shift with selected preset; right-click opens note dialog and prevents default context menu; toggle logic: delete if exists, create if not
Applied to files:
components/preset-edit-dialog.tsxcomponents/preset-selector.tsxcomponents/calendar-dialog.tsxhooks/useCalendars.tsmessages/en.json
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/**/*.tsx : Use `formatDateToLocal()` for converting dates to YYYY-MM-DD format in UI components
Applied to files:
components/preset-edit-dialog.tsxcomponents/calendar-dialog.tsxcomponents/external-sync-manage-dialog.tsxcomponents/shift-form-fields.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to app/page.tsx : Use `useState` for shifts, presets, notes, and calendars state; `useEffect` for data fetching on calendar/date changes; `useRouter().replace()` for URL state sync
Applied to files:
hooks/useShifts.tshooks/useCalendars.ts
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to messages/*.json : All translation keys in `messages/{de,en}.json` must be actively used in components; remove unused keys to keep translation files clean
Applied to files:
components/sync-notification-dialog.tsx.github/copilot-instructions.mdhooks/useNotes.tsmessages/en.jsonapp/page.tsxmessages/de.jsoncomponents/shift-form-fields.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/**/[!ui/]*.tsx : Dialog components must accept props: `open`, `onOpenChange`, `onSubmit`, and optional `onDelete`; reset local state when `open` changes to false
Applied to files:
components/sync-notification-dialog.tsxcomponents/password-dialog.tsxcomponents/external-sync-manage-dialog.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to app/page.tsx : Use separate mobile calendar selector with `showMobileCalendarDialog` for mobile UI
Applied to files:
components/calendar-dialog.tsx
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to components/*[Cc]alendar*.tsx : Use `<StickyNote>` icon indicator for calendar days with notes
Applied to files:
components/calendar-dialog.tsxhooks/useCalendars.ts
📚 Learning: 2025-12-07T02:40:45.432Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-07T02:40:45.432Z
Learning: Applies to {app/**/*.tsx,components/**/*.tsx} : Use `de` locale from date-fns when `locale === "de"`, otherwise use `enUS`
Applied to files:
.github/copilot-instructions.md
🧬 Code graph analysis (6)
components/preset-edit-dialog.tsx (2)
components/ui/label.tsx (1)
Label(24-24)components/ui/input.tsx (1)
Input(21-21)
hooks/useShifts.ts (1)
lib/db/schema.ts (1)
shifts(49-82)
components/calendar-dialog.tsx (2)
components/ui/label.tsx (1)
Label(24-24)components/ui/input.tsx (1)
Input(21-21)
components/external-sync-manage-dialog.tsx (1)
lib/external-calendar-utils.ts (2)
detectCalendarSyncType(15-40)isValidCalendarUrl(48-101)
app/page.tsx (2)
components/ui/label.tsx (1)
Label(24-24)components/ui/input.tsx (1)
Input(21-21)
components/shift-form-fields.tsx (2)
components/ui/label.tsx (1)
Label(24-24)components/ui/textarea.tsx (1)
Textarea(18-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Agent
- GitHub Check: build-dev
🔇 Additional comments (16)
components/shift-form-fields.tsx (1)
141-159: LGTM! Translation keys successfully migrated to centralized form namespace.The migration from component-specific keys (
shift.notesOptional,shift.notesPlaceholder,shift.color) to centralized form keys (form.notesLabel,form.notesPlaceholder,form.colorLabel) is consistent with the PR's i18n consolidation objectives.app/page.tsx (1)
918-939: LGTM! Password form and validation keys successfully centralized.The migration consolidates password-related UI strings into the centralized
form.*namespace (labels and placeholders) andvalidation.*namespace (error messages), improving consistency across the application.components/delete-calendar-dialog.tsx (1)
80-87: LGTM! Password field keys migrated to form namespace.The password input field now uses centralized
form.passwordLabelandform.passwordPlaceholderkeys, maintaining consistency with the broader i18n refactor across dialogs.hooks/useNotes.ts (1)
75-165: LGTM! Note operations migrated to parameterized common keys.All note CRUD operations now use centralized
common.*keys with{item}set tot("note.note"), following the same consistent pattern established across other hooks in this PR.components/calendar-dialog.tsx (1)
108-191: LGTM! Form fields successfully migrated to centralized namespace.All form labels and placeholders now use the
form.*namespace. The placeholder uses example interpolation ({ example: t("calendar.name") }), which provides context-specific hints while maintaining reusable keys.hooks/useShifts.ts (1)
76-166: LGTM! Shift operations consistently use parameterized common keys.All shift CRUD operations follow the established pattern of using centralized
common.*keys with{item}set tot("shift.title"), maintaining consistency across all data-fetching hooks.components/password-dialog.tsx (1)
56-118: LGTM! Password dialog fully migrated to centralized keys.The dialog now uses
validation.passwordIncorrectfor error messages,form.passwordLabelfor labels, andcommon.unlockfor the action button. The migration is complete and consistent with the centralized i18n structure.hooks/useCalendars.ts (1)
60-124: LGTM! Successfully migrated to parameterized common keys.The toast messages now use centralized
common.*keys with{item}parameterization (set tot("calendar.title")), and password validation usesvalidation.passwordIncorrect. This approach eliminates duplicate success/error message strings while maintaining clear, entity-specific user feedback. Thecalendar.titlekey exists in both locale files and is actively used across multiple components (useCalendars.ts, calendar-selector.tsx, app-header.tsx).components/preset-edit-dialog.tsx (1)
176-178: LGTM! Consistent migration to centralized form keys.The form field labels and placeholders have been correctly migrated to the centralized
form.*namespace with proper parameterization for the name placeholder.Also applies to: 245-245, 255-255, 259-259
components/manage-password-dialog.tsx (1)
64-64: LGTM! Clean migration to centralized validation keys.Password validation errors have been correctly migrated to the
validation.*namespace, and the password label now uses the centralizedform.passwordLabelkey. The changes maintain consistency across all error paths.Also applies to: 72-72, 105-105, 111-111, 132-132, 236-236
messages/de.json (1)
11-43: LGTM! Well-structured centralized translation keys.The new centralized namespaces (
common,validation,form) are well-organized and follow the informal "du" style consistently across all German translations. This consolidation eliminates duplication and improves maintainability.components/external-sync-manage-dialog.tsx (2)
194-194: LGTM! Validation errors properly centralized.All validation error messages have been correctly migrated to the
validation.*namespace with proper parameterization for dynamic values likemaxSize.Also applies to: 200-200, 207-207, 220-220, 234-234
700-700: LGTM! Form fields correctly use centralized keys.All form labels and placeholders have been properly migrated to the
form.*namespace with correct parameterization for examples.Also applies to: 852-852, 856-858, 928-928, 954-954
messages/en.json (1)
11-43: LGTM! Centralized translation structure is well-organized.The new
common,validation, andformnamespaces are properly structured and align perfectly with the German translations. This consolidation successfully eliminates duplication across the codebase..github/copilot-instructions.md (1)
152-214: Excellent documentation of the centralized translation structure.The new "Translation Structure (Optimized)" section clearly documents the conventions and provides helpful examples for using the centralized keys. This will help maintain consistency in future contributions.
components/sync-notification-dialog.tsx (1)
151-153: LGTM! Appropriate use of centralized CRUD messages.The migration to
common.*keys is correct, and usingsyncNotifications.titleas the item parameter produces clear, user-friendly messages like "Sync Notifications deleted successfully".Also applies to: 155-157, 161-163, 191-193, 197-199
There was a problem hiding this comment.
Pull request overview
This PR centralizes i18n translation keys to eliminate duplication and improve consistency across the BetterShift application. The refactoring introduces three new centralized namespaces (common, validation, and form) with parameterized messages, while removing redundant feature-specific translations. Additionally, German translations are updated to consistently use the informal "du" form throughout.
Key Changes
- New centralized namespaces: Added
common.*for CRUD operations (with{item}parameter),validation.*for all validation messages, andform.*for reusable form labels/placeholders - Comprehensive code migration: Updated 11 components and 3 hooks to use centralized translation keys instead of feature-specific duplicates
- German tone consistency: Updated all German translations to use informal "du" form (e.g., "Bitte entsperre..." instead of "Bitte entsperren Sie...")
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 29 comments.
Show a summary per file
| File | Description |
|---|---|
| messages/en.json | Adds centralized namespaces (common, validation, form) and removes duplicate CRUD/validation messages from feature-specific namespaces |
| messages/de.json | Same as en.json, with German translations updated to informal "du" tone throughout |
| hooks/useShifts.ts | Migrates shift CRUD toast messages to use common.* keys with parameterized {item} |
| hooks/useNotes.ts | Migrates note CRUD toast messages to use common.* keys with parameterized {item} |
| hooks/useCalendars.ts | Migrates calendar CRUD and password validation to use common.* and validation.* keys |
| components/sync-notification-dialog.tsx | Migrates sync log CRUD operations to use common.* keys |
| components/shift-form-fields.tsx | Replaces shift-specific form labels with reusable form.* keys |
| components/preset-selector.tsx | Migrates preset CRUD toast messages to use common.* keys |
| components/preset-edit-dialog.tsx | Replaces preset-specific form labels/placeholders with form.* keys |
| components/password-dialog.tsx | Migrates to use form.* for labels and validation.* for error messages |
| components/manage-password-dialog.tsx | Replaces password-specific validation messages with centralized validation.* keys |
| components/external-sync-manage-dialog.tsx | Comprehensive migration of external sync CRUD, validation, and form fields to centralized keys |
| components/delete-calendar-dialog.tsx | Migrates password input fields to use form.* keys |
| components/calendar-dialog.tsx | Replaces calendar-specific form labels with reusable form.* keys |
| app/page.tsx | Migrates unlock form to use form.* and validation.* keys |
| .github/copilot-instructions.md | Documents new centralized translation structure with usage examples and rules |
| } catch (error) { | ||
| console.error("Failed to save preset:", error); | ||
| toast.error(t("preset.saveError")); | ||
| toast.error(t("common.updateError", { item: t("preset.create") })); |
There was a problem hiding this comment.
Using t("preset.create") as the {item} parameter produces "New Preset could not be updated", which is grammatically awkward. Should use a generic "Preset" term instead. See comment on line 89.
| onPresetsChange(); | ||
| if (onShiftsChange) onShiftsChange(); | ||
| toast.success(t("preset.deleted")); | ||
| toast.success(t("common.deleted", { item: t("preset.create") })); |
There was a problem hiding this comment.
Using t("preset.create") as the {item} parameter produces "New Preset deleted successfully", which is grammatically awkward. Should use a generic "Preset" term instead. See comment on line 89.
| toast.success( | ||
| t("common.deleted", { item: t("syncNotifications.title") }) | ||
| ); |
There was a problem hiding this comment.
Using t("syncNotifications.title") as the {item} parameter produces "Sync Notifications deleted successfully", which is grammatically incorrect (plural noun with singular verb "deleted successfully").
The original message was "Sync logs deleted successfully". Consider adding a translation key like "syncNotifications.itemName": "Sync logs" or use a singular form like "Sync log" for grammatical correctness.
| toast.error( | ||
| t("common.deleteError", { item: t("syncNotifications.title") }) | ||
| ); |
There was a problem hiding this comment.
Using t("syncNotifications.title") as the {item} parameter produces "Sync Notifications could not be deleted", which is grammatically incorrect (plural noun with singular verb). See comment on line 151-153.
| ); | ||
| setShifts((shifts) => shifts.filter((s) => s.id !== tempId)); | ||
| toast.error(t("shift.createError")); | ||
| toast.error(t("common.createError", { item: t("shift.title") })); |
There was a problem hiding this comment.
[nitpick] Using t("shift.title") as the {item} parameter produces "Shifts could not be created", which uses a plural noun ("Shifts") in a singular context. While not strictly incorrect, it's less clear than using "Shift" (singular).
Consider adding "shift.itemName": "Shift" to the translation files for use in toast messages, keeping shift.title = "Shifts" for section titles.
| toast.error( | ||
| t("common.updateError", { item: t("externalSync.syncTypeCustom") }) | ||
| ); |
There was a problem hiding this comment.
Using t("externalSync.syncTypeCustom") as the {item} parameter produces "Custom Calendar could not be updated", which is semantically incorrect. Should use a generic "external calendar" term instead. See comment on line 274-276.
| toast.error( | ||
| t("common.updateError", { item: t("externalSync.syncTypeCustom") }) | ||
| ); |
There was a problem hiding this comment.
Using t("externalSync.syncTypeCustom") as the {item} parameter produces "Custom Calendar could not be updated", which is semantically incorrect. Should use a generic "external calendar" term instead. See comment on line 274-276.
| toast.error( | ||
| t("common.deleteError", { item: t("syncNotifications.title") }) | ||
| ); |
There was a problem hiding this comment.
Using t("syncNotifications.title") as the {item} parameter produces "Sync Notifications could not be deleted", which is grammatically incorrect (plural noun with singular verb). See comment on line 151-153.
| toast.error( | ||
| t("common.updateError", { item: t("syncNotifications.title") }) | ||
| ); |
There was a problem hiding this comment.
Using t("syncNotifications.title") as the {item} parameter produces "Sync Notifications could not be updated", which is grammatically incorrect (plural noun with singular verb). See comment on line 151-153.
| toast.success( | ||
| t("common.deleted", { item: t("externalSync.syncTypeCustom") }) | ||
| ); |
There was a problem hiding this comment.
Using t("externalSync.syncTypeCustom") as the {item} parameter produces "Custom Calendar deleted successfully", which is semantically incorrect. Should use a generic "external calendar" term instead. See comment on line 274-276.
Consolidates translation strings into centralized namespaces (common, validation, form) and migrates UI code to use these parameterized keys to eliminate duplication and improve consistency.
Updates locale files to include the new keys and adjusts German tone to use informal "du". Benefits: unified success/error messages with an {item} parameter, centralized validation messages, reusable form labels/placeholders, and simpler translation maintenance.
Summary by CodeRabbit
Refactor
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.