Merge pull request #728 from mikecao/dev

v1.19.0
This commit is contained in:
Mike Cao 2021-07-16 00:25:42 -07:00 committed by GitHub
commit a2fdab6c9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 959 additions and 569 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@
/build /build
/public/umami.js /public/umami.js
/public/geo /public/geo
/public/lang
/lang-compiled /lang-compiled
# misc # misc

View File

@ -10,7 +10,7 @@ A detailed getting started guide can be found at [https://umami.is/docs/](https:
### Requirements ### Requirements
- A server with Node.js 10.13 or newer - A server with Node.js 12 or newer
- A database (MySQL or Postgresql) - A database (MySQL or Postgresql)
### Get the source code and install packages ### Get the source code and install packages

View File

@ -27,7 +27,7 @@ import styles from './Calendar.module.css';
import Icon from './Icon'; import Icon from './Icon';
export default function Calendar({ date, minDate, maxDate, onChange }) { export default function Calendar({ date, minDate, maxDate, onChange }) {
const [locale] = useLocale(); const { locale } = useLocale();
const [selectMonth, setSelectMonth] = useState(false); const [selectMonth, setSelectMonth] = useState(false);
const [selectYear, setSelectYear] = useState(false); const [selectYear, setSelectYear] = useState(false);

View File

@ -55,7 +55,7 @@ const filterOptions = [
]; ];
function DateFilter({ value, startDate, endDate, onChange, className }) { function DateFilter({ value, startDate, endDate, onChange, className }) {
const [locale] = useLocale(); const { locale } = useLocale();
const [showPicker, setShowPicker] = useState(false); const [showPicker, setShowPicker] = useState(false);
const displayValue = const displayValue =
value === 'custom' ? ( value === 'custom' ? (
@ -102,7 +102,7 @@ function DateFilter({ value, startDate, endDate, onChange, className }) {
} }
const CustomRange = ({ startDate, endDate, onClick }) => { const CustomRange = ({ startDate, endDate, onClick }) => {
const [locale] = useLocale(); const { locale } = useLocale();
function handleClick(e) { function handleClick(e) {
e.stopPropagation(); e.stopPropagation();

View File

@ -12,7 +12,7 @@ import useLocale from 'hooks/useLocale';
function RefreshButton({ websiteId }) { function RefreshButton({ websiteId }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [locale] = useLocale(); const { locale } = useLocale();
const [dateRange] = useDateRange(websiteId); const [dateRange] = useDateRange(websiteId);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const completed = useSelector(state => state.queries[`/api/website/${websiteId}/stats`]); const completed = useSelector(state => state.queries[`/api/website/${websiteId}/stats`]);

View File

@ -24,7 +24,7 @@ function WorldMap({ data, className }) {
}), }),
[theme], [theme],
); );
const [locale] = useLocale(); const { locale } = useLocale();
const countryNames = useCountryNames(locale); const countryNames = useCountryNames(locale);
function getFillColor(code) { function getFillColor(code) {

View File

@ -9,7 +9,7 @@ import { rtlLocales } from 'lib/lang';
export default function Footer() { export default function Footer() {
const { current } = useVersion(); const { current } = useVersion();
const [locale] = useLocale(); const { locale } = useLocale();
return ( return (
<footer className="container" dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}> <footer className="container" dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}>

View File

@ -19,7 +19,7 @@ import Bars from 'assets/bars.svg';
export default function Header() { export default function Header() {
const user = useSelector(state => state.user); const user = useSelector(state => state.user);
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
const [locale] = useLocale(); const { locale } = useLocale();
function handleClick() { function handleClick() {
setActive(state => !state); setActive(state => !state);

View File

@ -6,7 +6,7 @@ import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang'; import { rtlLocales } from 'lib/lang';
export default function Layout({ title, children, header = true, footer = true }) { export default function Layout({ title, children, header = true, footer = true }) {
const [locale] = useLocale(); const { locale } = useLocale();
const dir = rtlLocales.includes(locale) ? 'rtl' : 'ltr'; const dir = rtlLocales.includes(locale) ? 'rtl' : 'ltr';
return ( return (

View File

@ -27,7 +27,7 @@ export default function BarChart({
const canvas = useRef(); const canvas = useRef();
const chart = useRef(); const chart = useRef();
const [tooltip, setTooltip] = useState(null); const [tooltip, setTooltip] = useState(null);
const [locale] = useLocale(); const { locale } = useLocale();
const [theme] = useTheme(); const [theme] = useTheme();
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();

View File

@ -6,7 +6,7 @@ import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
export default function CountriesTable({ websiteId, onDataLoad, ...props }) { export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
const [locale] = useLocale(); const { locale } = useLocale();
const countryNames = useCountryNames(locale); const countryNames = useCountryNames(locale);
function renderLabel({ x }) { function renderLabel({ x }) {

View File

@ -6,7 +6,7 @@ import styles from './Legend.module.css';
import useForceUpdate from '../../hooks/useForceUpdate'; import useForceUpdate from '../../hooks/useForceUpdate';
export default function Legend({ chart }) { export default function Legend({ chart }) {
const [locale] = useLocale(); const { locale } = useLocale();
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
function handleClick(index) { function handleClick(index) {

View File

@ -31,7 +31,7 @@ const TYPE_ICONS = {
export default function RealtimeLog({ data, websites, websiteId }) { export default function RealtimeLog({ data, websites, websiteId }) {
const intl = useIntl(); const intl = useIntl();
const [locale] = useLocale(); const { locale } = useLocale();
const countryNames = useCountryNames(locale); const countryNames = useCountryNames(locale);
const [filter, setFilter] = useState(TYPE_ALL); const [filter, setFilter] = useState(TYPE_ALL);

View File

@ -29,7 +29,7 @@ function filterWebsite(data, id) {
} }
export default function RealtimeDashboard() { export default function RealtimeDashboard() {
const [locale] = useLocale(); const { locale } = useLocale();
const countryNames = useCountryNames(locale); const countryNames = useCountryNames(locale);
const [data, setData] = useState(); const [data, setData] = useState();
const [websiteId, setWebsiteId] = useState(0); const [websiteId, setWebsiteId] = useState(0);

View File

@ -9,7 +9,7 @@ import styles from './DateRangeSetting.module.css';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
export default function DateRangeSetting() { export default function DateRangeSetting() {
const [locale] = useLocale(); const { locale } = useLocale();
const [dateRange, setDateRange] = useDateRange(); const [dateRange, setDateRange] = useDateRange();
const { startDate, endDate, value } = dateRange; const { startDate, endDate, value } = dateRange;

View File

@ -1,15 +1,16 @@
import React from 'react'; import React from 'react';
import { menuOptions } from 'lib/lang'; import { languages } from 'lib/lang';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
import MenuButton from 'components/common/MenuButton'; import MenuButton from 'components/common/MenuButton';
import Globe from 'assets/globe.svg'; import Globe from 'assets/globe.svg';
import styles from './LanguageButton.module.css'; import styles from './LanguageButton.module.css';
export default function LanguageButton() { export default function LanguageButton() {
const [locale, setLocale] = useLocale(); const { locale, saveLocale } = useLocale();
const menuOptions = Object.keys(languages).map(key => ({ ...languages[key], value: key }));
function handleSelect(value) { function handleSelect(value) {
setLocale(value); saveLocale(value);
} }
switch (locale) { switch (locale) {

View File

@ -11,6 +11,7 @@ services:
HASH_SALT: replace-me-with-a-random-string HASH_SALT: replace-me-with-a-random-string
depends_on: depends_on:
- db - db
restart: always
db: db:
image: postgres:12-alpine image: postgres:12-alpine
environment: environment:
@ -20,5 +21,6 @@ services:
volumes: volumes:
- ./sql/schema.postgresql.sql:/docker-entrypoint-initdb.d/schema.postgresql.sql:ro - ./sql/schema.postgresql.sql:/docker-entrypoint-initdb.d/schema.postgresql.sql:ro
- umami-db-data:/var/lib/postgresql/data - umami-db-data:/var/lib/postgresql/data
restart: always
volumes: volumes:
umami-db-data: umami-db-data:

View File

@ -9,7 +9,7 @@ import useLocale from './useLocale';
export default function useDateRange(websiteId, defaultDateRange = DEFAULT_DATE_RANGE) { export default function useDateRange(websiteId, defaultDateRange = DEFAULT_DATE_RANGE) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [locale] = useLocale(); const { locale } = useLocale();
const dateRange = useSelector(state => state.websites[websiteId]?.dateRange); const dateRange = useSelector(state => state.websites[websiteId]?.dateRange);
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();

View File

@ -1,16 +1,49 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { setLocale } from 'redux/actions/app'; import { setLocale } from 'redux/actions/app';
import { setItem } from 'lib/web'; import { useRouter } from 'next/router';
import { get, setItem } from 'lib/web';
import { LOCALE_CONFIG } from 'lib/constants'; import { LOCALE_CONFIG } from 'lib/constants';
import useForceUpdate from 'hooks/useForceUpdate';
import enUS from 'public/lang/en-US.json';
const messages = {
'en-US': enUS,
};
export default function useLocale() { export default function useLocale() {
const locale = useSelector(state => state.app.locale); const locale = useSelector(state => state.app.locale);
const dispatch = useDispatch(); const dispatch = useDispatch();
const { basePath } = useRouter();
const forceUpdate = useForceUpdate();
function saveLocale(value) { async function loadMessages(locale) {
setItem(LOCALE_CONFIG, value); const { ok, data } = await get(`${basePath}/lang/${locale}.json`);
dispatch(setLocale(value));
if (ok) {
messages[locale] = data;
}
} }
return [locale, saveLocale]; async function saveLocale(value) {
if (!messages[value]) {
await loadMessages(value);
}
setItem(LOCALE_CONFIG, value);
if (locale !== value) {
dispatch(setLocale(value));
} else {
forceUpdate();
}
}
useEffect(() => {
if (!messages[locale]) {
saveLocale(locale);
}
}, [locale]);
return { locale, saveLocale, messages };
} }

100
lang/hu-HU.json Normal file
View File

@ -0,0 +1,100 @@
{
"label.accounts": "Fiókok",
"label.add-account": "Fiók hozzáadása",
"label.add-website": "Weboldal hozzáadása",
"label.administrator": "Adminisztrátor",
"label.all": "Összes",
"label.all-websites": "Összes weboldal",
"label.all-events": "Összes esemény",
"label.back": "Vissza",
"label.cancel": "Mégsem",
"label.change-password": "Jelszó módosítása",
"label.confirm-password": "Jelszó megerősítése",
"label.copy-to-clipboard": "Vágólapra másolás",
"label.current-password": "Jelenlegi jelszó",
"label.custom-range": "Egyedi tartomány",
"label.dashboard": "Áttekintés",
"label.date-range": "Időintervallum",
"label.default-date-range": "Alapértelmezett időintervallum",
"label.delete": "Eltávolítás",
"label.delete-account": "Fiók eltávolítása",
"label.delete-website": "Weboldal eltávolítása",
"label.dismiss": "Mellőzés",
"label.domain": "Domain",
"label.edit": "Módosítás",
"label.edit-account": "Fiók módosítása",
"label.edit-website": "Weboldal módosítása",
"label.enable-share-url": "URL-megosztás engedélyezése",
"label.invalid": "Érvénytelen",
"label.invalid-domain": "Érvénytelen domain",
"label.last-days": "Legutóbbi {x} nap",
"label.last-hours": "Legutóbbi {x} óra",
"label.logged-in-as": "Bejelentkezve, mint {username}",
"label.login": "Bejelentkezés",
"label.logout": "Kijelentkezés",
"label.more": "Bővebben",
"label.name": "Név",
"label.new-password": "Új jelszó",
"label.password": "Jelszó",
"label.passwords-dont-match": "A jelszavak nem egyeznek",
"label.profile": "Profil",
"label.realtime": "Valós idejű",
"label.realtime-logs": "Valós idejű napló",
"label.refresh": "Frissítés",
"label.required": "Kötelező",
"label.reset": "Visszaállítás",
"label.save": "Mentés",
"label.settings": "Beállítások",
"label.share-url": "URL megosztása",
"label.single-day": "Egy nap",
"label.this-month": "Ezen hónap",
"label.this-week": "Ezen hét",
"label.this-year": "Ezen év",
"label.timezone": "Időzóna",
"label.today": "Ma",
"label.tracking-code": "Követési kód",
"label.unknown": "Ismeretlen",
"label.username": "Felhasználónév",
"label.view-details": "Részletek",
"label.websites": "Weboldalak",
"message.active-users": "{x} {x, plural, one {látogató} other {latógató}} jelenleg",
"message.confirm-delete": "Biztos, hogy törölni szeretnéd {target} elemet?",
"message.copied": "Kimásolva!",
"message.delete-warning": "Minden társított adat törlésre kerül.",
"message.failure": "Valami baj történt.",
"message.get-share-url": "Megosztási URL kimásolása",
"message.get-tracking-code": "Követési kód kimásolása",
"message.go-to-settings": "Tovább a beállításokhoz",
"message.incorrect-username-password": "Érvénytelen felhasználónév/jelszó.",
"message.log.visitor": "Látógató {country} területéről, {os} {device} eszközön, {browser} böngészőből.",
"message.new-version-available": "Elérhető az umami {version} új verziója!",
"message.no-data-available": "Nincs rendelkezésre álló adat.",
"message.no-websites-configured": "Még nem állítottál be egyetlen weboldalt sem.",
"message.page-not-found": "Oldal nem található.",
"message.powered-by": "Működteti az {name}",
"message.save-success": "Sikeres mentés.",
"message.share-url": "{target} nyilvánosan megosztott URL címe.",
"message.track-stats": "{target} statisztikáinak nyomon követéséhez, helyezd el az alábbi kódot a weboldalad {head} részébe.",
"message.type-delete": "Megerősítéshez írd be az alábbi mezőbe azt, hogy {delete}.",
"metrics.actions": "Műveletek",
"metrics.average-visit-time": "Átlagos látogatási idő",
"metrics.bounce-rate": "Visszafordulási arány",
"metrics.browsers": "Böngészők",
"metrics.countries": "Országok",
"metrics.device.desktop": "Asztali számítógép",
"metrics.device.laptop": "Laptop",
"metrics.device.mobile": "Telefon",
"metrics.device.tablet": "Táblagép",
"metrics.devices": "Eszközök",
"metrics.events": "Események",
"metrics.filter.combined": "Összevont",
"metrics.filter.domain-only": "Csak domain",
"metrics.filter.raw": "Nyers",
"metrics.operating-systems": "Operációs rendszerek",
"metrics.page-views": "Oldalmegtekintések",
"metrics.pages": "Oldalak",
"metrics.referrers": "Hivatkozók",
"metrics.unique-visitors": "Egyedi látogatók",
"metrics.views": "Megtekintések",
"metrics.visitors": "Látogatók"
}

View File

@ -57,29 +57,29 @@
"label.view-details": "查看更多", "label.view-details": "查看更多",
"label.websites": "網站", "label.websites": "網站",
"message.active-users": "当前線上 {x} 人", "message.active-users": "当前線上 {x} 人",
"message.confirm-delete": "你確定要删除{target}嗎?", "message.confirm-delete": "你確定要删除 {target} 嗎?",
"message.copied": "複製成功!", "message.copied": "複製成功",
"message.delete-warning": "所有相關數據將會被删除.", "message.delete-warning": "所有相關數據將會被删除",
"message.failure": "出現錯誤.", "message.failure": "出現錯誤",
"message.get-share-url": "獲得分享連結", "message.get-share-url": "獲得分享連結",
"message.get-tracking-code": "獲得追蹤代碼", "message.get-tracking-code": "獲得追蹤代碼",
"message.go-to-settings": "去設定", "message.go-to-settings": "去設定",
"message.incorrect-username-password": "用户名或密碼不正確.", "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.new-version-available": "umami 有新版本 {version} 發佈啦!",
"message.no-data-available": "無可用數據.", "message.no-data-available": "無可用數據",
"message.no-websites-configured": "目前無任何網站設定.", "message.no-websites-configured": "目前無任何網站設定",
"message.page-not-found": "網頁未找到.", "message.page-not-found": "網頁未找到",
"message.powered-by": "運行 {name}", "message.powered-by": "運行 {name}",
"message.save-success": "成功保存.", "message.save-success": "成功保存",
"message.share-url": "這是 {target} 的分享連結.", "message.share-url": "這是 {target} 的分享連結",
"message.track-stats": "將以下代碼放入被設定網站的{head}部分来收集{target}的資料.", "message.track-stats": "將以下代碼放入被設定網站的 {head} 部分来收集 {target} 的資料。",
"message.type-delete": "在下方空格輸入{delete}確認", "message.type-delete": "在下方空格輸入 {delete} 確認",
"metrics.actions": "用戶行為", "metrics.actions": "用戶行為",
"metrics.average-visit-time": "平均訪問時間", "metrics.average-visit-time": "平均訪問時間",
"metrics.bounce-rate": "跳出率", "metrics.bounce-rate": "跳出率",
"metrics.browsers": "瀏覽器", "metrics.browsers": "瀏覽器",
"metrics.countries": "國家", "metrics.countries": "國家/地區",
"metrics.device.desktop": "桌機", "metrics.device.desktop": "桌機",
"metrics.device.laptop": "筆記本", "metrics.device.laptop": "筆記本",
"metrics.device.mobile": "手機", "metrics.device.mobile": "手機",
@ -94,6 +94,6 @@
"metrics.pages": "網頁", "metrics.pages": "網頁",
"metrics.referrers": "指入域名", "metrics.referrers": "指入域名",
"metrics.unique-visitors": "獨立訪客", "metrics.unique-visitors": "獨立訪客",
"metrics.views": "面流量", "metrics.views": "面流量",
"metrics.visitors": "獨立訪客" "metrics.visitors": "獨立訪客"
} }

View File

@ -31,75 +31,44 @@ import {
zhCN, zhCN,
zhTW, zhTW,
ca, ca,
hu,
} from 'date-fns/locale'; } from 'date-fns/locale';
import arSAMessages from 'lang-compiled/ar-SA.json';
import enMessages from 'lang-compiled/en-US.json';
import nlMessages from 'lang-compiled/nl-NL.json';
import zhCNMessages from 'lang-compiled/zh-CN.json';
import zhTWMessages from 'lang-compiled/zh-TW.json';
import trTRMessages from 'lang-compiled/tr-TR.json';
import ruRUMessages from 'lang-compiled/ru-RU.json';
import deDEMessages from 'lang-compiled/de-DE.json';
import jaMessages from 'lang-compiled/ja-JP.json';
import esMXMessages from 'lang-compiled/es-MX.json';
import frMessages from 'lang-compiled/fr-FR.json';
import mnMNMessages from 'lang-compiled/mn-MN.json';
import daMessages from 'lang-compiled/da-DK.json';
import svMessages from 'lang-compiled/sv-SE.json';
import grMessages from 'lang-compiled/el-GR.json';
import foMessages from 'lang-compiled/fo-FO.json';
import ptMessages from 'lang-compiled/pt-PT.json';
import ptBRMessages from 'lang-compiled/pt-BR.json';
import roMessages from 'lang-compiled/ro-RO.json';
import nbNOMessages from 'lang-compiled/nb-NO.json';
import idMessages from 'lang-compiled/id-ID.json';
import ukMessages from 'lang-compiled/uk-UA.json';
import fiMessages from 'lang-compiled/fi-FI.json';
import csMessages from 'lang-compiled/cs-CZ.json';
import skMessages from 'lang-compiled/sk-SK.json';
import plMessages from 'lang-compiled/pl-PL.json';
import taMessages from 'lang-compiled/ta-IN.json';
import hiMessages from 'lang-compiled/hi-IN.json';
import heMessages from 'lang-compiled/he-IL.json';
import itMessages from 'lang-compiled/it-IT.json';
import faIRMessages from 'lang-compiled/fa-IR.json';
import msMYMessages from 'lang-compiled/ms-MY.json';
import caMessages from 'lang-compiled/ca-ES.json';
export const messages = { export const languages = {
'ar-SA': arSAMessages, 'ar-SA': { label: 'العربية', display: 'ar' },
'en-US': enMessages, 'zh-CN': { label: '中文', display: 'cn' },
'nl-NL': nlMessages, 'zh-TW': { label: '中文(繁體)', display: 'tw' },
'zh-CN': zhCNMessages, 'ca-ES': { label: 'Català', display: 'ca' },
'zh-TW': zhTWMessages, 'cs-CZ': { label: 'Čeština', display: 'cs' },
'de-DE': deDEMessages, 'da-DK': { label: 'Dansk', display: 'da' },
'ru-RU': ruRUMessages, 'de-DE': { label: 'Deutsch', display: 'de' },
'tr-TR': trTRMessages, 'en-US': { label: 'English', display: 'en' },
'ja-JP': jaMessages, 'es-MX': { label: 'Español', display: 'es' },
'es-MX': esMXMessages, 'fa-IR': { label: 'فارسی', display: 'fa' },
'fr-FR': frMessages, 'fo-FO': { label: 'Føroyskt', display: 'fo' },
'mn-MN': mnMNMessages, 'fr-FR': { label: 'Français', display: 'fr' },
'da-DK': daMessages, 'el-GR': { label: 'Ελληνικά', display: 'el' },
'sv-SE': svMessages, 'he-IL': { label: 'עברית', display: 'he' },
'el-GR': grMessages, 'hi-IN': { label: 'हिन्दी', display: 'hi' },
'fo-FO': foMessages, 'hu-HU': { label: 'Hungarian', display: 'hu' },
'pt-PT': ptMessages, 'it-IT': { label: 'Italiano', display: 'it' },
'pt-BR': ptBRMessages, 'id-ID': { label: 'Bahasa Indonesia', display: 'id' },
'ro-RO': roMessages, 'ja-JP': { label: '日本語', display: 'ja' },
'nb-NO': nbNOMessages, 'ms-MY': { label: 'Malay', display: 'ms' },
'id-ID': idMessages, 'mn-MN': { label: 'Монгол', display: 'mn' },
'uk-UA': ukMessages, 'nl-NL': { label: 'Nederlands', display: 'nl' },
'fi-FI': fiMessages, 'nb-NO': { label: 'Norsk Bokmål', display: 'nb' },
'cs-CZ': csMessages, 'pl-PL': { label: 'Polski', display: 'pl' },
'sk-SK': skMessages, 'pt-PT': { label: 'Português', display: 'pt' },
'pl-PL': plMessages, 'pt-BR': { label: 'Português do Brasil', display: 'pt-BR' },
'ta-IN': taMessages, 'ru-RU': { label: 'Русский', display: 'ru' },
'hi-IN': hiMessages, 'ro-RO': { label: 'Română', display: 'ro' },
'he-IL': heMessages, 'sk-SK': { label: 'Slovenčina', display: 'sk' },
'it-IT': itMessages, 'fi-FI': { label: 'Suomi', display: 'fi' },
'fa-IR': faIRMessages, 'sv-SE': { label: 'Svenska', display: 'sv' },
'ms-MY': msMYMessages, 'ta-IN': { label: 'தமிழ்', display: 'ta' },
'ca-ES': caMessages, 'tr-TR': { label: 'Türkçe', display: 'tr' },
'uk-UA': { label: 'українська', display: 'uk' },
}; };
export const rtlLocales = ['ar-SA', 'fa-IR']; export const rtlLocales = ['ar-SA', 'fa-IR'];
@ -138,40 +107,5 @@ export const dateLocales = {
'fa-IR': faIR, 'fa-IR': faIR,
'ms-MY': ms, 'ms-MY': ms,
'ca-ES': ca, 'ca-ES': ca,
'hu-HU': hu,
}; };
export const menuOptions = [
{ label: 'العربية', value: 'ar-SA', display: 'ar' },
{ label: '中文', value: 'zh-CN', display: 'cn' },
{ label: '中文(繁體)', value: 'zh-TW', display: 'tw' },
{ label: 'Català', value: 'ca-ES', display: 'ca' },
{ label: 'Čeština', value: 'cs-CZ', display: 'cs' },
{ label: 'Dansk', value: 'da-DK', display: 'da' },
{ label: 'Deutsch', value: 'de-DE', display: 'de' },
{ label: 'English', value: 'en-US', display: 'en' },
{ label: 'Español', value: 'es-MX', display: 'es' },
{ label: 'فارسی', value: 'fa-IR', display: 'fa' },
{ label: 'Føroyskt', value: 'fo-FO', display: 'fo' },
{ label: 'Français', value: 'fr-FR', display: 'fr' },
{ label: 'Ελληνικά', value: 'el-GR', display: 'el' },
{ label: 'עברית', value: 'he-IL', display: 'he' },
{ label: 'हिन्दी', value: 'hi-IN', display: 'hi' },
{ label: 'Italiano', value: 'it-IT', display: 'it' },
{ label: 'Bahasa Indonesia', value: 'id-ID', display: 'id' },
{ label: '日本語', value: 'ja-JP', display: 'ja' },
{ label: 'Malay', value: 'ms-MY', display: 'ms' },
{ label: 'Монгол', value: 'mn-MN', display: 'mn' },
{ label: 'Nederlands', value: 'nl-NL', display: 'nl' },
{ label: 'Norsk Bokmål', value: 'nb-NO', display: 'nb' },
{ label: 'Polski', value: 'pl-PL', display: 'pl' },
{ label: 'Português', value: 'pt-PT', display: 'pt' },
{ label: 'Português do Brasil', value: 'pt-BR', display: 'pt-BR' },
{ label: 'Русский', value: 'ru-RU', display: 'ru' },
{ label: 'Română', value: 'ro-RO', display: 'ro' },
{ label: 'Slovenčina', value: 'sk-SK', display: 'sk' },
{ label: 'Suomi', value: 'fi-FI', display: 'fi' },
{ label: 'Svenska', value: 'sv-SE', display: 'sv' },
{ label: 'தமிழ்', value: 'ta-IN', display: 'ta' },
{ label: 'Türkçe', value: 'tr-TR', display: 'tr' },
{ label: 'українська', value: 'uk-UA', display: 'uk' },
];

View File

@ -10,9 +10,7 @@ module.exports = {
webpack(config) { webpack(config) {
config.module.rules.push({ config.module.rules.push({
test: /\.svg$/, test: /\.svg$/,
issuer: { issuer: /\.js$/,
test: /\.js$/,
},
use: ['@svgr/webpack'], use: ['@svgr/webpack'],
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "umami", "name": "umami",
"version": "1.18.0", "version": "1.19.0",
"description": "A simple, fast, website analytics alternative to Google Analytics. ", "description": "A simple, fast, website analytics alternative to Google Analytics. ",
"author": "Mike Cao <mike@mikecao.com>", "author": "Mike Cao <mike@mikecao.com>",
"license": "MIT", "license": "MIT",
@ -30,7 +30,7 @@
"extract-lang": "formatjs extract '{pages,components}/**/*.js' --out-file build/messages.json", "extract-lang": "formatjs extract '{pages,components}/**/*.js' --out-file build/messages.json",
"merge-lang": "node scripts/merge-lang.js", "merge-lang": "node scripts/merge-lang.js",
"format-lang": "node scripts/format-lang.js", "format-lang": "node scripts/format-lang.js",
"compile-lang": "formatjs compile-folder --ast build lang-compiled", "compile-lang": "formatjs compile-folder --ast build public/lang",
"check-lang": "node scripts/check-lang.js", "check-lang": "node scripts/check-lang.js",
"download-country-names": "node scripts/download-country-names.js", "download-country-names": "node scripts/download-country-names.js",
"loadtest": "node scripts/loadtest.js", "loadtest": "node scripts/loadtest.js",
@ -56,11 +56,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@fontsource/inter": "^4.3.0", "@fontsource/inter": "^4.5.0",
"@fontsource/noto-sans-jp": "^4.3.0", "@fontsource/noto-sans-jp": "^4.5.0",
"@fontsource/noto-sans-sc": "^4.3.0", "@fontsource/noto-sans-sc": "^4.5.0",
"@fontsource/noto-sans-tc": "^4.3.0", "@fontsource/noto-sans-tc": "^4.5.0",
"@prisma/client": "2.23.0", "@prisma/client": "2.26.0",
"@reduxjs/toolkit": "^1.5.1", "@reduxjs/toolkit": "^1.5.1",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"chalk": "^4.1.1", "chalk": "^4.1.1",
@ -68,28 +68,28 @@
"classnames": "^2.3.1", "classnames": "^2.3.1",
"cookie": "^0.4.1", "cookie": "^0.4.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns": "^2.21.3", "date-fns": "^2.22.1",
"date-fns-tz": "^1.0.12", "date-fns-tz": "^1.1.4",
"detect-browser": "^5.2.0", "detect-browser": "^5.2.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"formik": "^2.2.7", "formik": "^2.2.9",
"immer": "^9.0.2", "immer": "^9.0.5",
"ipaddr.js": "^2.0.0", "ipaddr.js": "^2.0.1",
"is-localhost-ip": "^1.4.0", "is-localhost-ip": "^1.4.0",
"isbot": "^3.0.26", "isbot": "^3.1.0",
"jose": "2.0.5", "jose": "2.0.5",
"maxmind": "^4.3.1", "maxmind": "^4.3.2",
"moment-timezone": "^0.5.33", "moment-timezone": "^0.5.33",
"next": "^10.2.2", "next": "^11.0.1",
"prompts": "2.4.1", "prompts": "2.4.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-intl": "^5.16.0", "react-intl": "^5.20.4",
"react-redux": "^7.2.4", "react-redux": "^7.2.4",
"react-simple-maps": "^2.3.0", "react-simple-maps": "^2.3.0",
"react-spring": "^8.0.27", "react-spring": "^8.0.27",
"react-tooltip": "^4.2.19", "react-tooltip": "^4.2.21",
"react-use-measure": "^2.0.4", "react-use-measure": "^2.0.4",
"react-window": "^1.8.6", "react-window": "^1.8.6",
"redux": "^4.1.0", "redux": "^4.1.0",
@ -102,7 +102,7 @@
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@formatjs/cli": "^4.2.15", "@formatjs/cli": "^4.2.27",
"@rollup/plugin-buble": "^0.21.3", "@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.4", "@rollup/plugin-replace": "^2.3.4",
@ -110,10 +110,11 @@
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"del": "^6.0.0", "del": "^6.0.0",
"dotenv-cli": "^4.0.0", "dotenv-cli": "^4.0.0",
"eslint": "^7.26.0", "eslint": "^7.30.0",
"eslint-config-next": "^11.0.1",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.23.2", "eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.2.0",
"extract-react-intl-messages": "^4.1.1", "extract-react-intl-messages": "^4.1.1",
"husky": "^4.3.8", "husky": "^4.3.8",
@ -125,9 +126,9 @@
"postcss-import": "^13.0.0", "postcss-import": "^13.0.0",
"postcss-preset-env": "^6.7.0", "postcss-preset-env": "^6.7.0",
"postcss-rtlcss": "^3.3.2", "postcss-rtlcss": "^3.3.2",
"prettier": "^2.3.0", "prettier": "^2.3.2",
"prettier-eslint": "^12.0.0", "prettier-eslint": "^12.0.0",
"prisma": "2.23.0", "prisma": "2.26.0",
"rollup": "^2.48.0", "rollup": "^2.48.0",
"rollup-plugin-hashbang": "^2.2.2", "rollup-plugin-hashbang": "^2.2.2",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",

View File

@ -6,7 +6,6 @@ import { Provider } from 'react-redux';
import { useStore } from 'redux/store'; import { useStore } from 'redux/store';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
import useForceSSL from 'hooks/useForceSSL'; import useForceSSL from 'hooks/useForceSSL';
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';
@ -14,7 +13,7 @@ import '@fontsource/inter/400.css';
import '@fontsource/inter/600.css'; import '@fontsource/inter/600.css';
const Intl = ({ children }) => { const Intl = ({ children }) => {
const [locale] = useLocale(); const { locale, messages } = useLocale();
const Wrapper = ({ children }) => <span className={locale}>{children}</span>; const Wrapper = ({ children }) => <span className={locale}>{children}</span>;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1119
yarn.lock

File diff suppressed because it is too large Load Diff