mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 22:19:40 +02:00
Projects included: - maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing) - manacore (Expo mobile + SvelteKit web + Astro landing) - manadeck (NestJS backend + Expo mobile + SvelteKit web) - memoro (Expo mobile + SvelteKit web + Astro landing) This commit preserves the current state before monorepo restructuring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
217 lines
No EOL
6.3 KiB
TypeScript
217 lines
No EOL
6.3 KiB
TypeScript
/**
|
|
* Consolidated formatting utilities
|
|
* Combines and replaces duplicate formatting functions across the app
|
|
*/
|
|
|
|
import { TIME_SEC } from './sharedConstants';
|
|
|
|
/**
|
|
* Format duration in seconds to MM:SS or HH:MM:SS format
|
|
* Replaces multiple duplicate formatTime functions
|
|
*/
|
|
export const formatDuration = (seconds: number): string => {
|
|
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
return '--:--';
|
|
}
|
|
|
|
const hours = Math.floor(seconds / TIME_SEC.HOUR);
|
|
const minutes = Math.floor((seconds % TIME_SEC.HOUR) / TIME_SEC.MINUTE);
|
|
const remainingSeconds = Math.floor(seconds % TIME_SEC.MINUTE);
|
|
|
|
if (hours > 0) {
|
|
return `${hours}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|
}
|
|
|
|
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|
};
|
|
|
|
/**
|
|
* Format duration in seconds with units (for display)
|
|
* e.g., "2 min 30 sec" or "1h 23m"
|
|
*/
|
|
export const formatDurationWithUnits = (seconds: number): string => {
|
|
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
return 'keine Zeit';
|
|
}
|
|
|
|
const hours = Math.floor(seconds / TIME_SEC.HOUR);
|
|
const minutes = Math.floor((seconds % TIME_SEC.HOUR) / TIME_SEC.MINUTE);
|
|
const remainingSeconds = Math.floor(seconds % TIME_SEC.MINUTE);
|
|
|
|
if (hours > 0) {
|
|
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
}
|
|
|
|
if (minutes > 0) {
|
|
return remainingSeconds > 0 && minutes < 5 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
|
|
}
|
|
|
|
return `${remainingSeconds}s`;
|
|
};
|
|
|
|
/**
|
|
* Format duration in seconds to human readable German text
|
|
* e.g., "1 Stunde 23 Minuten"
|
|
*/
|
|
export const formatDurationHumanReadable = (seconds: number): string => {
|
|
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
return 'keine Zeit';
|
|
}
|
|
|
|
const hours = Math.floor(seconds / TIME_SEC.HOUR);
|
|
const minutes = Math.floor((seconds % TIME_SEC.HOUR) / TIME_SEC.MINUTE);
|
|
const remainingSeconds = Math.floor(seconds % TIME_SEC.MINUTE);
|
|
|
|
const parts = [];
|
|
|
|
if (hours > 0) {
|
|
parts.push(`${hours} ${hours === 1 ? 'Stunde' : 'Stunden'}`);
|
|
}
|
|
|
|
if (minutes > 0) {
|
|
parts.push(`${minutes} ${minutes === 1 ? 'Minute' : 'Minuten'}`);
|
|
}
|
|
|
|
if (remainingSeconds > 0 && hours === 0) {
|
|
parts.push(`${remainingSeconds} ${remainingSeconds === 1 ? 'Sekunde' : 'Sekunden'}`);
|
|
}
|
|
|
|
if (parts.length === 0) {
|
|
return '0 Sekunden';
|
|
}
|
|
|
|
return parts.join(' ');
|
|
};
|
|
|
|
/**
|
|
* Format duration from milliseconds
|
|
*/
|
|
export const formatDurationFromMs = (milliseconds: number): string => {
|
|
return formatDuration(milliseconds / 1000);
|
|
};
|
|
|
|
/**
|
|
* Format Date object to localized date string
|
|
* @param date - Date to format
|
|
* @param locale - Optional locale string (e.g., 'en-US', 'de-DE')
|
|
* @param options - Optional Intl.DateTimeFormatOptions
|
|
*/
|
|
export const formatDate = (date: Date, locale?: string, options?: Intl.DateTimeFormatOptions): string => {
|
|
// Default options for a nice full date format
|
|
const defaultOptions: Intl.DateTimeFormatOptions = {
|
|
weekday: 'short',
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric',
|
|
};
|
|
|
|
return date.toLocaleDateString(locale || undefined, options || defaultOptions);
|
|
};
|
|
|
|
/**
|
|
* Format Date object to localized time string
|
|
* @param date - Date to format
|
|
* @param locale - Optional locale string (e.g., 'en-US', 'de-DE')
|
|
* @param includeUhr - Whether to append "Uhr" for German locales
|
|
*/
|
|
export const formatTime = (date: Date, locale?: string, includeUhr: boolean = true): string => {
|
|
// Determine if we should use 12-hour format based on locale
|
|
const langCode = locale ? locale.split('-')[0] : '';
|
|
const is12Hour = ['en', 'hi', 'ur', 'tl', 'ms'].includes(langCode);
|
|
|
|
const timeString = date.toLocaleTimeString(locale || undefined, {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hour12: is12Hour || false,
|
|
});
|
|
|
|
// Add language-specific formatting
|
|
if (includeUhr && locale) {
|
|
const langCode = locale.split('-')[0];
|
|
|
|
// Germanic languages
|
|
if (langCode === 'de') return `${timeString} Uhr`;
|
|
if (langCode === 'nl') return `${timeString} uur`;
|
|
if (langCode === 'da') return `kl. ${timeString.replace(':', '.')}`;
|
|
if (langCode === 'sv') return `kl. ${timeString}`;
|
|
|
|
// Romance languages
|
|
if (langCode === 'fr') return timeString.replace(':', 'h');
|
|
|
|
// Slavic languages
|
|
if (langCode === 'bg') return `${timeString} ч.`;
|
|
|
|
// Baltic languages
|
|
if (langCode === 'lt') return `${timeString} val.`;
|
|
|
|
// Finno-Ugric languages
|
|
if (langCode === 'fi') return `klo ${timeString.replace(':', '.')}`;
|
|
|
|
// Asian languages with suffixes
|
|
if (langCode === 'th') return `${timeString} น.`;
|
|
|
|
// Languages with dot separator
|
|
if (langCode === 'id') return timeString.replace(':', '.');
|
|
|
|
// Special handling for 12h languages that need suffixes
|
|
if (langCode === 'ms' && is12Hour) {
|
|
// Malay uses PG (pagi/morning) and PTG (petang/evening)
|
|
const hour = date.getHours();
|
|
const suffix = hour < 12 ? 'PG' : 'PTG';
|
|
return timeString.replace(/[AP]M/i, suffix);
|
|
}
|
|
}
|
|
|
|
return timeString;
|
|
};
|
|
|
|
/**
|
|
* Format ISO date string to localized date
|
|
* Replaces inline formatDate functions in components
|
|
*/
|
|
export const formatDateFromString = (dateString: string): string => {
|
|
try {
|
|
const date = new Date(dateString);
|
|
return formatDate(date);
|
|
} catch {
|
|
return dateString;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Format ISO date string with time and optional duration
|
|
* Used in memo displays
|
|
*/
|
|
export const formatDateTimeForAudio = (dateString: string, durationSeconds?: number): string => {
|
|
try {
|
|
const date = new Date(dateString);
|
|
const formattedDate = date.toLocaleDateString('de-DE');
|
|
const formattedTime = date.toLocaleTimeString('de-DE', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
});
|
|
|
|
let result = `${formattedDate}, ${formattedTime}`;
|
|
|
|
if (durationSeconds && durationSeconds > 0) {
|
|
result += ` • ${formatDurationWithUnits(durationSeconds)}`;
|
|
}
|
|
|
|
return result;
|
|
} catch {
|
|
return dateString;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Simple duration formatter for short durations (used in statistics)
|
|
* Returns "X min" or "X sec" format
|
|
*/
|
|
export const formatSimpleDuration = (durationInSeconds: number): string => {
|
|
if (durationInSeconds < 60) {
|
|
const seconds = Math.ceil(durationInSeconds);
|
|
return `${seconds} sec`;
|
|
}
|
|
const minutes = Math.ceil(durationInSeconds / 60);
|
|
return `${minutes} min`;
|
|
}; |