Merge remote-tracking branch 'origin/dev' into testing-{city,-region}

pull/885/head
Nisarg Patel 2021-11-08 13:10:13 -08:00
commit 774b7f9296
19 changed files with 805 additions and 1187 deletions

26
app.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "Umami",
"description": "Umami is a simple, fast, website analytics alternative to Google Analytics.",
"keywords": [
"analytics",
"charts",
"statistics",
"web-analytics"
],
"website": "https://umami.is",
"repository": "https://github.com/mikecao/umami",
"addons": [
"heroku-postgresql"
],
"env": {
"HASH_SALT": {
"description": "Used to generate unique values for your installation",
"required": true,
"generator": "secret"
}
},
"scripts": {
"postdeploy": "psql $DATABASE_URL -f sql/schema.postgresql.sql"
},
"success_url": "/"
}

View File

@ -16,6 +16,7 @@ function MenuButton({
menuAlign = 'right',
onSelect,
renderValue,
hideLabel,
}) {
const [showMenu, setShowMenu] = useState(false);
const ref = useRef();
@ -44,7 +45,9 @@ function MenuButton({
onClick={toggleMenu}
variant="light"
>
<div className={styles.text}>{renderValue ? renderValue(selectedOption) : value}</div>
{!hideLabel && (
<div className={styles.text}>{renderValue ? renderValue(selectedOption) : value}</div>
)}
</Button>
{showMenu && (
<Menu

View File

@ -5,14 +5,13 @@ import Link from 'components/common/Link';
import styles from './Footer.module.css';
import useVersion from 'hooks/useVersion';
import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang';
export default function Footer() {
const { current } = useVersion();
const { locale } = useLocale();
const { dir } = useLocale();
return (
<footer className="container" dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}>
<footer className="container" dir={dir}>
<div className={classNames(styles.footer, 'row')}>
<div className="col-12 col-md-4" />
<div className="col-12 col-md-4">

View File

@ -12,21 +12,20 @@ import Button from 'components/common/Button';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang';
import XMark from 'assets/xmark.svg';
import Bars from 'assets/bars.svg';
export default function Header() {
const user = useSelector(state => state.user);
const [active, setActive] = useState(false);
const { locale } = useLocale();
const { locale, dir } = useLocale();
function handleClick() {
setActive(state => !state);
}
return (
<nav className="container" dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}>
<nav className="container" dir={dir}>
{user?.is_admin && <UpdateNotice />}
<div className={classNames(styles.header, 'row align-items-center')}>
<div className={styles.nav}>

View File

@ -3,11 +3,9 @@ import Head from 'next/head';
import Header from 'components/layout/Header';
import Footer from 'components/layout/Footer';
import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang';
export default function Layout({ title, children, header = true, footer = true }) {
const { locale } = useLocale();
const dir = rtlLocales.includes(locale) ? 'rtl' : 'ltr';
const { dir } = useLocale();
return (
<>

View File

@ -5,4 +5,5 @@
align-content: center;
min-height: 80px;
align-self: stretch;
font-weight: bold;
}

View File

@ -37,8 +37,8 @@ export default function LanguageButton() {
options={menuOptions}
value={locale}
menuClassName={styles.menu}
renderValue={option => option?.display}
onSelect={handleSelect}
hideLabel
/>
);
}

View File

@ -4,6 +4,7 @@ import { setLocale } from 'redux/actions/app';
import { useRouter } from 'next/router';
import { get, setItem } from 'lib/web';
import { LOCALE_CONFIG } from 'lib/constants';
import { languages } from 'lib/lang';
import useForceUpdate from 'hooks/useForceUpdate';
import enUS from 'public/lang/en-US.json';
@ -16,6 +17,7 @@ export default function useLocale() {
const dispatch = useDispatch();
const { basePath } = useRouter();
const forceUpdate = useForceUpdate();
const dir = languages[locale]?.dir || 'ltr';
async function loadMessages(locale) {
const { ok, data } = await get(`${basePath}/lang/${locale}.json`);
@ -45,5 +47,5 @@ export default function useLocale() {
}
}, [locale]);
return { locale, saveLocale, messages };
return { locale, saveLocale, messages, dir };
}

View File

@ -19,6 +19,7 @@
"label.delete": "حذف",
"label.delete-account": "حذف الحساب",
"label.delete-website": "حذف الموقع",
"label.reset-website": "اعادة تعيين الإحصائيات",
"label.dismiss": "اخفاء",
"label.domain": "نطاق",
"label.edit": "تعديل",
@ -58,8 +59,10 @@
"label.view-details": "عرض التفاصيل",
"label.websites": "المواقع",
"message.active-users": "{x} حاليا {x, plural, one {زائر واحد} other {زوار}}",
"message.confirm-reset": "هل أنت متأكد من اعادة تعيين الإحصائيات لـ {target}؟",
"message.confirm-delete": "هل أنت متأكد من حذف {target}?",
"message.copied": "تم النسخ!",
"message.reset-warning": "سيتم اعادة تعيين كافة الإحصائيات لهذا الموقع، لكن لن يتم تعيير كود التتبع",
"message.delete-warning": "كافة البيانات المرتبطة سيم حذفها ايضا.",
"message.failure": "حدث خطأ ما.",
"message.get-share-url": "احصل على رابط المشاركة",

View File

@ -59,8 +59,8 @@
"label.view-details": "View details",
"label.websites": "Websites",
"message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}",
"message.confirm-reset": "Are your sure you want to reset {target}'s statistics?",
"message.confirm-delete": "Are your sure you want to delete {target}?",
"message.confirm-reset": "Are you sure you want to reset {target}'s statistics?",
"message.confirm-delete": "Are you sure you want to delete {target}?",
"message.copied": "Copied!",
"message.reset-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.",
"message.delete-warning": "All associated data will be deleted as well.",

View File

@ -59,8 +59,8 @@
"label.view-details": "View details",
"label.websites": "Websites",
"message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}",
"message.confirm-reset": "Are your sure you want to reset {target}'s statistics?",
"message.confirm-delete": "Are your sure you want to delete {target}?",
"message.confirm-reset": "Are you sure you want to reset {target}'s statistics?",
"message.confirm-delete": "Are you sure you want to delete {target}?",
"message.copied": "Copied!",
"message.reset-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.",
"message.delete-warning": "All associated data will be deleted as well.",

View File

@ -5,6 +5,7 @@
"label.administrator": "Pengelola",
"label.all": "Semua",
"label.all-websites": "Semua website",
"label.all-events": "Semua peristiwa",
"label.back": "Kembali",
"label.cancel": "Batal",
"label.change-password": "Ganti kata sandi",
@ -18,6 +19,7 @@
"label.delete": "Hapus",
"label.delete-account": "Hapus akun",
"label.delete-website": "Hapus situs web",
"label.reset-website": "Atur ulang statistik",
"label.dismiss": "Tutup",
"label.domain": "Domain",
"label.edit": "Sunting",
@ -59,6 +61,7 @@
"message.active-users": "{x} pengunjung saat ini",
"message.confirm-delete": "Apakah kamu yakin ingin menghapus {target}?",
"message.copied": "Tersalin!",
"message.reset-warning": "Semua statistik pada website ini akan dihapus, tetapi kode lacak akan tetap terpasang",
"message.delete-warning": "Semua data terkait juga akan dihapus.",
"message.failure": "Ada yang salah.",
"message.get-share-url": "Dapatkan URL berbagi",

View File

@ -72,7 +72,7 @@
"message.page-not-found": "ページが見つかりません。",
"message.powered-by": "このシステムは {name} で実行されています。",
"message.save-success": "正常に保存されました。",
"message.share-url": "これは {target} の共有リンクです。",
"message.share-url": "これは{target}の共有リンクです。",
"message.track-stats": "{target}のアクセス解析を開始するには、次のコードをWebサイトの{head}セクションへ追加してください。",
"message.type-delete": "確認のため、下のフォームに{delete}と入力してください。",
"metrics.actions": "アクション",

View File

@ -65,7 +65,7 @@
"message.get-tracking-code": "获取跟踪代码",
"message.go-to-settings": "去设置",
"message.incorrect-username-password": "用户名或密码不正确。",
"message.log.visitor": "来自 {country} 的访客在搭载 {os} 的 {device} 上使用 {browser} 浏览器进行访问。",
"message.log.visitor": "来自{country}的访客在搭载 {os} 的{device}上使用 {browser} 浏览器进行访问。",
"message.new-version-available": "umami 有新版本 {version} 发布啦!",
"message.no-data-available": "无可用数据。",
"message.no-websites-configured": "你还没有设置任何网站。",

View File

@ -65,7 +65,7 @@
"message.get-tracking-code": "獲得追蹤代碼",
"message.go-to-settings": "去設定",
"message.incorrect-username-password": "用户名或密碼不正確。",
"message.log.visitor": "自 {country} 的訪客在搭載 {os} 的 {device} 上使用 {browser} 進行訪問。",
"message.log.visitor": "自{country}的訪客在搭載 {os} 的{device}上使用 {browser} 進行訪問。",
"message.new-version-available": "umami 有新版本 {version} 發佈啦!",
"message.no-data-available": "無可用數據。",
"message.no-websites-configured": "目前無任何網站設定。",

View File

@ -26,7 +26,7 @@ import {
format,
} from 'date-fns';
import { enUS } from 'date-fns/locale';
import { dateLocales } from 'lib/lang';
import { languages } from 'lib/lang';
export function getTimezone() {
return moment.tz.guess();
@ -38,7 +38,7 @@ export function getLocalTime(t) {
export function getDateRange(value, locale = 'en-US') {
const now = new Date();
const localeOptions = dateLocales[locale];
const dateLocale = languages[locale]?.dateLocale || enUS;
const { num, unit } = value.match(/^(?<num>[0-9]+)(?<unit>hour|day|week|month|year)$/).groups;
@ -53,8 +53,8 @@ export function getDateRange(value, locale = 'en-US') {
};
case 'week':
return {
startDate: startOfWeek(now, { locale: localeOptions }),
endDate: endOfWeek(now, { locale: localeOptions }),
startDate: startOfWeek(now, { locale: dateLocale }),
endDate: endOfWeek(now, { locale: dateLocale }),
unit: 'day',
value,
};
@ -164,6 +164,6 @@ export const customFormats = {
export function dateFormat(date, str, locale = 'en-US') {
return format(date, customFormats?.[locale]?.[str] || str, {
locale: dateLocales[locale] || enUS,
locale: languages[locale]?.dateLocale || enUS,
});
}

View File

@ -38,83 +38,41 @@ import {
} from 'date-fns/locale';
export const languages = {
'ar-SA': { label: 'العربية', display: 'ar' },
'zh-CN': { label: '中文', display: 'cn' },
'zh-TW': { label: '中文(繁體)', display: 'tw' },
'ca-ES': { label: 'Català', display: 'ca' },
'cs-CZ': { label: 'Čeština', display: 'cs' },
'da-DK': { label: 'Dansk', display: 'da' },
'de-DE': { label: 'Deutsch', display: 'de' },
'en-US': { label: 'English (US)', display: 'en' },
'en-GB': { label: 'English (UK)', display: 'en-GB'},
'es-MX': { label: 'Español', display: 'es' },
'fa-IR': { label: 'فارسی', display: 'fa' },
'fo-FO': { label: 'Føroyskt', display: 'fo' },
'fr-FR': { label: 'Français', display: 'fr' },
'el-GR': { label: 'Ελληνικά', display: 'el' },
'he-IL': { label: 'עברית', display: 'he' },
'hi-IN': { label: 'हिन्दी', display: 'hi' },
'hu-HU': { label: 'Hungarian', display: 'hu' },
'it-IT': { label: 'Italiano', display: 'it' },
'id-ID': { label: 'Bahasa Indonesia', display: 'id' },
'ja-JP': { label: '日本語', display: 'ja' },
'ko-KR': { label: '한국어', display: 'ko' },
'ms-MY': { label: 'Malay', display: 'ms' },
'mn-MN': { label: 'Монгол', display: 'mn' },
'nl-NL': { label: 'Nederlands', display: 'nl' },
'nb-NO': { label: 'Norsk Bokmål', display: 'nb' },
'pl-PL': { label: 'Polski', display: 'pl' },
'pt-PT': { label: 'Português', display: 'pt' },
'pt-BR': { label: 'Português do Brasil', display: 'pt-BR' },
'ru-RU': { label: 'Русский', display: 'ru' },
'ro-RO': { label: 'Română', display: 'ro' },
'sk-SK': { label: 'Slovenčina', display: 'sk' },
'sl-SI': { label: 'Slovenščina', display: 'sl' },
'fi-FI': { label: 'Suomi', display: 'fi' },
'sv-SE': { label: 'Svenska', display: 'sv' },
'ta-IN': { label: 'தமிழ்', display: 'ta' },
'tr-TR': { label: 'Türkçe', display: 'tr' },
'uk-UA': { label: 'українська', display: 'uk' },
};
export const rtlLocales = ['ar-SA', 'fa-IR'];
export const dateLocales = {
'ar-SA': arSA,
'en-US': enUS,
'en-GB': enGB,
'nl-NL': nl,
'zh-CN': zhCN,
'zh-TW': zhTW,
'de-DE': de,
'da-DK': da,
'ru-RU': ru,
'sv-SE': sv,
'tr-TR': tr,
'ja-JP': ja,
'es-MX': es,
'fr-FR': fr,
'mn-MN': mn,
'el-GR': el,
'fo-FO': da,
'pt-PT': pt,
'pt-BR': ptBR,
'ro-RO': ro,
'nb-NO': nb,
'id-ID': id,
'uk-UA': uk,
'fi-FI': fi,
'cs-CZ': cs,
'sk-SK': sk,
'pl-PL': pl,
'ta-In': ta,
'hi-IN': hi,
'he-IL': he,
'it-IT': it,
'fa-IR': faIR,
'ms-MY': ms,
'ca-ES': ca,
'hu-HU': hu,
'ko-KR': ko,
'sl-SI': sl,
'ar-SA': { label: 'العربية', dateLocale: arSA, dir: 'rtl' },
'zh-CN': { label: '中文', dateLocale: zhCN },
'zh-TW': { label: '中文(繁體)', dateLocale: zhTW },
'ca-ES': { label: 'Català', dateLocale: ca },
'cs-CZ': { label: 'Čeština', dateLocale: cs },
'da-DK': { label: 'Dansk', dateLocale: da },
'de-DE': { label: 'Deutsch', dateLocale: de },
'en-US': { label: 'English (US)', dateLocale: enUS },
'en-GB': { label: 'English (UK)', dateLocale: enGB },
'es-MX': { label: 'Español', dateLocale: es },
'fa-IR': { label: 'فارسی', dateLocale: faIR, dir: 'rtl' },
'fo-FO': { label: 'Føroyskt' },
'fr-FR': { label: 'Français', dateLocale: fr },
'el-GR': { label: 'Ελληνικά', dateLocale: el },
'he-IL': { label: 'עברית', dateLocale: he },
'hi-IN': { label: 'हिन्दी', dateLocale: hi },
'hu-HU': { label: 'Hungarian', dateLocale: hu },
'it-IT': { label: 'Italiano', dateLocale: it },
'id-ID': { label: 'Bahasa Indonesia', dateLocale: id },
'ja-JP': { label: '日本語', dateLocale: ja },
'ko-KR': { label: '한국어', dateLocale: ko },
'ms-MY': { label: 'Malay', dateLocale: ms },
'mn-MN': { label: 'Монгол', dateLocale: mn },
'nl-NL': { label: 'Nederlands', dateLocale: nl },
'nb-NO': { label: 'Norsk Bokmål', dateLocale: nb },
'pl-PL': { label: 'Polski', dateLocale: pl },
'pt-PT': { label: 'Português', dateLocale: pt },
'pt-BR': { label: 'Português do Brasil', dateLocale: ptBR },
'ru-RU': { label: 'Русский', dateLocale: ru },
'ro-RO': { label: 'Română', dateLocale: ro },
'sk-SK': { label: 'Slovenčina', dateLocale: sk },
'sl-SI': { label: 'Slovenščina', dateLocale: sl },
'fi-FI': { label: 'Suomi', dateLocale: fi },
'sv-SE': { label: 'Svenska', dateLocale: sv },
'ta-IN': { label: 'தமிழ்', dateLocale: ta },
'tr-TR': { label: 'Türkçe', dateLocale: tr },
'uk-UA': { label: 'українська', dateLocale: uk },
};

View File

@ -71,7 +71,7 @@
"date-fns": "^2.23.0",
"date-fns-tz": "^1.1.4",
"detect-browser": "^5.2.0",
"dotenv": "^8.2.0",
"dotenv": "^10.0.0",
"formik": "^2.2.9",
"immer": "^9.0.6",
"ipaddr.js": "^2.0.1",
@ -80,8 +80,8 @@
"jose": "2.0.5",
"maxmind": "^4.3.2",
"moment-timezone": "^0.5.33",
"next": "10.2.2",
"prompts": "2.4.1",
"next": "12.0.1",
"prompts": "2.4.2",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -110,31 +110,31 @@
"cross-env": "^7.0.3",
"del": "^6.0.0",
"dotenv-cli": "^4.0.0",
"eslint": "^7.31.0",
"eslint-config-next": "^11.0.1",
"eslint": "^8.1.0",
"eslint-config-next": "^12.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"extract-react-intl-messages": "^4.1.1",
"husky": "^4.3.8",
"husky": "^7.0.4",
"lint-staged": "^11.0.0",
"loadtest": "5.1.2",
"npm-run-all": "^4.1.5",
"postcss": "^8.2.15",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^13.0.0",
"postcss-import": "^14.0.2",
"postcss-preset-env": "^6.7.0",
"postcss-rtlcss": "^3.3.2",
"prettier": "^2.3.2",
"prettier-eslint": "^12.0.0",
"prettier-eslint": "^13.0.0",
"prisma": "2.29.1",
"rollup": "^2.48.0",
"rollup-plugin-hashbang": "^2.2.2",
"rollup-plugin-terser": "^7.0.2",
"stylelint": "^13.13.1",
"stylelint": "^14.0.1",
"stylelint-config-css-modules": "^2.2.0",
"stylelint-config-prettier": "^8.0.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^5.0.0",
"tar": "^6.1.2"
}

1768
yarn.lock

File diff suppressed because it is too large Load Diff