mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
localize dates (#1237)
This commit is contained in:
@@ -267,6 +267,12 @@
|
|||||||
"trackNumber": "track",
|
"trackNumber": "track",
|
||||||
"explicitStatus": "$t(common.explicitStatus)"
|
"explicitStatus": "$t(common.explicitStatus)"
|
||||||
},
|
},
|
||||||
|
"datetime": {
|
||||||
|
"minuteShort": "min",
|
||||||
|
"secondShort": "sec",
|
||||||
|
"hourShort": "hr",
|
||||||
|
"dayShort": "day"
|
||||||
|
},
|
||||||
"filterOperator": {
|
"filterOperator": {
|
||||||
"after": "is after",
|
"after": "is after",
|
||||||
"afterDate": "is after (date)",
|
"afterDate": "is after (date)",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { usePlayButtonClick } from '/@/renderer/features/shared/hooks/use-play-b
|
|||||||
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useCurrentServer, useCurrentServerId, usePermissions } from '/@/renderer/store';
|
import { useCurrentServer, useCurrentServerId, usePermissions } from '/@/renderer/store';
|
||||||
import { formatDurationStringShort } from '/@/renderer/utils';
|
import { formatDurationString } from '/@/renderer/utils';
|
||||||
import { Accordion } from '/@/shared/components/accordion/accordion';
|
import { Accordion } from '/@/shared/components/accordion/accordion';
|
||||||
import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon';
|
||||||
import { ButtonProps } from '/@/shared/components/button/button';
|
import { ButtonProps } from '/@/shared/components/button/button';
|
||||||
@@ -194,7 +194,7 @@ const PlaylistRowButton = memo(({ item, name, onContextMenu, to }: PlaylistRowBu
|
|||||||
<div className={styles.metadataGroupItem}>
|
<div className={styles.metadataGroupItem}>
|
||||||
<Icon color="muted" icon="duration" size="sm" />
|
<Icon color="muted" icon="duration" size="sm" />
|
||||||
<Text isMuted size="sm">
|
<Text isMuted size="sm">
|
||||||
{formatDurationStringShort(item.duration ?? 0)}
|
{formatDurationString(item.duration ?? 0)}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
{item.ownerId === permissions.userId && Boolean(item.public) && (
|
{item.ownerId === permissions.userId && Boolean(item.public) && (
|
||||||
|
|||||||
@@ -1,54 +1,118 @@
|
|||||||
import type { Album, AlbumArtist, Song } from '/@/shared/types/domain-types';
|
import type { Album, AlbumArtist, Song } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import 'dayjs/locale/ar';
|
||||||
|
import 'dayjs/locale/ca';
|
||||||
|
import 'dayjs/locale/cs';
|
||||||
|
import 'dayjs/locale/de';
|
||||||
|
import 'dayjs/locale/en';
|
||||||
|
import 'dayjs/locale/es';
|
||||||
|
import 'dayjs/locale/eu';
|
||||||
|
import 'dayjs/locale/fa';
|
||||||
|
import 'dayjs/locale/fi';
|
||||||
|
import 'dayjs/locale/fr';
|
||||||
|
import 'dayjs/locale/hu';
|
||||||
|
import 'dayjs/locale/id';
|
||||||
|
import 'dayjs/locale/it';
|
||||||
|
import 'dayjs/locale/ja';
|
||||||
|
import 'dayjs/locale/ko';
|
||||||
|
import 'dayjs/locale/nb';
|
||||||
|
import 'dayjs/locale/nl';
|
||||||
|
import 'dayjs/locale/pl';
|
||||||
|
import 'dayjs/locale/pt';
|
||||||
|
import 'dayjs/locale/pt-br';
|
||||||
|
import 'dayjs/locale/ru';
|
||||||
|
import 'dayjs/locale/sl';
|
||||||
|
import 'dayjs/locale/sr';
|
||||||
|
import 'dayjs/locale/sv';
|
||||||
|
import 'dayjs/locale/ta';
|
||||||
|
import 'dayjs/locale/tr';
|
||||||
|
import 'dayjs/locale/zh-cn';
|
||||||
|
import 'dayjs/locale/zh-tw';
|
||||||
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import utc from 'dayjs/plugin/utc';
|
import utc from 'dayjs/plugin/utc';
|
||||||
import formatDuration from 'format-duration';
|
import formatDuration from 'format-duration';
|
||||||
|
|
||||||
|
import i18n from '/@/i18n/i18n';
|
||||||
import { Rating } from '/@/shared/components/rating/rating';
|
import { Rating } from '/@/shared/components/rating/rating';
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(localizedFormat);
|
||||||
|
|
||||||
const FORMATS: Record<number, string> = Object.freeze({
|
const getDayjsLocale = (i18nLang: string): string => {
|
||||||
0: 'YYYY',
|
const localeMap: Record<string, string> = {
|
||||||
1: 'MMM YYYY',
|
ar: 'ar',
|
||||||
2: 'MMM D, YYYY',
|
ca: 'ca',
|
||||||
});
|
cs: 'cs',
|
||||||
|
de: 'de',
|
||||||
|
en: 'en',
|
||||||
|
es: 'es',
|
||||||
|
eu: 'eu',
|
||||||
|
fa: 'fa',
|
||||||
|
fi: 'fi',
|
||||||
|
fr: 'fr',
|
||||||
|
hu: 'hu',
|
||||||
|
id: 'id',
|
||||||
|
it: 'it',
|
||||||
|
ja: 'ja',
|
||||||
|
ko: 'ko',
|
||||||
|
'nb-NO': 'nb',
|
||||||
|
nl: 'nl',
|
||||||
|
pl: 'pl',
|
||||||
|
pt: 'pt',
|
||||||
|
'pt-BR': 'pt-br',
|
||||||
|
ru: 'ru',
|
||||||
|
sl: 'sl',
|
||||||
|
sr: 'sr',
|
||||||
|
sv: 'sv',
|
||||||
|
ta: 'ta',
|
||||||
|
tr: 'tr',
|
||||||
|
'zh-Hans': 'zh-cn',
|
||||||
|
'zh-Hant': 'zh-tw',
|
||||||
|
};
|
||||||
|
|
||||||
const getDateFormat = (key: string): string => {
|
return localeMap[i18nLang] || 'en';
|
||||||
const dashes = Math.min(key.split('-').length - 1, 2);
|
|
||||||
return FORMATS[dashes];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatDateAbsolute = (key: null | string) =>
|
const updateDayjsLocale = () => {
|
||||||
key ? dayjs(key).format(getDateFormat(key)) : '';
|
const dayjsLocale = getDayjsLocale(i18n.language);
|
||||||
|
dayjs.locale(dayjsLocale);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set initial locale
|
||||||
|
updateDayjsLocale();
|
||||||
|
|
||||||
|
// Listen for i18n language changes
|
||||||
|
i18n.on('languageChanged', updateDayjsLocale);
|
||||||
|
|
||||||
|
export const formatDateAbsolute = (key: null | string) => (key ? dayjs(key).format('LL') : '');
|
||||||
|
|
||||||
export const formatDateAbsoluteUTC = (key: null | string) =>
|
export const formatDateAbsoluteUTC = (key: null | string) =>
|
||||||
key ? dayjs.utc(key).format(getDateFormat(key)) : '';
|
key ? dayjs.utc(key).format('LL') : '';
|
||||||
|
|
||||||
export const formatHrDateTime = (key: null | string) =>
|
export const formatHrDateTime = (key: null | string) => (key ? dayjs(key).format('LLL') : '');
|
||||||
key ? dayjs(key).format('YYYY-MM-DD HH:mm') : '';
|
|
||||||
|
|
||||||
export const formatDateRelative = (key: null | string) => (key ? dayjs(key).fromNow() : '');
|
export const formatDateRelative = (key: null | string) => (key ? dayjs(key).fromNow() : '');
|
||||||
|
|
||||||
export const formatDurationString = (duration: number) => {
|
export const formatDurationString = (duration: number) => {
|
||||||
const rawDuration = formatDuration(duration).split(':');
|
const rawDuration = formatDuration(duration, { leading: false }).split(':');
|
||||||
|
|
||||||
let string;
|
let string;
|
||||||
|
|
||||||
switch (rawDuration.length) {
|
switch (rawDuration.length) {
|
||||||
case 1:
|
case 1:
|
||||||
string = `${rawDuration[0]} sec`;
|
string = `${rawDuration[0]} ${i18n.t('datetime.secondShort')}`;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
string = `${rawDuration[0]} min ${rawDuration[1]} sec`;
|
string = `${rawDuration[0]} ${i18n.t('datetime.minuteShort')} ${rawDuration[1]} ${i18n.t('datetime.secondShort')}`;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
string = `${rawDuration[0]} hr ${rawDuration[1]} min ${rawDuration[2]} sec`;
|
string = `${rawDuration[0]} ${i18n.t('datetime.hourShort')} ${rawDuration[1]} ${i18n.t('datetime.minuteShort')} ${rawDuration[2]} ${i18n.t('datetime.secondShort')}`;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
string = `${rawDuration[0]} day ${rawDuration[1]} hr ${rawDuration[2]} min ${rawDuration[3]} sec`;
|
string = `${rawDuration[0]} ${i18n.t('datetime.dayShort')} ${rawDuration[1]} ${i18n.t('datetime.hourShort')} ${rawDuration[2]} ${i18n.t('datetime.minuteShort')} ${rawDuration[3]} ${i18n.t('datetime.secondShort')}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,15 +122,17 @@ export const formatDurationString = (duration: number) => {
|
|||||||
export const formatDurationStringShort = (duration: number) => {
|
export const formatDurationStringShort = (duration: number) => {
|
||||||
const rawDuration = formatDuration(duration).split(':');
|
const rawDuration = formatDuration(duration).split(':');
|
||||||
|
|
||||||
if (rawDuration.length === 2) {
|
if (rawDuration.length === 4) {
|
||||||
// Less than 1 hour: show "0h" and minutes
|
return `${rawDuration[0]}${i18n.t('datetime.dayShort')} ${rawDuration[1]}${i18n.t('datetime.hourShort')}`;
|
||||||
return `0h ${rawDuration[0]}m`;
|
} else if (rawDuration.length === 3) {
|
||||||
} else if (rawDuration.length >= 3) {
|
return `${rawDuration[0]}${i18n.t('datetime.hourShort')} ${rawDuration[1]}${i18n.t('datetime.minuteShort')}`;
|
||||||
// 1 hour or more: show hours and minutes
|
} else if (rawDuration.length === 2) {
|
||||||
return `${rawDuration[0]}h ${rawDuration[1]}m`;
|
return `${rawDuration[0]}${i18n.t('datetime.minuteShort')} ${rawDuration[1]}${i18n.t('datetime.secondShort')}`;
|
||||||
|
} else if (rawDuration.length === 1) {
|
||||||
|
return `${rawDuration[0]}${i18n.t('datetime.secondShort')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '0h 0m';
|
return rawDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatRating = (item: Album | AlbumArtist | Song) =>
|
export const formatRating = (item: Album | AlbumArtist | Song) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user