Skip to content

feat: Centralize i18n keys and migrate usages#42

Merged
panteLx merged 1 commit intomainfrom
feat/centralized-i18n
Dec 7, 2025
Merged

feat: Centralize i18n keys and migrate usages#42
panteLx merged 1 commit intomainfrom
feat/centralized-i18n

Conversation

@panteLx
Copy link
Owner

@panteLx panteLx commented Dec 7, 2025

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

    • Consolidated and centralized message and label translations across all components for consistent user-facing messaging
    • Standardized form field labels, placeholders, and validation error messages
    • Unified success and error toast messages for CRUD operations (create, update, delete)
  • Documentation

    • Added comprehensive translation structure guidelines for i18n conventions and best practices

✏️ Tip: You can customize this high-level summary in your review settings.

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.
Copilot AI review requested due to automatic review settings December 7, 2025 04:03
@coderabbitai
Copy link

coderabbitai bot commented Dec 7, 2025

Walkthrough

This 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

Cohort / File(s) Summary
Documentation
.github/copilot-instructions.md
Added centralized Translation Structure (Optimized) documentation for i18n conventions, including German informal style, parametrized keys, validation keys, and form field keys.
Form & Validation Key Updates
app/page.tsx, components/calendar-dialog.tsx, components/delete-calendar-dialog.tsx, components/manage-password-dialog.tsx, components/password-dialog.tsx, components/preset-edit-dialog.tsx, components/shift-form-fields.tsx
Replaced form labels, placeholders, and validation error messages with centralized keys: password.passwordform.passwordLabel, password.passwordPlaceholderform.passwordPlaceholder, password.error*validation.*, shift.colorform.colorLabel, shift.notes*form.notes*.
Common CRUD & Message Consolidation
components/external-sync-manage-dialog.tsx, components/preset-selector.tsx, components/sync-notification-dialog.tsx, hooks/useCalendars.ts, hooks/useNotes.ts, hooks/useShifts.ts
Consolidated item-specific toast messages (created, updated, deleted, createError, updateError, deleteError) to generic common.* keys with item interpolation; replaced component-specific validation keys with validation.* equivalents.
Localization Files
messages/en.json, messages/de.json
Added new top-level groups: common (with CRUD operations and lifecycle messages), validation (passwordMatch, passwordIncorrect, passwordRequired, fileRequired, fileTooLarge, urlRequired, urlInvalid, urlAlreadyExists), and form (namePlaceholder, nameLabel, colorLabel, notesLabel, notesPlaceholder, passwordLabel, passwordPlaceholder, urlLabel, urlPlaceholder). Removed scattered component-specific keys (calendar.namePlaceholder, password.password, shift.color, preset.namePlaceholder, externalSync.*, note.created/updated/deleted, etc.).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Areas requiring extra attention:

  • Verify all removed translation keys from en.json and de.json are no longer referenced anywhere in the codebase (especially in external-sync and syncNotifications sections).
  • Confirm all new common.*, validation.*, and form.* keys are properly populated in both language files with accurate translations.
  • Check that parametrized keys (e.g., item in common.created, maxSize in validation.fileTooLarge) are correctly interpolated in all component usages.
  • Validate consistency across hooks (useCalendars, useNotes, useShifts, useShifts) for item label references (e.g., calendar.title, note.label, shift.title).

Possibly related PRs

Poem

🐰 Keys once scattered far and wide,
Now neatly tucked in one place to reside,
form., common., validation. shine,
Translation structure? chef's kiss — divine! ✨
Refactored hops through every file,
Makes this bunny smile!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Centralize i18n keys and migrate usages' accurately captures the main objective of the PR, which consolidates translation strings into centralized namespaces and migrates UI code to use these new keys.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/centralized-i18n

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 the preset section in both en.json and de.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

📥 Commits

Reviewing files that changed from the base of the PR and between 54e0319 and 9ef7b00.

📒 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}: Use lib/password-cache.ts utilities: getCachedPassword(calendarId), verifyAndCachePassword(calendarId, password), setCachedPassword(calendarId, password), and removeCachedPassword(calendarId) for password management
Password verification is asynchronous - always await verifyAndCachePassword(calendarId, password) before executing mutations

Files:

  • components/delete-calendar-dialog.tsx
  • components/preset-edit-dialog.tsx
  • hooks/useShifts.ts
  • components/sync-notification-dialog.tsx
  • components/preset-selector.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • hooks/useNotes.ts
  • hooks/useCalendars.ts
  • components/external-sync-manage-dialog.tsx
  • app/page.tsx
  • components/shift-form-fields.tsx
components/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

