feat: overhaul header display options with mobile portrait/landscape settings

- Rename options to be clearer:
  - "Vollständiges Datum" → "Heutiges Datum" (always today's date)
  - "Aktueller Tag" → "Selektierter Tag" with custom format input
  - "Benutzerdefiniert" → "Woche Benutzerdefiniert" (week-based tokens)
- Add custom format input for "Selektierter Tag" (headerCurrentDayFormat)
  with tokens: DDDD, DDD, DD, MMMM, MMM, MM, YYYY, WW
- Add separate header display settings for Mobile Portrait and
  Mobile Landscape (mobilePortraitHeaderDisplay, mobileLandscapeHeaderDisplay)
- Move refresh/sync icon to fixed-width slot right of date — date no
  longer shifts when the icon appears or the spinner replaces it
- Add 3 new Prisma fields (db push required on deploy)
- All 5 languages updated (EN, DE, FR, ES, IT)

v1.81.0
This commit is contained in:
mARTin 2026-03-31 09:14:16 +02:00
parent 033603e899
commit e800d87235
6 changed files with 181 additions and 99 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.80.4", "version": "1.81.0",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -80,7 +80,10 @@ model User {
dateAlignment String @default("center") dateAlignment String @default("center")
dateVerticalAlign String? @default("middle") dateVerticalAlign String? @default("middle")
headerDisplay String? @default("kw") headerDisplay String? @default("kw")
headerCustomFormat String? @default("KW WW | YYYY") headerCustomFormat String? @default("KW WW | YYYY")
headerCurrentDayFormat String? @default("DDD, DD. MMMM YYYY")
mobilePortraitHeaderDisplay String? @default("current_day")
mobileLandscapeHeaderDisplay String? @default("kw")
hourLabelFormat String @default("short") hourLabelFormat String @default("short")
showSubHourSlots Boolean @default(true) showSubHourSlots Boolean @default(true)
allDayPosition String @default("above") allDayPosition String @default("above")

View File

@ -48,6 +48,9 @@ export async function GET(request: NextRequest) {
dateVerticalAlign: true, dateVerticalAlign: true,
headerDisplay: true, headerDisplay: true,
headerCustomFormat: true, headerCustomFormat: true,
headerCurrentDayFormat: true,
mobilePortraitHeaderDisplay: true,
mobileLandscapeHeaderDisplay: true,
headlineFont: true, headlineFont: true,
headlineFontSize: true, headlineFontSize: true,
headlineFontWeight: true, headlineFontWeight: true,
@ -141,7 +144,7 @@ export async function PATCH(request: NextRequest) {
weekdayColor, dateColor, taskColor, todayHighlightColor, weekdayColor, dateColor, taskColor, todayHighlightColor,
pastDayColor, goalFallbackType, goalDefaultSentence, pastDayColor, goalFallbackType, goalDefaultSentence,
goalFontFamily, goalFontSize, goalFontWeight, goalScope, goalFontFamily, goalFontSize, goalFontWeight, goalScope,
dateLayout, mobileDateLayout, dateVerticalAlign, headerDisplay, headerCustomFormat, dateLayout, mobileDateLayout, dateVerticalAlign, headerDisplay, headerCustomFormat, headerCurrentDayFormat, mobilePortraitHeaderDisplay, mobileLandscapeHeaderDisplay,
hourLabelFormat, showSubHourSlots, allDayPosition, hourLabelFormat, showSubHourSlots, allDayPosition,
cwFontFamily, cwFontSize, cwFontWeight, cwColor, cwFontFamily, cwFontSize, cwFontWeight, cwColor,
yearFontFamily, yearFontSize, yearFontWeight, yearColor, yearFontFamily, yearFontSize, yearFontWeight, yearColor,
@ -211,6 +214,9 @@ export async function PATCH(request: NextRequest) {
...(dateVerticalAlign !== undefined && { dateVerticalAlign }), ...(dateVerticalAlign !== undefined && { dateVerticalAlign }),
...(headerDisplay !== undefined && { headerDisplay }), ...(headerDisplay !== undefined && { headerDisplay }),
...(headerCustomFormat !== undefined && { headerCustomFormat }), ...(headerCustomFormat !== undefined && { headerCustomFormat }),
...(headerCurrentDayFormat !== undefined && { headerCurrentDayFormat }),
...(mobilePortraitHeaderDisplay !== undefined && { mobilePortraitHeaderDisplay }),
...(mobileLandscapeHeaderDisplay !== undefined && { mobileLandscapeHeaderDisplay }),
...(hourLabelFormat !== undefined && { hourLabelFormat }), ...(hourLabelFormat !== undefined && { hourLabelFormat }),
...(showSubHourSlots !== undefined && { showSubHourSlots }), ...(showSubHourSlots !== undefined && { showSubHourSlots }),
...(allDayPosition !== undefined && { allDayPosition }), ...(allDayPosition !== undefined && { allDayPosition }),
@ -314,6 +320,9 @@ export async function PATCH(request: NextRequest) {
dateVerticalAlign: true, dateVerticalAlign: true,
headerDisplay: true, headerDisplay: true,
headerCustomFormat: true, headerCustomFormat: true,
headerCurrentDayFormat: true,
mobilePortraitHeaderDisplay: true,
mobileLandscapeHeaderDisplay: true,
cwFontFamily: true, cwFontFamily: true,
cwFontSize: true, cwFontSize: true,
cwFontWeight: true, cwFontWeight: true,

View File

@ -900,6 +900,27 @@ function SettingsSidebar({
<option value="none">{t.headerDisplayNone}</option> <option value="none">{t.headerDisplayNone}</option>
</select> </select>
</div> </div>
{profile.headerDisplay === "current_day" && (
<div className="mt-2">
<label style={{ fontSize: "0.75rem", color: "var(--weekly-settings-label)", display: "block", marginBottom: "4px" }}>
{t.headerCurrentDayFormatLabel}
</label>
<input
type="text"
value={profile.headerCurrentDayFormat || ""}
onChange={(e) => {
const val = e.target.value;
setProfile({ ...profile, headerCurrentDayFormat: val });
saveSetting("headerCurrentDayFormat", val);
}}
placeholder="DDD, DD. MMMM YYYY"
className="w-full px-3 py-1.5 text-sm rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
/>
<div className="mt-1 text-[10px] text-gray-500 leading-tight">
Tokens: DDDD (Montag), DDD (Mo.), DD (30), MMMM (März), MMM (Mär), MM (03), YYYY (2026)
</div>
</div>
)}
{profile.headerDisplay === "custom" && ( {profile.headerDisplay === "custom" && (
<div className="mt-2"> <div className="mt-2">
<label style={{ fontSize: "0.75rem", color: "var(--weekly-settings-label)", display: "block", marginBottom: "4px" }}> <label style={{ fontSize: "0.75rem", color: "var(--weekly-settings-label)", display: "block", marginBottom: "4px" }}>
@ -913,14 +934,61 @@ function SettingsSidebar({
setProfile({ ...profile, headerCustomFormat: val }); setProfile({ ...profile, headerCustomFormat: val });
saveSetting("headerCustomFormat", val); saveSetting("headerCustomFormat", val);
}} }}
placeholder="KW WW | YYYY or DD.MM.YYYY" placeholder="KW WW | YYYY"
className="w-full px-3 py-1.5 text-sm rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white" className="w-full px-3 py-1.5 text-sm rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
/> />
<div className="mt-1 text-[10px] text-gray-500 leading-tight"> <div className="mt-1 text-[10px] text-gray-500 leading-tight">
Tokens: WW (Week), YYYY (Year), MMMM (Month Name), MM (Month Num), DD (Day), [TODAY] (Active Date) Tokens: WW (KW), YYYY (Jahr), MMMM (März), MM (03), DD (30), [TODAY] (Heute)
</div> </div>
</div> </div>
)} )}
{/* Mobile Portrait/Landscape overrides */}
<div className="mt-3 grid grid-cols-2 gap-2">
<div>
<label style={{ fontSize: "0.75rem", color: "var(--weekly-settings-label)", display: "block", marginBottom: "4px" }}>
{t.headerMobilePortrait}
</label>
<select
value={profile.mobilePortraitHeaderDisplay || "current_day"}
onChange={(e) => {
const val = e.target.value as any;
setProfile({ ...profile, mobilePortraitHeaderDisplay: val });
saveSetting("mobilePortraitHeaderDisplay", val);
}}
className="weekly-input w-full p-1.5 text-xs border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white rounded"
>
<option value="kw">{t.headerDisplayKW}</option>
<option value="month">{t.headerDisplayMonth}</option>
<option value="month_year">{t.headerDisplayMonthYear}</option>
<option value="date">{t.headerDisplayDate}</option>
<option value="current_day">{t.headerDisplayCurrentDay}</option>
<option value="custom">{t.headerDisplayCustom}</option>
<option value="none">{t.headerDisplayNone}</option>
</select>
</div>
<div>
<label style={{ fontSize: "0.75rem", color: "var(--weekly-settings-label)", display: "block", marginBottom: "4px" }}>
{t.headerMobileLandscape}
</label>
<select
value={profile.mobileLandscapeHeaderDisplay || "kw"}
onChange={(e) => {
const val = e.target.value as any;
setProfile({ ...profile, mobileLandscapeHeaderDisplay: val });
saveSetting("mobileLandscapeHeaderDisplay", val);
}}
className="weekly-input w-full p-1.5 text-xs border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white rounded"
>
<option value="kw">{t.headerDisplayKW}</option>
<option value="month">{t.headerDisplayMonth}</option>
<option value="month_year">{t.headerDisplayMonthYear}</option>
<option value="date">{t.headerDisplayDate}</option>
<option value="current_day">{t.headerDisplayCurrentDay}</option>
<option value="custom">{t.headerDisplayCustom}</option>
<option value="none">{t.headerDisplayNone}</option>
</select>
</div>
</div>
</div> </div>
{/* Push Notifications */} {/* Push Notifications */}

View File

@ -380,9 +380,9 @@ function getCWReferenceDate(days: Date[]): Date {
} }
// Format a custom header string using tokens // Format a custom header string using tokens
function formatCustomHeader(format: string, days: Date[], language: string, t: any): string { function formatCustomHeader(format: string, days: Date[], language: string, t: any, refDateOverride?: Date): string {
if (!format) return ""; if (!format) return "";
// Choose the reference date: if today is within the visible days, use today. // Choose the reference date: if today is within the visible days, use today.
// Otherwise, use the standard CW reference date (start of week). // Otherwise, use the standard CW reference date (start of week).
const today = new Date(); const today = new Date();
@ -391,7 +391,7 @@ function formatCustomHeader(format: string, days: Date[], language: string, t: a
d.getMonth() === today.getMonth() && d.getMonth() === today.getMonth() &&
d.getFullYear() === today.getFullYear() d.getFullYear() === today.getFullYear()
); );
const refDate = isTodayInWeek ? today : getCWReferenceDate(days); const refDate = refDateOverride ?? (isTodayInWeek ? today : getCWReferenceDate(days));
// Define token mappings // Define token mappings
const tokens: Record<string, string> = { const tokens: Record<string, string> = {
@ -415,6 +415,16 @@ function formatCustomHeader(format: string, days: Date[], language: string, t: a
return format.replace(regex, (match) => tokens[match] || match); return format.replace(regex, (match) => tokens[match] || match);
} }
// Get the "selected" day for current_day header: today if visible, otherwise first visible day
function getSelectedDay(days: Date[]): Date {
const today = new Date();
return days.some(d =>
d.getDate() === today.getDate() &&
d.getMonth() === today.getMonth() &&
d.getFullYear() === today.getFullYear()
) ? today : days[0];
}
// Check if an event is an all-day event // Check if an event is an all-day event
// Defined outside component to avoid stale closure issues in useCallbacks // Defined outside component to avoid stale closure issues in useCallbacks
const isAllDayEvent = (event: CalendarEvent): boolean => { const isAllDayEvent = (event: CalendarEvent): boolean => {
@ -5603,33 +5613,22 @@ export default function WeeklyView() {
)} )}
{syncError && <AlertCircle size={14} className="text-red-500" />} {syncError && <AlertCircle size={14} className="text-red-500" />}
<span> <span>
{isPortrait ? (() => { {(() => {
const shownDay = getVisibleDays()[0]; const visibleDays = getVisibleDays();
return shownDay.toLocaleDateString(profile.language || "de-DE", { weekday: "short", day: "2-digit", month: "long", year: "numeric" }); const mobileDisplay = isPortrait
})() : ? (profile.mobilePortraitHeaderDisplay || "current_day")
profile.headerDisplay === "none" ? "" : : (profile.mobileLandscapeHeaderDisplay || profile.headerDisplay || "kw");
profile.headerDisplay === "current_day" ? (() => { if (mobileDisplay === "none") return "";
const shownDay = getVisibleDays()[0]; if (mobileDisplay === "current_day") {
return shownDay.toLocaleDateString(profile.language || "de-DE", { weekday: "short", day: "2-digit", month: "long", year: "numeric" }); const fmt = profile.headerCurrentDayFormat || "DDD, DD. MMMM YYYY";
})() : return formatCustomHeader(fmt, visibleDays, profile.language, t, getSelectedDay(visibleDays));
profile.headerDisplay === "date" ? (() => { }
const today = new Date(); if (mobileDisplay === "date") return new Date().toLocaleDateString(profile.language, { day: '2-digit', month: '2-digit', year: 'numeric' });
const isTodayInWeek = getVisibleDays().some(d => if (mobileDisplay === "month_year") return getCWReferenceDate(visibleDays).toLocaleDateString(profile.language, { month: 'long', year: 'numeric' });
d.getDate() === today.getDate() && if (mobileDisplay === "month") return getCWReferenceDate(visibleDays).toLocaleDateString(profile.language, { month: "long" });
d.getMonth() === today.getMonth() && if (mobileDisplay === "custom") return formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", visibleDays, profile.language, t);
d.getFullYear() === today.getFullYear() return `KW ${getWeekNumber(getCWReferenceDate(visibleDays)).toString().padStart(2, "0")} | ${getCWReferenceDate(visibleDays).getFullYear()}`;
); })()}
const refDate = isTodayInWeek ? today : getCWReferenceDate(getVisibleDays());
return refDate.toLocaleDateString(profile.language, { day: '2-digit', month: '2-digit', year: 'numeric' });
})() :
profile.headerDisplay === "month_year" ?
getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: 'long', year: 'numeric' }) :
profile.headerDisplay === "custom" ?
formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), profile.language, t) :
profile.headerDisplay === "month" ?
getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: "long" }) :
`KW ${getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")} | ${getCWReferenceDate(getVisibleDays()).getFullYear()}`
}
</span> </span>
<ChevronDown size={12} style={{ opacity: 0.5 }} /> <ChevronDown size={12} style={{ opacity: 0.5 }} />
</button> </button>
@ -5763,35 +5762,21 @@ export default function WeeklyView() {
filter: "brightness(var(--weekly-header-brightness, 1))" filter: "brightness(var(--weekly-header-brightness, 1))"
}}> }}>
{(() => { {(() => {
const effectiveDisplay = profile.viewStyle === "kanban" && !profile.headerDisplay ? "current_day" : (profile.headerDisplay || "kw"); const effectiveDisplay = (profile.viewStyle === "kanban" && !profile.headerDisplay) ? "current_day" : (profile.headerDisplay || "kw");
const visibleDays = getVisibleDays();
if (effectiveDisplay === "none") return ""; if (effectiveDisplay === "none") return "";
if (effectiveDisplay === "current_day") { if (effectiveDisplay === "current_day") {
const today = new Date(); const fmt = profile.headerCurrentDayFormat || "DDD, DD. MMMM YYYY";
const visibleDays = getVisibleDays(); return formatCustomHeader(fmt, visibleDays, profile.language, t, getSelectedDay(visibleDays));
const shownDay = visibleDays.some(d =>
d.getDate() === today.getDate() &&
d.getMonth() === today.getMonth() &&
d.getFullYear() === today.getFullYear()
) ? today : visibleDays[0];
return shownDay.toLocaleDateString(profile.language || "de-DE", { weekday: "short", day: "2-digit", month: "long", year: "numeric" });
} }
if (effectiveDisplay === "month") return getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: "long" }); if (effectiveDisplay === "month") return getCWReferenceDate(visibleDays).toLocaleDateString(profile.language, { month: "long" });
if (effectiveDisplay === "month_year") return getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: "long", year: "numeric" }); if (effectiveDisplay === "month_year") return getCWReferenceDate(visibleDays).toLocaleDateString(profile.language, { month: "long", year: "numeric" });
if (effectiveDisplay === "date") { if (effectiveDisplay === "date") return new Date().toLocaleDateString(profile.language, { day: '2-digit', month: '2-digit', year: 'numeric' });
const today = new Date(); if (effectiveDisplay === "custom") return formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", visibleDays, profile.language, t);
const isTodayInWeek = getVisibleDays().some(d => return `KW ${getWeekNumber(getCWReferenceDate(visibleDays)).toString().padStart(2, "0")}`;
d.getDate() === today.getDate() &&
d.getMonth() === today.getMonth() &&
d.getFullYear() === today.getFullYear()
);
const refDate = isTodayInWeek ? today : getCWReferenceDate(getVisibleDays());
return refDate.toLocaleDateString(profile.language, { day: '2-digit', month: '2-digit', year: 'numeric' });
}
if (effectiveDisplay === "custom") return formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), profile.language, t);
return `KW ${getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")}`;
})()} })()}
</span> </span>
{profile.headerDisplay !== "none" && profile.headerDisplay !== "current_day" && (profile.headerDisplay === "kw" || profile.headerDisplay === "month" || !profile.headerDisplay) && ( {profile.headerDisplay !== "none" && profile.headerDisplay !== "current_day" && profile.headerDisplay !== "date" && profile.headerDisplay !== "month_year" && (profile.headerDisplay === "kw" || profile.headerDisplay === "month" || !profile.headerDisplay) && (
<> <>
<span className="text-gray-400">|</span> <span className="text-gray-400">|</span>
<span <span
@ -5808,26 +5793,28 @@ export default function WeeklyView() {
</> </>
)} )}
</div> </div>
{/* Refresh / sync status — fixed-width slot so the date never shifts */}
<div style={{ width: 28, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
{syncError ? (
<div title={syncError} className="flex items-center text-red-500">
<AlertCircle size={14} />
</div>
) : (isLoading || isSyncing || syncStatus === "syncing") ? (
<div className="weekly-spinner" title="Syncing..." style={{ width: 14, height: 14 }}></div>
) : (
<button
onClick={() => { fetchCalendarEvents(true); fetchTasks(); }}
className="p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-all text-gray-400 hover:text-gray-600 opacity-0 group-hover:opacity-100"
title="Refresh Calendar & Tasks"
>
<RefreshCcw size={14} />
</button>
)}
</div>
</div> </div>
{/* Goal — hidden on tablet to save space */} {/* Goal — hidden on tablet to save space */}
<div className="header-desktop-only items-center text-sm"> <div className="header-desktop-only items-center text-sm">
{syncError ? (
<div className="flex items-center gap-1 text-red-500 mr-2" title={syncError}>
<AlertCircle size={14} />
<span className="text-xs">{syncError}</span>
</div>
) : (isLoading || isSyncing || syncStatus === "syncing") ? (
<div className="weekly-spinner mr-2" title="Syncing..."></div>
) : (
<button
onClick={() => { fetchCalendarEvents(true); fetchTasks(); }}
className="p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-all text-gray-400 hover:text-gray-600 opacity-0 group-hover:opacity-100 mr-1"
title="Refresh Calendar & Tasks"
>
<RefreshCcw size={14} />
</button>
)}
{isEditingGoal ? ( {isEditingGoal ? (
<input <input
type="text" type="text"

View File

@ -27,13 +27,16 @@ export const translations: Record<string, any> = {
noStage: "No stage", noStage: "No stage",
headerDisplay: "Header Display", headerDisplay: "Header Display",
headerDisplayKW: "Calendar Week (KW)", headerDisplayKW: "Calendar Week (KW)",
headerDisplayMonth: "Month Name - March", headerDisplayMonth: "Month Name March",
headerDisplayMonthYear: "Month & Year - March | 2026", headerDisplayMonthYear: "Month & Year March | 2026",
headerDisplayDate: "Full Date - 13.03.2026", headerDisplayDate: "Today's Date — 30.03.2026",
headerDisplayCustom: "Custom - Friday - 13. March", headerDisplayCurrentDay: "Selected Day — Mo., 30. March 2026",
headerDisplayCurrentDay: "Current Day - Friday, 13. March 2026", headerDisplayCustom: "Custom Week Format",
headerDisplayNone: "None", headerDisplayNone: "None",
headerCustomFormatLabel: "Format string (e.g. DD.MM.YYYY)", headerCustomFormatLabel: "Week format (e.g. KW WW | YYYY)",
headerCurrentDayFormatLabel: "Day format (e.g. DDD, DD. MMMM YYYY)",
headerMobilePortrait: "Mobile Portrait",
headerMobileLandscape: "Mobile Landscape",
language: "Language", language: "Language",
dateFormat: "Date Format", dateFormat: "Date Format",
timeFormat: "Time Format", timeFormat: "Time Format",
@ -270,13 +273,16 @@ export const translations: Record<string, any> = {
noStage: "Keine Phase", noStage: "Keine Phase",
headerDisplay: "Kopfzeile", headerDisplay: "Kopfzeile",
headerDisplayKW: "Kalenderwoche (KW)", headerDisplayKW: "Kalenderwoche (KW)",
headerDisplayMonth: "Monatsname - März", headerDisplayMonth: "Monatsname März",
headerDisplayMonthYear: "Monat & Jahr - März | 2026", headerDisplayMonthYear: "Monat & Jahr März | 2026",
headerDisplayDate: "Vollständiges Datum - 13.03.2026", headerDisplayDate: "Heutiges Datum — 30.03.2026",
headerDisplayCustom: "Benutzerdefiniert - Freitag - 13. März", headerDisplayCurrentDay: "Selektierter Tag — Mo., 30. März 2026",
headerDisplayCurrentDay: "Aktueller Tag - Freitag, 13. März 2026", headerDisplayCustom: "Woche Benutzerdefiniert",
headerDisplayNone: "Nichts", headerDisplayNone: "Nichts",
headerCustomFormatLabel: "Format (z.B. DD.MM.YYYY)", headerCustomFormatLabel: "Wochenformat (z.B. KW WW | YYYY)",
headerCurrentDayFormatLabel: "Tagesformat (z.B. DDD, DD. MMMM YYYY)",
headerMobilePortrait: "Mobile Hochformat",
headerMobileLandscape: "Mobile Querformat",
listView: "Liste", listView: "Liste",
notes: "Notizen", notes: "Notizen",
notesSidebar: "Notizen-Seitenleiste", notesSidebar: "Notizen-Seitenleiste",
@ -517,11 +523,14 @@ export const translations: Record<string, any> = {
headerDisplayKW: "Semaine calendaire (KW)", headerDisplayKW: "Semaine calendaire (KW)",
headerDisplayMonth: "Nom du mois - Mars", headerDisplayMonth: "Nom du mois - Mars",
headerDisplayMonthYear: "Mois & Année - Mars | 2026", headerDisplayMonthYear: "Mois & Année - Mars | 2026",
headerDisplayDate: "Date complète - 13.03.2026", headerDisplayDate: "Date du jour — 30.03.2026",
headerDisplayCustom: "Personnalisé - Vendredi - 13 Mars", headerDisplayCurrentDay: "Jour sélectionné — Lu., 30 Mars 2026",
headerDisplayCurrentDay: "Jour actuel - Vendredi, 13 Mars 2026", headerDisplayCustom: "Semaine personnalisée",
headerDisplayNone: "Aucun", headerDisplayNone: "Aucun",
headerCustomFormatLabel: "Format (ex: DD.MM.YYYY)", headerCustomFormatLabel: "Format semaine (ex: KW WW | YYYY)",
headerCurrentDayFormatLabel: "Format jour (ex: DDD, DD. MMMM YYYY)",
headerMobilePortrait: "Mobile Portrait",
headerMobileLandscape: "Mobile Paysage",
language: "Langue", language: "Langue",
dateFormat: "Format de date", dateFormat: "Format de date",
timeFormat: "Format d'heure", timeFormat: "Format d'heure",
@ -735,11 +744,14 @@ export const translations: Record<string, any> = {
headerDisplayKW: "Semana calendario (KW)", headerDisplayKW: "Semana calendario (KW)",
headerDisplayMonth: "Nombre del mes - Marzo", headerDisplayMonth: "Nombre del mes - Marzo",
headerDisplayMonthYear: "Mes y Año - Marzo | 2026", headerDisplayMonthYear: "Mes y Año - Marzo | 2026",
headerDisplayDate: "Fecha completa - 13.03.2026", headerDisplayDate: "Fecha de hoy — 30.03.2026",
headerDisplayCustom: "Personalizado - Viernes - 13 Marzo", headerDisplayCurrentDay: "Día seleccionado — Lu., 30 Marzo 2026",
headerDisplayCurrentDay: "Día actual - Viernes, 13 Marzo 2026", headerDisplayCustom: "Semana personalizada",
headerDisplayNone: "Ninguno", headerDisplayNone: "Ninguno",
headerCustomFormatLabel: "Formato (ej. DD.MM.YYYY)", headerCustomFormatLabel: "Formato semana (ej. KW WW | YYYY)",
headerCurrentDayFormatLabel: "Formato día (ej. DDD, DD. MMMM YYYY)",
headerMobilePortrait: "Móvil Retrato",
headerMobileLandscape: "Móvil Paisaje",
language: "Idioma", language: "Idioma",
dateFormat: "Formato de fecha", dateFormat: "Formato de fecha",
timeFormat: "Formato de hora", timeFormat: "Formato de hora",
@ -953,11 +965,14 @@ export const translations: Record<string, any> = {
headerDisplayKW: "Settimana calendario (KW)", headerDisplayKW: "Settimana calendario (KW)",
headerDisplayMonth: "Nome del mese - Marzo", headerDisplayMonth: "Nome del mese - Marzo",
headerDisplayMonthYear: "Mese e Anno - Marzo | 2026", headerDisplayMonthYear: "Mese e Anno - Marzo | 2026",
headerDisplayDate: "Data completa - 13.03.2026", headerDisplayDate: "Data di oggi — 30.03.2026",
headerDisplayCustom: "Personalizzato - Venerdì - 13 Marzo", headerDisplayCurrentDay: "Giorno selezionato — Lu., 30 Marzo 2026",
headerDisplayCurrentDay: "Giorno corrente - Venerdì, 13 Marzo 2026", headerDisplayCustom: "Settimana personalizzata",
headerDisplayNone: "Nessuno", headerDisplayNone: "Nessuno",
headerCustomFormatLabel: "Formato (es. DD.MM.YYYY)", headerCustomFormatLabel: "Formato settimana (es. KW WW | YYYY)",
headerCurrentDayFormatLabel: "Formato giorno (es. DDD, DD. MMMM YYYY)",
headerMobilePortrait: "Mobile Verticale",
headerMobileLandscape: "Mobile Orizzontale",
language: "Lingua", language: "Lingua",
dateFormat: "Formato data", dateFormat: "Formato data",
timeFormat: "Formato ora", timeFormat: "Formato ora",