mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Date formatting using locale.
This commit is contained in:
parent
987fdfd57d
commit
ff854150ae
@ -4,24 +4,16 @@ import Globe from 'assets/globe.svg';
|
|||||||
import useDocumentClick from 'hooks/useDocumentClick';
|
import useDocumentClick from 'hooks/useDocumentClick';
|
||||||
import { updateApp } from 'redux/actions/app';
|
import { updateApp } from 'redux/actions/app';
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
import styles from './LanguageButton.module.css';
|
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
|
import { menuOptions } from 'lib/lang';
|
||||||
const supportedLanguages = {
|
import styles from './LanguageButton.module.css';
|
||||||
en: 'EN',
|
|
||||||
'zh-CN': '中文',
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuOptions = [
|
|
||||||
{ label: 'English', value: 'en' },
|
|
||||||
{ label: '中文 (Chinese Simplified)', value: 'zh-CN' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function LanguageButton({ menuPosition = 'bottom', menuAlign = 'left' }) {
|
export default function LanguageButton({ menuPosition = 'bottom', menuAlign = 'left' }) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const locale = useSelector(state => state.app.locale);
|
const locale = useSelector(state => state.app.locale);
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
|
const selectedLocale = menuOptions.find(e => e.value === locale)?.display;
|
||||||
|
|
||||||
function handleSelect(value) {
|
function handleSelect(value) {
|
||||||
dispatch(updateApp({ locale: value }));
|
dispatch(updateApp({ locale: value }));
|
||||||
@ -38,7 +30,7 @@ export default function LanguageButton({ menuPosition = 'bottom', menuAlign = 'l
|
|||||||
return (
|
return (
|
||||||
<div ref={ref} className={styles.container}>
|
<div ref={ref} className={styles.container}>
|
||||||
<Button icon={<Globe />} onClick={() => setShowMenu(true)} size="small">
|
<Button icon={<Globe />} onClick={() => setShowMenu(true)} size="small">
|
||||||
<div>{supportedLanguages[locale]}</div>
|
<div className={locale}>{selectedLocale}</div>
|
||||||
</Button>
|
</Button>
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<Menu
|
<Menu
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
background: var(--gray50);
|
background: var(--gray50);
|
||||||
min-width: 200px;
|
min-width: 400px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
border: 1px solid var(--gray300);
|
border: 1px solid var(--gray300);
|
||||||
|
@ -3,8 +3,9 @@ import ReactTooltip from 'react-tooltip';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import ChartJS from 'chart.js';
|
import ChartJS from 'chart.js';
|
||||||
import styles from './BarChart.module.css';
|
import styles from './BarChart.module.css';
|
||||||
import { format } from 'date-fns';
|
|
||||||
import { formatLongNumber } from 'lib/format';
|
import { formatLongNumber } from 'lib/format';
|
||||||
|
import { dateFormat } from 'lib/lang';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
export default function BarChart({
|
export default function BarChart({
|
||||||
chartId,
|
chartId,
|
||||||
@ -21,6 +22,7 @@ export default function BarChart({
|
|||||||
const canvas = useRef();
|
const canvas = useRef();
|
||||||
const chart = useRef();
|
const chart = useRef();
|
||||||
const [tooltip, setTooltip] = useState({});
|
const [tooltip, setTooltip] = useState({});
|
||||||
|
const locale = useSelector(state => state.app.locale);
|
||||||
|
|
||||||
function renderXLabel(label, index, values) {
|
function renderXLabel(label, index, values) {
|
||||||
const d = new Date(values[index].value);
|
const d = new Date(values[index].value);
|
||||||
@ -28,23 +30,23 @@ export default function BarChart({
|
|||||||
|
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case 'hour':
|
case 'hour':
|
||||||
return format(d, 'ha');
|
return dateFormat(d, 'ha', locale);
|
||||||
case 'day':
|
case 'day':
|
||||||
if (records > 31) {
|
if (records > 31) {
|
||||||
if (w <= 500) {
|
if (w <= 500) {
|
||||||
return index % 10 === 0 ? format(d, 'M/d') : '';
|
return index % 10 === 0 ? dateFormat(d, 'M/d', locale) : '';
|
||||||
}
|
}
|
||||||
return index % 5 === 0 ? format(d, 'M/d') : '';
|
return index % 5 === 0 ? dateFormat(d, 'M/d', locale) : '';
|
||||||
}
|
}
|
||||||
if (w <= 500) {
|
if (w <= 500) {
|
||||||
return index % 2 === 0 ? format(d, 'MMM d') : '';
|
return index % 2 === 0 ? dateFormat(d, 'MMM d', locale) : '';
|
||||||
}
|
}
|
||||||
return format(d, 'EEE M/d');
|
return dateFormat(d, 'EEE M/d', locale);
|
||||||
case 'month':
|
case 'month':
|
||||||
if (w <= 660) {
|
if (w <= 660) {
|
||||||
return format(d, 'MMM');
|
return dateFormat(d, 'MMM', locale);
|
||||||
}
|
}
|
||||||
return format(d, 'MMMM');
|
return dateFormat(d, 'MMMM', locale);
|
||||||
default:
|
default:
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@
|
|||||||
"defaultMessage": "用户行为"
|
"defaultMessage": "用户行为"
|
||||||
},
|
},
|
||||||
"metrics.average-visit-time": {
|
"metrics.average-visit-time": {
|
||||||
"defaultMessage": "停留时间"
|
"defaultMessage": "平均访问时间"
|
||||||
},
|
},
|
||||||
"metrics.bounce-rate": {
|
"metrics.bounce-rate": {
|
||||||
"defaultMessage": "跳出率"
|
"defaultMessage": "跳出率"
|
||||||
@ -168,22 +168,22 @@
|
|||||||
"defaultMessage": "操作系统"
|
"defaultMessage": "操作系统"
|
||||||
},
|
},
|
||||||
"metrics.page-views": {
|
"metrics.page-views": {
|
||||||
"defaultMessage": "访问量"
|
"defaultMessage": "页面流量"
|
||||||
},
|
},
|
||||||
"metrics.pages": {
|
"metrics.pages": {
|
||||||
"defaultMessage": "网页"
|
"defaultMessage": "网页"
|
||||||
},
|
},
|
||||||
"metrics.referrers": {
|
"metrics.referrers": {
|
||||||
"defaultMessage": "来源"
|
"defaultMessage": "指入域名"
|
||||||
},
|
},
|
||||||
"metrics.unique-visitors": {
|
"metrics.unique-visitors": {
|
||||||
"defaultMessage": "访问者"
|
"defaultMessage": "独立访客"
|
||||||
},
|
},
|
||||||
"metrics.views": {
|
"metrics.views": {
|
||||||
"defaultMessage": "访问量"
|
"defaultMessage": "页面流量"
|
||||||
},
|
},
|
||||||
"metrics.visitors": {
|
"metrics.visitors": {
|
||||||
"defaultMessage": "访问者"
|
"defaultMessage": "独立访客"
|
||||||
},
|
},
|
||||||
"placeholder.message.go-to-settings": {
|
"placeholder.message.go-to-settings": {
|
||||||
"defaultMessage": "去设置"
|
"defaultMessage": "去设置"
|
||||||
@ -204,7 +204,7 @@
|
|||||||
"defaultMessage": "添加账户"
|
"defaultMessage": "添加账户"
|
||||||
},
|
},
|
||||||
"title.add-website": {
|
"title.add-website": {
|
||||||
"defaultMessage": "A添加网站"
|
"defaultMessage": "添加网站"
|
||||||
},
|
},
|
||||||
"title.delete-account": {
|
"title.delete-account": {
|
||||||
"defaultMessage": "删除账户"
|
"defaultMessage": "删除账户"
|
||||||
|
23
lib/lang.js
Normal file
23
lib/lang.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import enMessages from 'lang-compiled/en.json';
|
||||||
|
import zhCNMessages from 'lang-compiled/zh-CN.json';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import { enUS, zhCN } from 'date-fns/locale';
|
||||||
|
|
||||||
|
export const messages = {
|
||||||
|
en: enMessages,
|
||||||
|
'zh-CN': zhCNMessages,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dateLocales = {
|
||||||
|
en: enUS,
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const menuOptions = [
|
||||||
|
{ label: 'English', value: 'en', display: 'EN' },
|
||||||
|
{ label: '中文 (Chinese Simplified)', value: 'zh-CN', display: '中文' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function dateFormat(date, str, locale) {
|
||||||
|
return format(date, str, { locale: dateLocales[locale] || enUS });
|
||||||
|
}
|
@ -2,17 +2,11 @@ import React, { useEffect } from 'react';
|
|||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
import { Provider, useDispatch, useSelector } from 'react-redux';
|
import { Provider, useDispatch, useSelector } from 'react-redux';
|
||||||
import { useStore } from 'redux/store';
|
import { useStore } from 'redux/store';
|
||||||
|
import { updateApp } from 'redux/actions/app';
|
||||||
|
import { messages } from 'lib/lang';
|
||||||
import 'styles/variables.css';
|
import 'styles/variables.css';
|
||||||
import 'styles/bootstrap-grid.css';
|
import 'styles/bootstrap-grid.css';
|
||||||
import 'styles/index.css';
|
import 'styles/index.css';
|
||||||
import en from 'lang-compiled/en.json';
|
|
||||||
import cn from 'lang-compiled/zh-CN.json';
|
|
||||||
import { updateApp } from '../redux/actions/app';
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
en,
|
|
||||||
'zh-CN': cn,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Intl = ({ children }) => {
|
const Intl = ({ children }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
@ -15,6 +15,7 @@ body {
|
|||||||
|
|
||||||
.zh-CN {
|
.zh-CN {
|
||||||
font-family: 'Noto Sans SC', sans-serif !important;
|
font-family: 'Noto Sans SC', sans-serif !important;
|
||||||
|
font-size: 110%;
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
|
Loading…
Reference in New Issue
Block a user