components/**/*.tsx: Use formatDateToLocal() for converting dates to YYYY-MM-DD format in UI components
Use PRESET_COLORS array with hex format (#3b82f6) for color selection, and apply 20% opacity for backgrounds using format ${color}20

Files:

  • components/delete-calendar-dialog.tsx
  • components/preset-edit-dialog.tsx
  • components/sync-notification-dialog.tsx
  • components/preset-selector.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • components/external-sync-manage-dialog.tsx
  • components/shift-form-fields.tsx
{app/**/*.tsx,components/**/*.tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

{app/**/*.tsx,components/**/*.tsx}: Use useTranslations() from next-intl for all user-facing text in components
Use de locale from date-fns when locale === "de", otherwise use enUS
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 pattern fetchData(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.tsx
  • components/preset-edit-dialog.tsx
  • components/sync-notification-dialog.tsx
  • components/preset-selector.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • components/external-sync-manage-dialog.tsx
  • app/page.tsx
  • components/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.tsx
  • components/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 call getCachedPassword(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.ts
  • hooks/useNotes.ts
  • hooks/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.ts
  • hooks/useNotes.ts
  • hooks/useCalendars.ts
messages/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

All translation keys in messages/{de,en}.json must be actively used in components; remove unused keys to keep translation files clean

Files:

  • messages/en.json
  • messages/de.json
app/page.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

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
Use statsRefreshTrigger counter for tracking mutations and triggering re-fetches of statistics
Use separate mobile calendar selector with showMobileCalendarDialog for mobile UI
After successful password entry in locked calendar, call handlePasswordSuccess(password) which triggers refetchShifts(), refetchPresets(), refetchNotes(), fetchExternalSyncs(), fetchSyncErrorStatus(), and increment setStatsRefreshTrigger

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.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • hooks/useCalendars.ts
  • app/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.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • hooks/useCalendars.ts
  • app/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.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • hooks/useCalendars.ts
  • components/external-sync-manage-dialog.tsx
  • app/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.tsx
  • components/password-dialog.tsx
  • components/calendar-dialog.tsx
  • components/manage-password-dialog.tsx
  • hooks/useCalendars.ts
  • app/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.tsx
  • hooks/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.tsx
  • hooks/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.tsx
  • components/preset-selector.tsx
  • components/password-dialog.tsx
  • components/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.tsx
  • components/preset-selector.tsx
  • components/calendar-dialog.tsx
  • components/external-sync-manage-dialog.tsx
  • components/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.tsx
  • components/sync-notification-dialog.tsx
  • components/preset-selector.tsx
  • components/calendar-dialog.tsx
  • .github/copilot-instructions.md
  • app/page.tsx
  • components/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.tsx
  • components/sync-notification-dialog.tsx
  • components/password-dialog.tsx
  • components/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.tsx
  • components/preset-selector.tsx
  • components/calendar-dialog.tsx
  • hooks/useCalendars.ts
  • messages/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.tsx
  • components/calendar-dialog.tsx
  • components/external-sync-manage-dialog.tsx
  • components/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.ts
  • hooks/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.md
  • hooks/useNotes.ts
  • messages/en.json
  • app/page.tsx
  • messages/de.json
  • components/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.tsx
  • components/password-dialog.tsx
  • components/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.tsx
  • hooks/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) and validation.* 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.passwordLabel and form.passwordPlaceholder keys, 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 to t("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 to t("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.passwordIncorrect for error messages, form.passwordLabel for labels, and common.unlock for 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 to t("calendar.title")), and password validation uses validation.passwordIncorrect. This approach eliminates duplicate success/error message strings while maintaining clear, entity-specific user feedback. The calendar.title key 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 centralized form.passwordLabel key. 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 like maxSize.

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, and form namespaces 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 using syncNotifications.title as the item parameter produces clear, user-friendly messages like "Sync Notifications deleted successfully".

Also applies to: 155-157, 161-163, 191-193, 197-199

@panteLx panteLx merged commit acf8b06 into main Dec 7, 2025
8 checks passed
@panteLx panteLx deleted the feat/centralized-i18n branch December 7, 2025 04:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, and form.* 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") }));
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
onPresetsChange();
if (onShiftsChange) onShiftsChange();
toast.success(t("preset.deleted"));
toast.success(t("common.deleted", { item: t("preset.create") }));
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +153
toast.success(
t("common.deleted", { item: t("syncNotifications.title") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +157
toast.error(
t("common.deleteError", { item: t("syncNotifications.title") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
);
setShifts((shifts) => shifts.filter((s) => s.id !== tempId));
toast.error(t("shift.createError"));
toast.error(t("common.createError", { item: t("shift.title") }));
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copilot uses AI. Check for mistakes.
Comment on lines +506 to +508
toast.error(
t("common.updateError", { item: t("externalSync.syncTypeCustom") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +586 to +588
toast.error(
t("common.updateError", { item: t("externalSync.syncTypeCustom") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +163
toast.error(
t("common.deleteError", { item: t("syncNotifications.title") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +191 to +193
toast.error(
t("common.updateError", { item: t("syncNotifications.title") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +400 to +402
toast.success(
t("common.deleted", { item: t("externalSync.syncTypeCustom") })
);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

better usage of message translations. more common uses instead of specific translation for everything

1 participant