mirror of
https://github.com/umami-software/umami.git
synced 2026-02-19 20:15:41 +01:00
i18n datetime
This commit is contained in:
parent
338c2f3071
commit
4c3f3cbc71
9 changed files with 57 additions and 55 deletions
|
|
@ -2,15 +2,15 @@ import { useContext } from 'react';
|
|||
import classNames from 'classnames';
|
||||
import { ReportContext } from '../[reportId]/Report';
|
||||
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
|
||||
import { useMessages, useLocale } from 'components/hooks';
|
||||
import { formatDate } from 'lib/date';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import styles from './RetentionTable.module.css';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
const DAYS = [1, 2, 3, 4, 5, 6, 7, 14, 21, 28];
|
||||
|
||||
export function RetentionTable({ days = DAYS }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { locale } = useLocale();
|
||||
const intl = useIntl();
|
||||
const { report } = useContext(ReportContext);
|
||||
const { data } = report || {};
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ export function RetentionTable({ days = DAYS }) {
|
|||
{rows.map(({ date, visitors, records }, rowIndex) => {
|
||||
return (
|
||||
<div key={rowIndex} className={styles.row}>
|
||||
<div className={styles.date}>{formatDate(date, 'PP', locale)}</div>
|
||||
<div className={styles.date}>{intl.formatDate(date, { dateStyle: 'medium' })}</div>
|
||||
<div className={styles.visitors}>{visitors}</div>
|
||||
{days.map(day => {
|
||||
if (totalDays - rowIndex < day) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { renderDateLabels } from 'lib/charts';
|
|||
import { formatLongNumber } from 'lib/format';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { ReportContext } from '../[reportId]/Report';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
export interface PageviewsChartProps extends BarChartProps {
|
||||
isLoading?: boolean;
|
||||
|
|
@ -14,6 +15,7 @@ export interface PageviewsChartProps extends BarChartProps {
|
|||
export function RevenueChart({ isLoading, ...props }: PageviewsChartProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { locale } = useLocale();
|
||||
const intl = useIntl();
|
||||
const { report } = useContext(ReportContext);
|
||||
const { data, parameters } = report || {};
|
||||
|
||||
|
|
@ -88,7 +90,7 @@ export function RevenueChart({ isLoading, ...props }: PageviewsChartProps) {
|
|||
data={chartData}
|
||||
unit={parameters?.dateRange.unit}
|
||||
isLoading={isLoading}
|
||||
renderXLabel={renderDateLabels(parameters?.dateRange.unit, locale)}
|
||||
renderXLabel={renderDateLabels(intl, parameters?.dateRange.unit)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,34 @@
|
|||
import { formatDate } from 'lib/date';
|
||||
import { Flexbox, StatusLight } from 'react-basics';
|
||||
import { formatLongNumber } from 'lib/format';
|
||||
import { useLocale } from 'components/hooks';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
const formats = {
|
||||
millisecond: 'T',
|
||||
second: 'pp',
|
||||
minute: 'p',
|
||||
hour: 'h:mm aaa - PP',
|
||||
day: 'PPPP',
|
||||
week: 'PPPP',
|
||||
month: 'LLLL yyyy',
|
||||
quarter: 'qqq',
|
||||
year: 'yyyy',
|
||||
millisecond: {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
fractionalSecondDigits: 3,
|
||||
},
|
||||
second: { timeStyle: 'medium' },
|
||||
minute: { timeStyle: 'short' },
|
||||
hour: { dateStyle: 'medium', timeStyle: 'short' },
|
||||
day: { dateStyle: 'full' },
|
||||
week: { dateStyle: 'full' },
|
||||
month: { year: 'numeric', month: 'long' },
|
||||
quarter: { year: 'numeric', month: 'long' },
|
||||
year: { year: 'numeric' },
|
||||
};
|
||||
|
||||
export default function BarChartTooltip({ tooltip, unit }) {
|
||||
const { locale } = useLocale();
|
||||
const intl = useIntl();
|
||||
const { labelColors, dataPoints } = tooltip;
|
||||
|
||||
return (
|
||||
<Flexbox direction="column" gap={10}>
|
||||
<div>
|
||||
{formatDate(new Date(dataPoints[0].raw.d || dataPoints[0].raw.x), formats[unit], locale)}
|
||||
</div>
|
||||
<div>{intl.formatDate(dataPoints[0].raw.d || dataPoints[0].raw.x, formats[unit])}</div>
|
||||
<div>
|
||||
<StatusLight color={labelColors?.[0]?.backgroundColor}>
|
||||
{formatLongNumber(dataPoints[0].raw.y)} {dataPoints[0].dataset.label}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import { useState } from 'react';
|
|||
import { Icon, Modal, Dropdown, Item, Text, Flexbox } from 'react-basics';
|
||||
import { endOfYear, isSameDay } from 'date-fns';
|
||||
import DatePickerForm from 'components/metrics/DatePickerForm';
|
||||
import { useLocale, useMessages } from 'components/hooks';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import Icons from 'components/icons';
|
||||
import { formatDate, parseDateValue } from 'lib/date';
|
||||
import { parseDateValue } from 'lib/date';
|
||||
import styles from './DateFilter.module.css';
|
||||
import classNames from 'classnames';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
export interface DateFilterProps {
|
||||
value: string;
|
||||
|
|
@ -31,7 +32,7 @@ export function DateFilter({
|
|||
}: DateFilterProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
const { locale } = useLocale();
|
||||
const intl = useIntl();
|
||||
|
||||
const options = [
|
||||
{ label: formatMessage(labels.today), value: '0day' },
|
||||
|
|
@ -101,11 +102,11 @@ export function DateFilter({
|
|||
const { unit } = parseDateValue(value) || {};
|
||||
|
||||
if (offset && unit === 'year') {
|
||||
return formatDate(startDate, 'yyyy', locale);
|
||||
return intl.formatDate(startDate, { year: 'numeric' });
|
||||
}
|
||||
|
||||
if (offset && unit === 'month') {
|
||||
return formatDate(startDate, 'MMMM yyyy', locale);
|
||||
return intl.formatDate(startDate, { year: 'numeric', month: 'long' });
|
||||
}
|
||||
|
||||
if (value.startsWith('range') || offset) {
|
||||
|
|
@ -156,7 +157,7 @@ export function DateFilter({
|
|||
}
|
||||
|
||||
const CustomRange = ({ startDate, endDate, unit, onClick }) => {
|
||||
const { locale } = useLocale();
|
||||
const intl = useIntl();
|
||||
|
||||
const monthFormat = unit === 'month';
|
||||
|
||||
|
|
@ -173,11 +174,12 @@ const CustomRange = ({ startDate, endDate, unit, onClick }) => {
|
|||
</Icon>
|
||||
<Text>
|
||||
{monthFormat ? (
|
||||
<>{formatDate(startDate, 'MMMM yyyy', locale)}</>
|
||||
<>{intl.formatDate(startDate, { year: 'numeric', month: 'long' })}</>
|
||||
) : (
|
||||
<>
|
||||
{formatDate(startDate, 'd LLL y', locale)}
|
||||
{!isSameDay(startDate, endDate) && ` — ${formatDate(endDate, 'd LLL y', locale)}`}
|
||||
{intl.formatDate(startDate, { dateStyle: 'medium' })}
|
||||
{!isSameDay(startDate, endDate) &&
|
||||
` — ${intl.formatDate(endDate, { dateStyle: 'medium' })}`}
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@ import {
|
|||
import { startOfMonth, endOfMonth } from 'date-fns';
|
||||
import Icons from 'components/icons';
|
||||
import { useLocale } from 'components/hooks';
|
||||
import { formatDate } from 'lib/date';
|
||||
import styles from './MonthSelect.module.css';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
export function MonthSelect({ date = new Date(), onChange }) {
|
||||
const { locale, dateLocale } = useLocale();
|
||||
const month = formatDate(date, 'MMMM', locale);
|
||||
const year = date.getFullYear();
|
||||
const { dateLocale } = useLocale();
|
||||
const intl = useIntl();
|
||||
const month = intl.formatDate(date, { month: 'numeric' });
|
||||
const year = intl.formatDate(date, { year: 'numeric' });
|
||||
const ref = useRef();
|
||||
|
||||
const handleChange = (close: () => void, date: Date) => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { useMemo } from 'react';
|
||||
import { colord } from 'colord';
|
||||
import BarChart from 'components/charts/BarChart';
|
||||
import { useLocale, useDateRange, useWebsiteEventsSeries } from 'components/hooks';
|
||||
import { useDateRange, useWebsiteEventsSeries } from 'components/hooks';
|
||||
import { CHART_COLORS } from 'lib/constants';
|
||||
import { renderDateLabels } from 'lib/charts';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
export interface EventsChartProps {
|
||||
websiteId: string;
|
||||
|
|
@ -14,8 +15,8 @@ export function EventsChart({ websiteId, className }: EventsChartProps) {
|
|||
const {
|
||||
dateRange: { startDate, endDate, unit },
|
||||
} = useDateRange(websiteId);
|
||||
const { locale } = useLocale();
|
||||
const { data, isLoading } = useWebsiteEventsSeries(websiteId);
|
||||
const intl = useIntl();
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
|
@ -51,7 +52,7 @@ export function EventsChart({ websiteId, className }: EventsChartProps) {
|
|||
data={chartData}
|
||||
unit={unit}
|
||||
stacked={true}
|
||||
renderXLabel={renderDateLabels(unit, locale)}
|
||||
renderXLabel={renderDateLabels(intl, unit)}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useMemo } from 'react';
|
|||
import BarChart, { BarChartProps } from 'components/charts/BarChart';
|
||||
import { useLocale, useTheme, useMessages } from 'components/hooks';
|
||||
import { renderDateLabels } from 'lib/charts';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
export interface PagepageviewsChartProps extends BarChartProps {
|
||||
data: {
|
||||
|
|
@ -20,6 +21,7 @@ export function PagepageviewsChart({ data, unit, isLoading, ...props }: Pagepage
|
|||
const { formatMessage, labels } = useMessages();
|
||||
const { colors } = useTheme();
|
||||
const { locale } = useLocale();
|
||||
const intl = useIntl();
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!data) {
|
||||
|
|
@ -74,7 +76,7 @@ export function PagepageviewsChart({ data, unit, isLoading, ...props }: Pagepage
|
|||
data={chartData}
|
||||
unit={unit}
|
||||
isLoading={isLoading}
|
||||
renderXLabel={renderDateLabels(unit, locale)}
|
||||
renderXLabel={renderDateLabels(intl, unit)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
import { formatDate } from 'lib/date';
|
||||
import { formatLongNumber } from 'lib/format';
|
||||
import { type IntlShape } from 'react-intl';
|
||||
|
||||
export function renderNumberLabels(label: string) {
|
||||
return +label > 1000 ? formatLongNumber(+label) : label;
|
||||
}
|
||||
|
||||
export function renderDateLabels(unit: string, locale: string) {
|
||||
export function renderDateLabels(intl: IntlShape, unit: string) {
|
||||
return (label: string, index: number, values: any[]) => {
|
||||
const d = new Date(values[index].value);
|
||||
|
||||
switch (unit) {
|
||||
case 'minute':
|
||||
return formatDate(d, 'h:mm', locale);
|
||||
return intl.formatDate(d, { timeStyle: 'short' });
|
||||
case 'hour':
|
||||
return formatDate(d, 'p', locale);
|
||||
return intl.formatDate(d, { timeStyle: 'short' });
|
||||
case 'day':
|
||||
return formatDate(d, 'MMM d', locale);
|
||||
return intl.formatDate(d, { month: 'short', day: 'numeric' });
|
||||
case 'month':
|
||||
return formatDate(d, 'MMM', locale);
|
||||
return intl.formatDate(d, { month: 'short' });
|
||||
case 'year':
|
||||
return formatDate(d, 'YYY', locale);
|
||||
return intl.formatDate(d, { year: 'numeric' });
|
||||
default:
|
||||
return label;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import {
|
|||
differenceInCalendarWeeks,
|
||||
differenceInCalendarMonths,
|
||||
differenceInCalendarYears,
|
||||
format,
|
||||
max,
|
||||
min,
|
||||
isDate,
|
||||
|
|
@ -288,16 +287,6 @@ export function getDateArray(data: any[], startDate: Date, endDate: Date, unit:
|
|||
return arr;
|
||||
}
|
||||
|
||||
export function formatDate(date: string | number | Date, str: string, locale = 'en-US') {
|
||||
return format(
|
||||
typeof date === 'string' ? new Date(date) : date,
|
||||
CUSTOM_FORMATS?.[locale]?.[str] || str,
|
||||
{
|
||||
locale: getDateLocale(locale),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function maxDate(...args: Date[]) {
|
||||
return max(args.filter(n => isDate(n)));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue