diff --git a/.github/ISSUE_TEMPLATE/2.feature_request.yml b/.github/ISSUE_TEMPLATE/2.feature_request.yml index 3034767bbf..529a6c7328 100644 --- a/.github/ISSUE_TEMPLATE/2.feature_request.yml +++ b/.github/ISSUE_TEMPLATE/2.feature_request.yml @@ -1,10 +1,9 @@ -name: "✨ Feature Request" +name: '✨ Feature Request' description: Create a feature or enhancement request for Umami. -labels: ['enhancement'] body: - type: textarea attributes: label: Describe the feature or enhancement description: A clear and concise description of what the feature or enhancement is. validations: - required: true \ No newline at end of file + required: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c24c2e6d01..c140f626c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,9 @@ jobs: db-type: mysql steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' diff --git a/assets/add-user.svg b/assets/add-user.svg index 9d0544c638..c6b4f484f6 100644 --- a/assets/add-user.svg +++ b/assets/add-user.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/clock.svg b/assets/clock.svg index 9c2a9a41d8..ab4c1dec5c 100644 --- a/assets/clock.svg +++ b/assets/clock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/dashboard.svg b/assets/dashboard.svg index 11859d28af..2090e5dcd3 100644 --- a/assets/dashboard.svg +++ b/assets/dashboard.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/expand.svg b/assets/expand.svg new file mode 100644 index 0000000000..43b9036f92 --- /dev/null +++ b/assets/expand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/funnel.svg b/assets/funnel.svg new file mode 100644 index 0000000000..63fb715894 --- /dev/null +++ b/assets/funnel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/lightbulb.svg b/assets/lightbulb.svg new file mode 100644 index 0000000000..4ff96dcca0 --- /dev/null +++ b/assets/lightbulb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/lock.svg b/assets/lock.svg index c13fb7c716..27fcc5e1bd 100644 --- a/assets/lock.svg +++ b/assets/lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/logo.svg b/assets/logo.svg index d2c71326b8..b139531324 100644 --- a/assets/logo.svg +++ b/assets/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/nodes.svg b/assets/nodes.svg new file mode 100644 index 0000000000..b3e22a75d0 --- /dev/null +++ b/assets/nodes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/overview.svg b/assets/overview.svg new file mode 100644 index 0000000000..ec44b4ea39 --- /dev/null +++ b/assets/overview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/profile.svg b/assets/profile.svg index 133b1bc162..6a1af5a000 100644 --- a/assets/profile.svg +++ b/assets/profile.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/reports.svg b/assets/reports.svg new file mode 100644 index 0000000000..66dfc325ed --- /dev/null +++ b/assets/reports.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/user.svg b/assets/user.svg index a75cbb8d07..245a67f6f4 100644 --- a/assets/user.svg +++ b/assets/user.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/users.svg b/assets/users.svg index f775ea912a..7036a22c0f 100644 --- a/assets/users.svg +++ b/assets/users.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/website.svg b/assets/website.svg index cfa9e565ee..6096a6502b 100644 --- a/assets/website.svg +++ b/assets/website.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/components/common/NoData.js b/components/common/Empty.js similarity index 57% rename from components/common/NoData.js rename to components/common/Empty.js index e9c95754ca..95681b1676 100644 --- a/components/common/NoData.js +++ b/components/common/Empty.js @@ -1,15 +1,15 @@ import classNames from 'classnames'; -import styles from './NoData.module.css'; +import styles from './Empty.module.css'; import useMessages from 'hooks/useMessages'; -export function NoData({ className }) { +export function Empty({ message, className }) { const { formatMessage, messages } = useMessages(); return (
- {formatMessage(messages.noDataAvailable)} + {message || formatMessage(messages.noDataAvailable)}
); } -export default NoData; +export default Empty; diff --git a/components/common/NoData.module.css b/components/common/Empty.module.css similarity index 100% rename from components/common/NoData.module.css rename to components/common/Empty.module.css diff --git a/components/common/HamburgerButton.js b/components/common/HamburgerButton.js index f4b1285955..48c8077087 100644 --- a/components/common/HamburgerButton.js +++ b/components/common/HamburgerButton.js @@ -15,7 +15,6 @@ export function HamburgerButton() { label: formatMessage(labels.dashboard), url: '/dashboard', }, - { label: formatMessage(labels.realtime), url: '/realtime' }, !cloudMode && { label: formatMessage(labels.settings), url: '/settings', diff --git a/components/common/HoverTooltip.js b/components/common/HoverTooltip.js index 2a98ab84cb..614841dfe7 100644 --- a/components/common/HoverTooltip.js +++ b/components/common/HoverTooltip.js @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import { Tooltip } from 'react-basics'; import styles from './HoverTooltip.module.css'; -export function HoverTooltip({ tooltip }) { +export function HoverTooltip({ children }) { const [position, setPosition] = useState({ x: -1000, y: -1000 }); useEffect(() => { @@ -18,9 +18,9 @@ export function HoverTooltip({ tooltip }) { }, []); return ( -
- -
+ + {children} + ); } diff --git a/components/common/HoverTooltip.module.css b/components/common/HoverTooltip.module.css index ec1abc1c15..c4bb76ea93 100644 --- a/components/common/HoverTooltip.module.css +++ b/components/common/HoverTooltip.module.css @@ -1,43 +1,6 @@ -.chart { - position: relative; -} - .tooltip { position: fixed; pointer-events: none; z-index: var(--z-index-popup); -} - -.content { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - text-align: center; -} - -.title { - font-size: var(--font-size-xs); - font-weight: 600; -} - -.metric { - display: flex; - justify-content: center; - align-items: center; - font-size: var(--font-size-sm); - font-weight: 600; -} - -.dot { - position: relative; - overflow: hidden; - border-radius: 100%; - margin-right: 8px; - background: var(--base50); -} - -.color { - width: 10px; - height: 10px; + transform: translate(-50%, calc(-100% - 5px)); } diff --git a/components/common/WorldMap.js b/components/common/WorldMap.js index 55a13f0b17..9c91e4a477 100644 --- a/components/common/WorldMap.js +++ b/components/common/WorldMap.js @@ -4,7 +4,7 @@ import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simp import classNames from 'classnames'; import { colord } from 'colord'; import HoverTooltip from 'components/common/HoverTooltip'; -import { ISO_COUNTRIES, THEME_COLORS, MAP_FILE } from 'lib/constants'; +import { ISO_COUNTRIES, MAP_FILE } from 'lib/constants'; import useTheme from 'hooks/useTheme'; import useCountryNames from 'hooks/useCountryNames'; import useLocale from 'hooks/useLocale'; @@ -14,17 +14,8 @@ import styles from './WorldMap.module.css'; export function WorldMap({ data, className }) { const { basePath } = useRouter(); - const [tooltip, setTooltip] = useState(); - const [theme] = useTheme(); - const colors = useMemo( - () => ({ - baseColor: THEME_COLORS[theme].primary, - fillColor: THEME_COLORS[theme].gray100, - strokeColor: THEME_COLORS[theme].primary, - hoverColor: THEME_COLORS[theme].primary, - }), - [theme], - ); + const [tooltip, setTooltipPopup] = useState(); + const { theme, colors } = useTheme(); const { locale } = useLocale(); const countryNames = useCountryNames(locale); const metrics = useMemo(() => (data ? percentFilter(data) : []), [data]); @@ -34,10 +25,10 @@ export function WorldMap({ data, className }) { const country = metrics?.find(({ x }) => x === code); if (!country) { - return colors.fillColor; + return colors.map.fillColor; } - return colord(colors.baseColor) + return colord(colors.map.baseColor) [theme === 'light' ? 'lighten' : 'darken'](0.4 * (1.0 - country.z / 100)) .toHex(); } @@ -49,7 +40,7 @@ export function WorldMap({ data, className }) { function handleHover(code) { if (code === 'AQ') return; const country = metrics?.find(({ x }) => x === code); - setTooltip(`${countryNames[code]}: ${formatLongNumber(country?.y || 0)} visitors`); + setTooltipPopup(`${countryNames[code]}: ${formatLongNumber(country?.y || 0)} visitors`); } return ( @@ -70,15 +61,15 @@ export function WorldMap({ data, className }) { key={geo.rsmKey} geography={geo} fill={getFillColor(code)} - stroke={colors.strokeColor} + stroke={colors.map.strokeColor} opacity={getOpacity(code)} style={{ default: { outline: 'none' }, - hover: { outline: 'none', fill: colors.hoverColor }, + hover: { outline: 'none', fill: colors.map.hoverColor }, pressed: { outline: 'none' }, }} onMouseOver={() => handleHover(code)} - onMouseOut={() => setTooltip(null)} + onMouseOut={() => setTooltipPopup(null)} /> ); }); @@ -86,7 +77,7 @@ export function WorldMap({ data, className }) { - {tooltip && } + {tooltip && {tooltip}} ); } diff --git a/components/icons.ts b/components/icons.ts index f3040bbd4b..efd6914bb9 100644 --- a/components/icons.ts +++ b/components/icons.ts @@ -10,7 +10,10 @@ import Globe from 'assets/globe.svg'; import Lock from 'assets/lock.svg'; import Logo from 'assets/logo.svg'; import Moon from 'assets/moon.svg'; +import Nodes from 'assets/nodes.svg'; +import Overview from 'assets/overview.svg'; import Profile from 'assets/profile.svg'; +import Reports from 'assets/reports.svg'; import Sun from 'assets/sun.svg'; import User from 'assets/user.svg'; import Users from 'assets/users.svg'; @@ -29,7 +32,10 @@ const icons = { Lock, Logo, Moon, + Nodes, + Overview, Profile, + Reports, Sun, User, Users, diff --git a/components/input/DateFilter.js b/components/input/DateFilter.js index b6c1ee7288..7fc4319de1 100644 --- a/components/input/DateFilter.js +++ b/components/input/DateFilter.js @@ -3,31 +3,22 @@ import { Icon, Modal, Dropdown, Item, Text, Flexbox } from 'react-basics'; import { endOfYear, isSameDay } from 'date-fns'; import DatePickerForm from 'components/metrics/DatePickerForm'; import useLocale from 'hooks/useLocale'; -import { dateFormat, getDateRangeValues } from 'lib/date'; +import { dateFormat } from 'lib/date'; import Icons from 'components/icons'; -import useApi from 'hooks/useApi'; -import useDateRange from 'hooks/useDateRange'; import useMessages from 'hooks/useMessages'; -export function DateFilter({ websiteId, value, className }) { +export function DateFilter({ + value, + startDate, + endDate, + className, + onChange, + showAllTime = false, + alignment = 'end', +}) { const { formatMessage, labels } = useMessages(); - const { get } = useApi(); - const [dateRange, setDateRange] = useDateRange(websiteId); - const { startDate, endDate } = dateRange; const [showPicker, setShowPicker] = useState(false); - async function handleDateChange(value) { - if (value === 'all' && websiteId) { - const data = await get(`/websites/${websiteId}`); - - if (data) { - setDateRange({ value, ...getDateRangeValues(new Date(data.createdAt), Date.now()) }); - } - } else if (value !== 'all') { - setDateRange(value); - } - } - const options = [ { label: formatMessage(labels.today), value: '1day' }, { @@ -61,7 +52,7 @@ export function DateFilter({ websiteId, value, className }) { value: '90day', }, { label: formatMessage(labels.thisYear), value: '1year' }, - websiteId && { + showAllTime && { label: formatMessage(labels.allTime), value: 'all', divider: true, @@ -74,7 +65,7 @@ export function DateFilter({ websiteId, value, className }) { ].filter(n => n); const renderValue = value => { - return value === 'custom' ? ( + return value.startsWith('range') ? ( handleChange('custom')} /> ) : ( options.find(e => e.value === value).label @@ -86,12 +77,12 @@ export function DateFilter({ websiteId, value, className }) { setShowPicker(true); return; } - handleDateChange(value); + onChange(value); }; const handlePickerChange = value => { setShowPicker(false); - handleDateChange(value); + onChange(value); }; const handleClose = () => setShowPicker(false); @@ -103,7 +94,8 @@ export function DateFilter({ websiteId, value, className }) { items={options} renderValue={renderValue} value={value} - alignment="end" + alignment={alignment} + placeholder={formatMessage(labels.selectDate)} onChange={handleChange} > {({ label, value, divider }) => ( diff --git a/components/input/LanguageButton.js b/components/input/LanguageButton.js index 1297d6c2ee..d4c1cbc397 100644 --- a/components/input/LanguageButton.js +++ b/components/input/LanguageButton.js @@ -9,8 +9,10 @@ export function LanguageButton() { const { locale, saveLocale, dir } = useLocale(); const items = Object.keys(languages).map(key => ({ ...languages[key], value: key })); - function handleSelect(value) { + function handleSelect(value, close, e) { + e.stopPropagation(); saveLocale(value); + close(); } return ( @@ -21,24 +23,28 @@ export function LanguageButton() { -
- {items.map(({ value, label }) => { - return ( -
- {label} - {value === locale && ( - - - - )} -
- ); - })} -
+ {close => { + return ( +
+ {items.map(({ value, label }) => { + return ( +
+ {label} + {value === locale && ( + + + + )} +
+ ); + })} +
+ ); + }}
); diff --git a/components/input/LanguageButton.module.css b/components/input/LanguageButton.module.css index 16d6197872..3d4c0c5697 100644 --- a/components/input/LanguageButton.module.css +++ b/components/input/LanguageButton.module.css @@ -1,8 +1,7 @@ .menu { display: flex; flex-flow: row wrap; - min-width: 600px; - max-width: 100vw; + min-width: 640px; padding: 10px; background: var(--base50); z-index: var(--z-index-popup); diff --git a/components/input/LogoutButton.js b/components/input/LogoutButton.js index 3314956ee3..4a15cd68b0 100644 --- a/components/input/LogoutButton.js +++ b/components/input/LogoutButton.js @@ -1,4 +1,4 @@ -import { Button, Icon, Icons, Tooltip } from 'react-basics'; +import { Button, Icon, Icons, TooltipPopup } from 'react-basics'; import Link from 'next/link'; import useMessages from 'hooks/useMessages'; @@ -6,13 +6,13 @@ export function LogoutButton({ tooltipPosition = 'top' }) { const { formatMessage, labels } = useMessages(); return ( - + - + ); } diff --git a/components/input/RefreshButton.js b/components/input/RefreshButton.js index b3e2b81566..444f324743 100644 --- a/components/input/RefreshButton.js +++ b/components/input/RefreshButton.js @@ -1,4 +1,4 @@ -import { LoadingButton, Icon, Tooltip } from 'react-basics'; +import { LoadingButton, Icon, TooltipPopup } from 'react-basics'; import { setWebsiteDateRange } from 'store/websites'; import useDateRange from 'hooks/useDateRange'; import Icons from 'components/icons'; @@ -19,13 +19,13 @@ export function RefreshButton({ websiteId, isLoading }) { } return ( - + - + ); } diff --git a/components/input/ThemeButton.js b/components/input/ThemeButton.js index b945ab7dcd..8ab0cdcd59 100644 --- a/components/input/ThemeButton.js +++ b/components/input/ThemeButton.js @@ -5,7 +5,7 @@ import Icons from 'components/icons'; import styles from './ThemeButton.module.css'; export function ThemeButton() { - const [theme, setTheme] = useTheme(); + const { theme, saveTheme } = useTheme(); const transitions = useTransition(theme, { initial: { opacity: 1 }, @@ -21,7 +21,7 @@ export function ThemeButton() { }); function handleClick() { - setTheme(theme === 'light' ? 'dark' : 'light'); + saveTheme(theme === 'light' ? 'dark' : 'light'); } return ( diff --git a/components/input/WebsiteDateFilter.js b/components/input/WebsiteDateFilter.js new file mode 100644 index 0000000000..9172197443 --- /dev/null +++ b/components/input/WebsiteDateFilter.js @@ -0,0 +1,35 @@ +import useApi from 'hooks/useApi'; +import useDateRange from 'hooks/useDateRange'; +import DateFilter from './DateFilter'; +import styles from './WebsiteDateFilter.module.css'; + +export default function WebsiteDateFilter({ websiteId }) { + const { get } = useApi(); + const [dateRange, setDateRange] = useDateRange(websiteId); + const { value, startDate, endDate } = dateRange; + + const handleChange = async value => { + if (value === 'all' && websiteId) { + const data = await get(`/websites/${websiteId}`); + + if (data) { + const start = new Date(data.createdAt).getTime(); + const end = Date.now(); + setDateRange(`range:${start}:${end}`); + } + } else if (value !== 'all') { + setDateRange(value); + } + }; + + return ( + + ); +} diff --git a/components/input/WebsiteDateFilter.module.css b/components/input/WebsiteDateFilter.module.css new file mode 100644 index 0000000000..13234c55c3 --- /dev/null +++ b/components/input/WebsiteDateFilter.module.css @@ -0,0 +1,3 @@ +.dropdown { + min-width: 200px; +} diff --git a/components/input/WebsiteSelect.js b/components/input/WebsiteSelect.js index a0ac38e4bc..b77ae57c80 100644 --- a/components/input/WebsiteSelect.js +++ b/components/input/WebsiteSelect.js @@ -19,7 +19,6 @@ export function WebsiteSelect({ websiteId, onSelect }) { onChange={onSelect} alignment="end" placeholder={formatMessage(labels.selectWebsite)} - style={{ width: 200 }} > {({ id, name }) => {name}} diff --git a/components/layout/AppLayout.module.css b/components/layout/AppLayout.module.css index 6cc9e41420..a83039cef0 100644 --- a/components/layout/AppLayout.module.css +++ b/components/layout/AppLayout.module.css @@ -2,6 +2,7 @@ display: grid; grid-template-rows: max-content 1fr; grid-template-columns: 1fr; + overflow: hidden; } .nav { diff --git a/components/layout/Grid.module.css b/components/layout/Grid.module.css index 655e4ea273..dc2e8ff6c5 100644 --- a/components/layout/Grid.module.css +++ b/components/layout/Grid.module.css @@ -29,6 +29,7 @@ .row > .col { border-top: 1px solid var(--base300); + border-inline-start: 0; border-inline-end: 0; padding: 20px 0; } diff --git a/components/layout/NavBar.js b/components/layout/NavBar.js index 5a6c877ec4..97eaa46c3d 100644 --- a/components/layout/NavBar.js +++ b/components/layout/NavBar.js @@ -18,7 +18,6 @@ export function NavBar() { const links = [ { label: formatMessage(labels.dashboard), url: '/dashboard' }, - { label: formatMessage(labels.realtime), url: '/realtime' }, !cloudMode && { label: formatMessage(labels.settings), url: '/settings' }, ].filter(n => n); diff --git a/components/layout/NavBar.module.css b/components/layout/NavBar.module.css index 05dce2af3a..dd5085a03a 100644 --- a/components/layout/NavBar.module.css +++ b/components/layout/NavBar.module.css @@ -27,7 +27,6 @@ gap: 10px; font-size: 16px; font-weight: 700; - cursor: pointer; min-width: 0; } diff --git a/components/layout/NavGroup.js b/components/layout/NavGroup.js index b9e7155d0a..94f9d8e663 100644 --- a/components/layout/NavGroup.js +++ b/components/layout/NavGroup.js @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { Icon, Text, Tooltip } from 'react-basics'; +import { Icon, Text, TooltipPopup } from 'react-basics'; import classNames from 'classnames'; import { useRouter } from 'next/router'; import Link from 'next/link'; @@ -36,7 +36,7 @@ export function NavGroup({
{items.map(({ label, url, icon, divider }) => { return ( - + {icon} {label} - + ); })}
diff --git a/components/layout/PageHeader.js b/components/layout/PageHeader.js index bf243c2171..f13631409f 100644 --- a/components/layout/PageHeader.js +++ b/components/layout/PageHeader.js @@ -1,10 +1,11 @@ +import classNames from 'classnames'; import React from 'react'; import styles from './PageHeader.module.css'; -export function PageHeader({ title, children }) { +export function PageHeader({ title, children, className }) { return ( -
-
{title}
+
+ {title &&
{title}
}
{children}
); diff --git a/components/layout/PageHeader.module.css b/components/layout/PageHeader.module.css index 5ea85b700d..03a1c7c8a8 100644 --- a/components/layout/PageHeader.module.css +++ b/components/layout/PageHeader.module.css @@ -4,7 +4,6 @@ align-items: center; align-content: center; align-self: stretch; - margin-bottom: 40px; flex-wrap: wrap; } @@ -23,6 +22,7 @@ font-weight: 700; gap: 20px; height: 60px; + flex: 1; } .actions { diff --git a/components/layout/ReportsLayout.js b/components/layout/ReportsLayout.js new file mode 100644 index 0000000000..fd63a67e7b --- /dev/null +++ b/components/layout/ReportsLayout.js @@ -0,0 +1,23 @@ +import { Column, Row } from 'react-basics'; +import styles from './ReportsLayout.module.css'; + +export function SettingsLayout({ children, filter, header }) { + return ( + <> + {header} + + {filter && ( + +

Filters

+ {filter} +
+ )} + + {children} + +
+ + ); +} + +export default SettingsLayout; diff --git a/components/layout/ReportsLayout.module.css b/components/layout/ReportsLayout.module.css new file mode 100644 index 0000000000..6922665fa2 --- /dev/null +++ b/components/layout/ReportsLayout.module.css @@ -0,0 +1,23 @@ +.filter { + margin-top: 30px; + min-width: 200px; + max-width: 100vw; + padding: 10px; + background: var(--base50); + border-radius: 5px; + border: 1px solid var(--border-color); +} + +.filter h2 { + padding-bottom: 20px; +} + +.content { + min-height: 50vh; +} + +@media only screen and (max-width: 768px) { + .menu { + display: none; + } +} diff --git a/components/messages.js b/components/messages.js index aa268225f9..d41c443019 100644 --- a/components/messages.js +++ b/components/messages.js @@ -15,9 +15,11 @@ export const labels = defineMessages({ password: { id: 'label.password', defaultMessage: 'Password' }, role: { id: 'label.role', defaultMessage: 'Role' }, user: { id: 'label.user', defaultMessage: 'User' }, + viewOnly: { id: 'label.view-only', defaultMessage: 'View only' }, admin: { id: 'label.admin', defaultMessage: 'Administrator' }, confirm: { id: 'label.confirm', defaultMessage: 'Confirm' }, details: { id: 'label.details', defaultMessage: 'Details' }, + website: { id: 'label.website', defaultMessage: 'Website' }, websites: { id: 'label.websites', defaultMessage: 'Websites' }, created: { id: 'label.created', defaultMessage: 'Created' }, edit: { id: 'label.edit', defaultMessage: 'Edit' }, @@ -47,6 +49,7 @@ export const labels = defineMessages({ deleteWebsite: { id: 'label.delete-website', defaultMessage: 'Delete website' }, reset: { id: 'label.reset', defaultMessage: 'Reset' }, addWebsite: { id: 'label.add-website', defaultMessage: 'Add website' }, + addDescription: { id: 'label.add-description', defaultMessage: 'Add description' }, changePassword: { id: 'label.change-password', defaultMessage: 'Change password' }, currentPassword: { id: 'label.current-password', defaultMessage: 'Current password' }, newPassword: { id: 'label.new-password', defaultMessage: 'New password' }, @@ -79,7 +82,8 @@ export const labels = defineMessages({ countries: { id: 'label.countries', defaultMessage: 'Countries' }, languages: { id: 'label.languages', defaultMessage: 'Languages' }, events: { id: 'label.events', defaultMessage: 'Events' }, - query: { id: 'label.query-parameters', defaultMessage: 'Query parameters' }, + query: { id: 'label.query', defaultMessage: 'Query' }, + queryParameters: { id: 'label.query-parameters', defaultMessage: 'Query parameters' }, back: { id: 'label.back', defaultMessage: 'Back' }, visitors: { id: 'label.visitors', defaultMessage: 'Visitors' }, filterCombined: { id: 'label.filter-combined', defaultMessage: 'Combined' }, @@ -97,6 +101,7 @@ export const labels = defineMessages({ allTime: { id: 'label.all-time', defaultMessage: 'All time' }, customRange: { id: 'label.custom-range', defaultMessage: 'Custom range' }, selectWebsite: { id: 'label.select-website', defaultMessage: 'Select website' }, + selectDate: { id: 'label.select-date', defaultMessage: 'Select date' }, all: { id: 'label.all', defaultMessage: 'All' }, sessions: { id: 'label.sessions', defaultMessage: 'Sessions' }, pageNotFound: { id: 'message.page-not-found', defaultMessage: 'Page not found' }, @@ -117,6 +122,43 @@ export const labels = defineMessages({ view: { id: 'label.view', defaultMessage: 'View' }, cities: { id: 'label.cities', defaultMessage: 'Cities' }, regions: { id: 'label.regions', defaultMessage: 'Regions' }, + reports: { id: 'label.reports', defaultMessage: 'Reports' }, + eventData: { id: 'label.event-data', defaultMessage: 'Event data' }, + funnel: { id: 'label.funnel', defaultMessage: 'Funnel' }, + url: { id: 'label.url', defaultMessage: 'URL' }, + urls: { id: 'label.urls', defaultMessage: 'URLs' }, + add: { id: 'label.add', defaultMessage: 'Add' }, + window: { id: 'label.window', defaultMessage: 'Window' }, + runQuery: { id: 'label.run-query', defaultMessage: 'Run query' }, + field: { id: 'label.field', defaultMessage: 'Field' }, + fields: { id: 'label.fields', defaultMessage: 'Fields' }, + createReport: { id: 'labels.create-report', defaultMessage: 'Create report' }, + description: { id: 'labels.description', defaultMessage: 'Description' }, + untitled: { id: 'labels.untitled', defaultMessage: 'Untitled' }, + type: { id: 'labels.type', defaultMessage: 'Type' }, + filters: { id: 'labels.filters', defaultMessage: 'Filters' }, + breakdown: { id: 'labels.breakdown', defaultMessage: 'Breakdown' }, + true: { id: 'labels.true', defaultMessage: 'True' }, + false: { id: 'labels.false', defaultMessage: 'False' }, + equals: { id: 'labels.equals', defaultMessage: 'Equals' }, + doesNotEqual: { id: 'labels.does-not-equal', defaultMessage: 'Does not equal' }, + greaterThan: { id: 'labels.greater-than', defaultMessage: 'Greater than' }, + lessThan: { id: 'labels.less-than', defaultMessage: 'Less than' }, + greaterThanEquals: { id: 'labels.greater-than-equals', defaultMessage: 'Greater than or equals' }, + lessThanEquals: { id: 'labels.less-than-equals', defaultMessage: 'Less than or equals' }, + contains: { id: 'labels.contains', defaultMessage: 'Contains' }, + doesNotContain: { id: 'labels.does-not-contain', defaultMessage: 'Does not contain' }, + before: { id: 'labels.before', defaultMessage: 'Before' }, + after: { id: 'labels.after', defaultMessage: 'After' }, + total: { id: 'labels.total', defaultMessage: 'Total' }, + sum: { id: 'labels.sum', defaultMessage: 'Sum' }, + average: { id: 'labels.average', defaultMessage: 'Average' }, + min: { id: 'labels.min', defaultMessage: 'Min' }, + max: { id: 'labels.max', defaultMessage: 'Max' }, + unique: { id: 'labels.unique', defaultMessage: 'Unique' }, + value: { id: 'labels.value', defaultMessage: 'Value' }, + overview: { id: 'labels.overview', defaultMessage: 'Overview' }, + totalRecords: { id: 'labels.total-records', defaultMessage: 'Total records' }, }); export const messages = defineMessages({ @@ -158,6 +200,10 @@ export const messages = defineMessages({ id: 'message.team-already-member', defaultMessage: 'You are already a member of the team.', }, + deleteAccount: { + id: 'message.delete-account', + defaultMessage: 'To delete this account, type {confirmation} in the box below to confirm.', + }, deleteWebsite: { id: 'message.delete-website', defaultMessage: 'To delete this website, type {confirmation} in the box below to confirm.', @@ -179,6 +225,10 @@ export const messages = defineMessages({ id: 'message.delete-website-warning', defaultMessage: 'All website data will be deleted.', }, + noResultsFound: { + id: 'messages.no-results-found', + defaultMessage: 'No results were found.', + }, noWebsitesConfigured: { id: 'messages.no-websites-configured', defaultMessage: 'You do not have any websites configured.', @@ -216,4 +266,8 @@ export const messages = defineMessages({ id: 'message.incorrect-username-password', defaultMessage: 'Incorrect username and/or password.', }, + noEventData: { + id: 'message.no-event-data', + defaultMessage: 'No event data is available.', + }, }); diff --git a/components/metrics/BarChart.js b/components/metrics/BarChart.js index cd7070e882..c086017e8e 100644 --- a/components/metrics/BarChart.js +++ b/components/metrics/BarChart.js @@ -1,14 +1,13 @@ -import { useState, useRef, useEffect, useMemo, useCallback } from 'react'; -import { StatusLight, Loading } from 'react-basics'; +import { useState, useRef, useEffect, useCallback } from 'react'; +import { Loading } from 'react-basics'; import classNames from 'classnames'; import Chart from 'chart.js/auto'; import HoverTooltip from 'components/common/HoverTooltip'; import Legend from 'components/metrics/Legend'; -import { formatLongNumber } from 'lib/format'; -import { dateFormat } from 'lib/date'; import useLocale from 'hooks/useLocale'; import useTheme from 'hooks/useTheme'; -import { DEFAULT_ANIMATION_DURATION, THEME_COLORS } from 'lib/constants'; +import { DEFAULT_ANIMATION_DURATION } from 'lib/constants'; +import { renderNumberLabels } from 'lib/charts'; import styles from './BarChart.module.css'; export function BarChart({ @@ -17,84 +16,20 @@ export function BarChart({ animationDuration = DEFAULT_ANIMATION_DURATION, stacked = false, loading = false, - onCreate = () => {}, - onUpdate = () => {}, + renderXLabel, + renderYLabel, + XAxisType = 'time', + YAxisType = 'linear', + renderTooltipPopup, + onCreate, + onUpdate, className, }) { const canvas = useRef(); const chart = useRef(null); - const [tooltip, setTooltip] = useState(null); + const [tooltip, setTooltipPopup] = useState(null); const { locale } = useLocale(); - const [theme] = useTheme(); - - const colors = useMemo( - () => ({ - text: THEME_COLORS[theme].gray700, - line: THEME_COLORS[theme].gray200, - }), - [theme], - ); - - const renderYLabel = label => { - return +label > 1000 ? formatLongNumber(label) : label; - }; - - const renderXLabel = useCallback( - (label, index, values) => { - const d = new Date(values[index].value); - - switch (unit) { - case 'minute': - return dateFormat(d, 'h:mm', locale); - case 'hour': - return dateFormat(d, 'p', locale); - case 'day': - return dateFormat(d, 'MMM d', locale); - case 'month': - return dateFormat(d, 'MMM', locale); - default: - return label; - } - }, - [locale, unit], - ); - - const renderTooltip = useCallback( - model => { - const { opacity, labelColors, dataPoints } = model.tooltip; - - if (!dataPoints?.length || !opacity) { - setTooltip(null); - return; - } - - const formats = { - millisecond: 'T', - second: 'pp', - minute: 'p', - hour: 'h:mm aaa - PP', - day: 'PPPP', - week: 'PPPP', - month: 'LLLL yyyy', - quarter: 'qqq', - year: 'yyyy', - }; - - setTooltip( -
-
{dateFormat(new Date(dataPoints[0].raw.x), formats[unit], locale)}
-
- -
- {formatLongNumber(dataPoints[0].raw.y)} {dataPoints[0].dataset.label} -
-
-
-
, - ); - }, - [unit], - ); + const { theme, colors } = useTheme(); const getOptions = useCallback(() => { return { @@ -115,12 +50,12 @@ export function BarChart({ }, tooltip: { enabled: false, - external: renderTooltip, + external: renderTooltipPopup ? renderTooltipPopup.bind(null, setTooltipPopup) : undefined, }, }, scales: { x: { - type: 'time', + type: XAxisType, stacked: true, time: { unit, @@ -129,34 +64,44 @@ export function BarChart({ display: false, }, border: { - color: colors.line, + color: colors.chart.line, }, ticks: { - color: colors.text, + color: colors.chart.text, autoSkip: false, maxRotation: 0, callback: renderXLabel, }, }, y: { - type: 'linear', + type: YAxisType, min: 0, beginAtZero: true, stacked, grid: { - color: colors.line, + color: colors.chart.line, }, border: { - color: colors.line, + color: colors.chart.line, }, ticks: { color: colors.text, - callback: renderYLabel, + callback: renderYLabel || renderNumberLabels, }, }, }, }; - }, [animationDuration, renderTooltip, renderXLabel, stacked, colors, unit, locale]); + }, [ + animationDuration, + renderTooltipPopup, + renderXLabel, + XAxisType, + YAxisType, + stacked, + colors, + unit, + locale, + ]); const createChart = () => { Chart.defaults.font.family = 'Inter'; @@ -171,11 +116,11 @@ export function BarChart({ options, }); - onCreate(chart.current); + onCreate?.(chart.current); }; const updateChart = () => { - setTooltip(null); + setTooltipPopup(null); datasets.forEach((dataset, index) => { chart.current.data.datasets[index].data = dataset.data; @@ -184,7 +129,7 @@ export function BarChart({ chart.current.options = getOptions(); - onUpdate(chart.current); + onUpdate?.(chart.current); chart.current.update(); }; @@ -206,7 +151,11 @@ export function BarChart({
- {tooltip && } + {tooltip && ( + +
{tooltip}
+
+ )} ); } diff --git a/components/metrics/BarChart.module.css b/components/metrics/BarChart.module.css index 850d1ea721..f2e26db1d0 100644 --- a/components/metrics/BarChart.module.css +++ b/components/metrics/BarChart.module.css @@ -13,9 +13,3 @@ .tooltip .value { text-transform: lowercase; } - -@media only screen and (max-width: 992px) { - .chart { - /*height: 200px;*/ - } -} diff --git a/components/metrics/BrowsersTable.js b/components/metrics/BrowsersTable.js index a8dd34ea54..2920280f66 100644 --- a/components/metrics/BrowsersTable.js +++ b/components/metrics/BrowsersTable.js @@ -2,12 +2,23 @@ import FilterLink from 'components/common/FilterLink'; import MetricsTable from 'components/metrics/MetricsTable'; import { BROWSERS } from 'lib/constants'; import useMessages from 'hooks/useMessages'; +import { useRouter } from 'next/router'; export function BrowsersTable({ websiteId, ...props }) { const { formatMessage, labels } = useMessages(); + const { basePath } = useRouter(); function renderLink({ x: browser }) { - return ; + return ( + + {browser} + + ); } return ( diff --git a/components/metrics/CountriesTable.js b/components/metrics/CountriesTable.js index 2d7b5ceb8d..c72d96ed2e 100644 --- a/components/metrics/CountriesTable.js +++ b/components/metrics/CountriesTable.js @@ -1,13 +1,15 @@ -import MetricsTable from './MetricsTable'; +import { useRouter } from 'next/router'; import FilterLink from 'components/common/FilterLink'; import useCountryNames from 'hooks/useCountryNames'; import useLocale from 'hooks/useLocale'; import useMessages from 'hooks/useMessages'; +import MetricsTable from './MetricsTable'; export function CountriesTable({ websiteId, ...props }) { const { locale } = useLocale(); const countryNames = useCountryNames(locale); const { formatMessage, labels } = useMessages(); + const { basePath } = useRouter(); function renderLink({ x: code }) { return ( @@ -17,7 +19,7 @@ export function CountriesTable({ websiteId, ...props }) { value={countryNames[code] && code} label={countryNames[code]} > - {code} + {code} ); } diff --git a/components/metrics/DataTable.js b/components/metrics/DataTable.js index 086f98ae21..e2e9462df1 100644 --- a/components/metrics/DataTable.js +++ b/components/metrics/DataTable.js @@ -3,10 +3,10 @@ import useMeasure from 'react-use-measure'; import { FixedSizeList } from 'react-window'; import { useSpring, animated, config } from 'react-spring'; import classNames from 'classnames'; -import NoData from 'components/common/NoData'; +import Empty from 'components/common/Empty'; import { formatNumber, formatLongNumber } from 'lib/format'; +import useMessages from 'hooks/useMessages'; import styles from './DataTable.module.css'; -import useMessages from '../../hooks/useMessages'; export function DataTable({ data = [], @@ -55,7 +55,7 @@ export function DataTable({
- {data?.length === 0 && } + {data?.length === 0 && } {virtualize && data.length > 0 ? ( {Row} diff --git a/components/metrics/DataTable.module.css b/components/metrics/DataTable.module.css index c5b2bd7c37..04e12e9be2 100644 --- a/components/metrics/DataTable.module.css +++ b/components/metrics/DataTable.module.css @@ -1,9 +1,9 @@ .table { position: relative; - height: 100%; display: grid; grid-template-rows: fit-content(100%) auto; overflow: hidden; + flex: 1; } .body { diff --git a/components/metrics/DatePickerForm.js b/components/metrics/DatePickerForm.js index 967305916a..53f027bb64 100644 --- a/components/metrics/DatePickerForm.js +++ b/components/metrics/DatePickerForm.js @@ -2,7 +2,6 @@ import { useState } from 'react'; import { Button, ButtonGroup, Calendar } from 'react-basics'; import { isAfter, isBefore, isSameDay } from 'date-fns'; import useLocale from 'hooks/useLocale'; -import { getDateRangeValues } from 'lib/date'; import { getDateLocale } from 'lib/lang'; import { FILTER_DAY, FILTER_RANGE } from 'lib/constants'; import useMessages from 'hooks/useMessages'; @@ -19,7 +18,7 @@ export function DatePickerForm({ const [selected, setSelected] = useState( isSameDay(defaultStartDate, defaultEndDate) ? FILTER_DAY : FILTER_RANGE, ); - const [date, setDate] = useState(defaultStartDate); + const [singleDate, setSingleDate] = useState(defaultStartDate); const [startDate, setStartDate] = useState(defaultStartDate); const [endDate, setEndDate] = useState(defaultEndDate); const { locale } = useLocale(); @@ -27,14 +26,14 @@ export function DatePickerForm({ const disabled = selected === FILTER_DAY - ? isAfter(minDate, date) && isBefore(maxDate, date) + ? isAfter(minDate, singleDate) && isBefore(maxDate, singleDate) : isAfter(startDate, endDate); const handleSave = () => { if (selected === FILTER_DAY) { - onChange({ ...getDateRangeValues(date, date), value: 'custom' }); + onChange(`range:${singleDate.getTime()}:${singleDate.getTime()}`); } else { - onChange({ ...getDateRangeValues(startDate, endDate), value: 'custom' }); + onChange(`range:${startDate.getTime()}:${endDate.getTime()}`); } }; @@ -48,7 +47,12 @@ export function DatePickerForm({
{selected === FILTER_DAY && ( - + )} {selected === FILTER_RANGE && ( <> diff --git a/components/metrics/DevicesTable.js b/components/metrics/DevicesTable.js index 1bc3ac0419..0b8d57086d 100644 --- a/components/metrics/DevicesTable.js +++ b/components/metrics/DevicesTable.js @@ -1,9 +1,11 @@ import MetricsTable from './MetricsTable'; import FilterLink from 'components/common/FilterLink'; import useMessages from 'hooks/useMessages'; +import { useRouter } from 'next/router'; export function DevicesTable({ websiteId, ...props }) { const { formatMessage, labels } = useMessages(); + const { basePath } = useRouter(); function renderLink({ x: device }) { return ( @@ -11,7 +13,14 @@ export function DevicesTable({ websiteId, ...props }) { id="device" value={labels[device] && device} label={formatMessage(labels[device] || labels.unknown)} - /> + > + {device} + ); } diff --git a/components/metrics/EventsChart.js b/components/metrics/EventsChart.js index eb397cc990..82b8c8f712 100644 --- a/components/metrics/EventsChart.js +++ b/components/metrics/EventsChart.js @@ -2,16 +2,15 @@ import { useMemo } from 'react'; import { Loading } from 'react-basics'; import { colord } from 'colord'; import BarChart from './BarChart'; -import { getDateArray, getDateLength } from 'lib/date'; -import useApi from 'hooks/useApi'; -import useDateRange from 'hooks/useDateRange'; -import useTimezone from 'hooks/useTimezone'; -import usePageQuery from 'hooks/usePageQuery'; +import { getDateArray } from 'lib/date'; +import { useApi, useLocale, useDateRange, useTimezone, usePageQuery } from 'hooks'; import { EVENT_COLORS } from 'lib/constants'; +import { renderDateLabels, renderStatusTooltipPopup } from 'lib/charts'; export function EventsChart({ websiteId, className, token }) { const { get, useQuery } = useApi(); const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId); + const { locale } = useLocale(); const [timezone] = useTimezone(); const { query: { url, eventName }, @@ -70,9 +69,10 @@ export function EventsChart({ websiteId, className, token }) { datasets={datasets} unit={unit} height={300} - records={getDateLength(startDate, endDate, unit)} loading={isLoading} stacked + renderXLabel={renderDateLabels(unit, locale)} + renderTooltipPopup={renderStatusTooltipPopup(unit, locale)} /> ); } diff --git a/components/metrics/MetricsBar.js b/components/metrics/MetricsBar.js index 25b93115c4..2b74e42560 100644 --- a/components/metrics/MetricsBar.js +++ b/components/metrics/MetricsBar.js @@ -1,113 +1,27 @@ import { useState } from 'react'; -import { Loading } from 'react-basics'; +import { Loading, cloneChildren } from 'react-basics'; import ErrorMessage from 'components/common/ErrorMessage'; -import useApi from 'hooks/useApi'; -import useDateRange from 'hooks/useDateRange'; -import usePageQuery from 'hooks/usePageQuery'; -import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format'; -import MetricCard from './MetricCard'; -import useMessages from 'hooks/useMessages'; import styles from './MetricsBar.module.css'; +import { formatLongNumber, formatNumber } from 'lib/format'; -export function MetricsBar({ websiteId }) { - const { formatMessage, labels } = useMessages(); - const { get, useQuery } = useApi(); - const [dateRange] = useDateRange(websiteId); - const { startDate, endDate, modified } = dateRange; +export function MetricsBar({ children, isLoading, isFetched, error }) { const [format, setFormat] = useState(true); - const { - query: { url, referrer, os, browser, device, country, region, city }, - } = usePageQuery(); - - const { data, error, isLoading, isFetched } = useQuery( - [ - 'websites:stats', - { websiteId, modified, url, referrer, os, browser, device, country, region, city }, - ], - () => - get(`/websites/${websiteId}/stats`, { - startAt: +startDate, - endAt: +endDate, - url, - referrer, - os, - browser, - device, - country, - region, - city, - }), - ); const formatFunc = format ? n => (n >= 0 ? formatLongNumber(n) : `-${formatLongNumber(Math.abs(n))}`) : formatNumber; - function handleSetFormat() { + const handleSetFormat = () => { setFormat(state => !state); - } - - const { pageviews, uniques, bounces, totaltime } = data || {}; - const num = Math.min(data && uniques.value, data && bounces.value); - const diffs = data && { - pageviews: pageviews.value - pageviews.change, - uniques: uniques.value - uniques.change, - bounces: bounces.value - bounces.change, - totaltime: totaltime.value - totaltime.change, }; return (
{isLoading && !isFetched && } {error && } - {data && !error && isFetched && ( - <> - - - Number(n).toFixed(0) + '%'} - reverseColors - /> - `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`} - /> - - )} + {cloneChildren(children, child => { + return { format: child.props.format || formatFunc }; + })}
); } diff --git a/components/metrics/MetricsBar.module.css b/components/metrics/MetricsBar.module.css index 0e305c70c7..eb33a3241d 100644 --- a/components/metrics/MetricsBar.module.css +++ b/components/metrics/MetricsBar.module.css @@ -1,7 +1,8 @@ .bar { display: flex; + flex-direction: row; cursor: pointer; - min-height: 80px; + min-height: 110px; gap: 20px; flex-wrap: wrap; } diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js index 97deb39d64..5026279844 100644 --- a/components/metrics/MetricsTable.js +++ b/components/metrics/MetricsTable.js @@ -30,7 +30,7 @@ export function MetricsTable({ const { resolveUrl, router, - query: { url, referrer, os, browser, device, country, region, city }, + query: { url, referrer, title, os, browser, device, country, region, city }, } = usePageQuery(); const { formatMessage, labels } = useMessages(); const { get, useQuery } = useApi(); @@ -38,7 +38,20 @@ export function MetricsTable({ const { data, isLoading, isFetched, error } = useQuery( [ 'websites:metrics', - { websiteId, type, modified, url, referrer, os, browser, device, country, region, city }, + { + websiteId, + type, + modified, + url, + referrer, + os, + title, + browser, + device, + country, + region, + city, + }, ], () => get(`/websites/${websiteId}/metrics`, { @@ -46,6 +59,7 @@ export function MetricsTable({ startAt: +startDate, endAt: +endDate, url, + title, referrer, os, browser, @@ -59,13 +73,27 @@ export function MetricsTable({ const filteredData = useMemo(() => { if (data) { - let items = percentFilter(dataFilter ? dataFilter(data, filterOptions) : data); + let items = data; + + if (dataFilter) { + if (Array.isArray(dataFilter)) { + items = dataFilter.reduce((arr, filter) => { + return filter(arr); + }, items); + } else { + items = dataFilter(data); + } + } + + items = percentFilter(items); + if (limit) { items = items.filter((e, i) => i < limit); } if (filterOptions?.sort === false) { return items; } + return items.sort(firstBy('y', -1).thenBy('x')); } return []; diff --git a/components/metrics/OSTable.js b/components/metrics/OSTable.js index a638038b38..e4bc323403 100644 --- a/components/metrics/OSTable.js +++ b/components/metrics/OSTable.js @@ -1,12 +1,25 @@ import MetricsTable from './MetricsTable'; import FilterLink from 'components/common/FilterLink'; import useMessages from 'hooks/useMessages'; +import { useRouter } from 'next/router'; export function OSTable({ websiteId, ...props }) { const { formatMessage, labels } = useMessages(); + const { basePath } = useRouter(); function renderLink({ x: os }) { - return ; + return ( + + {os} + + ); } return ( diff --git a/components/metrics/PageviewsChart.js b/components/metrics/PageviewsChart.js index 6ea162268d..362c616e2b 100644 --- a/components/metrics/PageviewsChart.js +++ b/components/metrics/PageviewsChart.js @@ -1,34 +1,13 @@ import { useMemo } from 'react'; -import { colord } from 'colord'; import BarChart from './BarChart'; -import { THEME_COLORS } from 'lib/constants'; -import useTheme from 'hooks/useTheme'; -import useMessages from 'hooks/useMessages'; -import useLocale from 'hooks/useLocale'; +import { useLocale, useTheme, useMessages } from 'hooks'; +import { renderDateLabels, renderStatusTooltipPopup } from 'lib/charts'; -export function PageviewsChart({ websiteId, data, unit, records, className, loading, ...props }) { +export function PageviewsChart({ websiteId, data, unit, className, loading, ...props }) { const { formatMessage, labels } = useMessages(); - const [theme] = useTheme(); + const { colors } = useTheme(); const { locale } = useLocale(); - const colors = useMemo(() => { - const primaryColor = colord(THEME_COLORS[theme].primary); - return { - views: { - hoverBackgroundColor: primaryColor.alpha(0.7).toRgbString(), - backgroundColor: primaryColor.alpha(0.4).toRgbString(), - borderColor: primaryColor.alpha(0.7).toRgbString(), - hoverBorderColor: primaryColor.toRgbString(), - }, - visitors: { - hoverBackgroundColor: primaryColor.alpha(0.9).toRgbString(), - backgroundColor: primaryColor.alpha(0.6).toRgbString(), - borderColor: primaryColor.alpha(0.9).toRgbString(), - hoverBorderColor: primaryColor.toRgbString(), - }, - }; - }, [theme]); - const datasets = useMemo(() => { if (!data) return []; @@ -37,13 +16,13 @@ export function PageviewsChart({ websiteId, data, unit, records, className, load label: formatMessage(labels.uniqueVisitors), data: data.sessions, borderWidth: 1, - ...colors.visitors, + ...colors.chart.visitors, }, { label: formatMessage(labels.pageViews), data: data.pageviews, borderWidth: 1, - ...colors.views, + ...colors.chart.views, }, ]; }, [data, locale, colors]); @@ -55,8 +34,9 @@ export function PageviewsChart({ websiteId, data, unit, records, className, load className={className} datasets={datasets} unit={unit} - records={records} loading={loading} + renderXLabel={renderDateLabels(unit, locale)} + renderTooltipPopup={renderStatusTooltipPopup(unit, locale)} /> ); } diff --git a/components/metrics/QueryParametersTable.js b/components/metrics/QueryParametersTable.js index c5f573e3c4..23193c2e1a 100644 --- a/components/metrics/QueryParametersTable.js +++ b/components/metrics/QueryParametersTable.js @@ -9,7 +9,7 @@ import styles from './QueryParametersTable.module.css'; const filters = { [FILTER_RAW]: emptyFilter, - [FILTER_COMBINED]: paramFilter, + [FILTER_COMBINED]: [emptyFilter, paramFilter], }; export function QueryParametersTable({ websiteId, showFilters, ...props }) { diff --git a/components/metrics/RegionsTable.js b/components/metrics/RegionsTable.js index c8e0b76861..d7b614f75f 100644 --- a/components/metrics/RegionsTable.js +++ b/components/metrics/RegionsTable.js @@ -1,15 +1,17 @@ -import MetricsTable from './MetricsTable'; -import { emptyFilter } from 'lib/filters'; +import { useRouter } from 'next/router'; import FilterLink from 'components/common/FilterLink'; +import { emptyFilter } from 'lib/filters'; import useLocale from 'hooks/useLocale'; import useMessages from 'hooks/useMessages'; import useCountryNames from 'hooks/useCountryNames'; +import MetricsTable from './MetricsTable'; import regions from 'public/iso-3166-2.json'; export function RegionsTable({ websiteId, ...props }) { const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); const countryNames = useCountryNames(locale); + const { basePath } = useRouter(); const renderLabel = x => { return regions[x] ? `${regions[x]}, ${countryNames[x.split('-')[0]]}` : x; @@ -18,7 +20,10 @@ export function RegionsTable({ websiteId, ...props }) { const renderLink = ({ x: code }) => { return ( - {code} + {code} ); }; diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js deleted file mode 100644 index 6614d40f01..0000000000 --- a/components/metrics/WebsiteChart.js +++ /dev/null @@ -1,132 +0,0 @@ -import { useMemo } from 'react'; -import { Button, Icon, Text, Row, Column } from 'react-basics'; -import Link from 'next/link'; -import classNames from 'classnames'; -import PageviewsChart from './PageviewsChart'; -import MetricsBar from './MetricsBar'; -import WebsiteHeader from './WebsiteHeader'; -import DateFilter from 'components/input/DateFilter'; -import ErrorMessage from 'components/common/ErrorMessage'; -import FilterTags from 'components/metrics/FilterTags'; -import RefreshButton from 'components/input/RefreshButton'; -import useApi from 'hooks/useApi'; -import useDateRange from 'hooks/useDateRange'; -import useTimezone from 'hooks/useTimezone'; -import usePageQuery from 'hooks/usePageQuery'; -import { getDateArray, getDateLength } from 'lib/date'; -import Icons from 'components/icons'; -import useSticky from 'hooks/useSticky'; -import useMessages from 'hooks/useMessages'; -import styles from './WebsiteChart.module.css'; -import useLocale from 'hooks/useLocale'; - -export function WebsiteChart({ - websiteId, - name, - domain, - stickyHeader = false, - showChart = true, - showDetailsButton = false, - onDataLoad = () => {}, -}) { - const { formatMessage, labels } = useMessages(); - const [dateRange] = useDateRange(websiteId); - const { startDate, endDate, unit, value, modified } = dateRange; - const [timezone] = useTimezone(); - const { - query: { url, referrer, os, browser, device, country, region, city, title }, - } = usePageQuery(); - const { get, useQuery } = useApi(); - const { ref, isSticky } = useSticky({ enabled: stickyHeader }); - - const { data, isLoading, error } = useQuery( - [ - 'websites:pageviews', - { websiteId, modified, url, referrer, os, browser, device, country, region, city, title }, - ], - () => - get(`/websites/${websiteId}/pageviews`, { - startAt: +startDate, - endAt: +endDate, - unit, - timezone, - url, - referrer, - os, - browser, - device, - country, - region, - city, - title, - }), - { onSuccess: onDataLoad }, - ); - - const chartData = useMemo(() => { - if (data) { - return { - pageviews: getDateArray(data.pageviews, startDate, endDate, unit), - sessions: getDateArray(data.sessions, startDate, endDate, unit), - }; - } - return { pageviews: [], sessions: [] }; - }, [data, modified]); - - const { dir } = useLocale(); - return ( - <> - - {showDetailsButton && ( - - - - )} - - - - - - - -
- - -
-
-
- - - {error && } - {showChart && ( - - )} - - - - ); -} - -export default WebsiteChart; diff --git a/components/metrics/WebsiteHeader.js b/components/metrics/WebsiteHeader.js deleted file mode 100644 index de21c0c785..0000000000 --- a/components/metrics/WebsiteHeader.js +++ /dev/null @@ -1,21 +0,0 @@ -import { Row, Column, Text } from 'react-basics'; -import Favicon from 'components/common/Favicon'; -import ActiveUsers from './ActiveUsers'; -import styles from './WebsiteHeader.module.css'; - -export function WebsiteHeader({ websiteId, name, domain, children }) { - return ( - - - - {name} - - - - {children} - - - ); -} - -export default WebsiteHeader; diff --git a/components/pages/console/TestConsole.js b/components/pages/console/TestConsole.js index 745bf94c57..4f167b9a72 100644 --- a/components/pages/console/TestConsole.js +++ b/components/pages/console/TestConsole.js @@ -2,7 +2,7 @@ import WebsiteSelect from 'components/input/WebsiteSelect'; import Page from 'components/layout/Page'; import PageHeader from 'components/layout/PageHeader'; import EventsChart from 'components/metrics/EventsChart'; -import WebsiteChart from 'components/metrics/WebsiteChart'; +import WebsiteChart from 'components/pages/websites/WebsiteChart'; import useApi from 'hooks/useApi'; import Head from 'next/head'; import Link from 'next/link'; @@ -28,20 +28,42 @@ export function TestConsole() { window.umami.track({ url: '/page-view', referrer: 'https://www.google.com' }); window.umami.track('track-event-no-data'); window.umami.track('track-event-with-data', { - data: { + test: 'test-data', + boolean: true, + booleanError: 'true', + time: new Date(), + number: 1, + number2: Math.random() * 100, + time2: new Date().toISOString(), + nested: { test: 'test-data', - time: new Date(), number: 1, - time2: new Date().toISOString(), - nested: { + object: { test: 'test-data', - number: 1, - object: { - test: 'test-data', - }, }, - array: [1, 2, 3], }, + array: [1, 2, 3], + }); + } + + function handleIdentifyClick() { + window.umami.identify({ + userId: 123, + name: 'brian', + number: Math.random() * 100, + test: 'test-data', + boolean: true, + booleanError: 'true', + time: new Date(), + time2: new Date().toISOString(), + nested: { + test: 'test-data', + number: 1, + object: { + test: 'test-data', + }, + }, + array: [1, 2, 3], }); } @@ -114,16 +136,15 @@ export function TestConsole() { +

+ - + diff --git a/components/pages/event-data/EventDataMetricsBar.js b/components/pages/event-data/EventDataMetricsBar.js new file mode 100644 index 0000000000..48843287c0 --- /dev/null +++ b/components/pages/event-data/EventDataMetricsBar.js @@ -0,0 +1,54 @@ +import { Column, Row } from 'react-basics'; +import { useApi, useDateRange } from 'hooks'; +import MetricCard from 'components/metrics/MetricCard'; +import useMessages from 'hooks/useMessages'; +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import MetricsBar from 'components/metrics/MetricsBar'; +import styles from './EventDataMetricsBar.module.css'; + +export function EventDataMetricsBar({ websiteId }) { + const { formatMessage, labels } = useMessages(); + const { get, useQuery } = useApi(); + const [dateRange] = useDateRange(websiteId); + const { startDate, endDate, modified } = dateRange; + + const { data, error, isLoading, isFetched } = useQuery( + ['event-data:stats', { websiteId, startDate, endDate, modified }], + () => + get(`/event-data/stats`, { + websiteId, + startAt: +startDate, + endAt: +endDate, + }), + ); + + return ( + + + + {!error && isFetched && ( + <> + + + + )} + + + +

+ +
+ + + ); +} + +export default EventDataMetricsBar; diff --git a/components/pages/event-data/EventDataMetricsBar.module.css b/components/pages/event-data/EventDataMetricsBar.module.css new file mode 100644 index 0000000000..43b1458022 --- /dev/null +++ b/components/pages/event-data/EventDataMetricsBar.module.css @@ -0,0 +1,46 @@ +.container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0; + min-height: 90px; + margin-bottom: 20px; + background: var(--base50); + z-index: var(--z-index-above); +} + +.metrics { + display: flex; + flex-direction: row; + align-items: center; +} + +.actions { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + flex: 1; +} + +.bar { + display: flex; + cursor: pointer; + min-height: 110px; + gap: 20px; + flex-wrap: wrap; +} + +.card { + justify-self: flex-start; +} + +@media only screen and (max-width: 992px) { + .card { + flex-basis: calc(50% - 20px); + } +} + +.row { + border-bottom: 1px solid var(--border-color); +} diff --git a/components/pages/event-data/EventDataTable.js b/components/pages/event-data/EventDataTable.js new file mode 100644 index 0000000000..2724962fe0 --- /dev/null +++ b/components/pages/event-data/EventDataTable.js @@ -0,0 +1,32 @@ +import Link from 'next/link'; +import { GridTable, GridColumn } from 'react-basics'; +import { useMessages, usePageQuery } from 'hooks'; +import Empty from 'components/common/Empty'; + +export function EventDataTable({ data = [] }) { + const { formatMessage, labels } = useMessages(); + const { resolveUrl } = usePageQuery(); + + if (data.length === 0) { + return ; + } + + return ( + + + {row => { + return ( + + {row.field} + + ); + }} + + + {({ total }) => total.toLocaleString()} + + + ); +} + +export default EventDataTable; diff --git a/components/pages/event-data/EventDataValueTable.js b/components/pages/event-data/EventDataValueTable.js new file mode 100644 index 0000000000..2a20c9b097 --- /dev/null +++ b/components/pages/event-data/EventDataValueTable.js @@ -0,0 +1,44 @@ +import { GridTable, GridColumn, Button, Icon, Text, Flexbox } from 'react-basics'; +import { useMessages, usePageQuery } from 'hooks'; +import Link from 'next/link'; +import Icons from 'components/icons'; +import PageHeader from 'components/layout/PageHeader'; +import Empty from 'components/common/Empty'; + +export function EventDataTable({ data = [], field }) { + const { formatMessage, labels } = useMessages(); + const { resolveUrl } = usePageQuery(); + + const Title = () => { + return ( + <> + + + + {field} + + ); + }; + + return ( + <> + } /> + {data.length <= 0 && } + {data.length > 0 && ( + + + + {({ total }) => total.toLocaleString()} + + + )} + + ); +} + +export default EventDataTable; diff --git a/components/pages/realtime/RealtimeLog.js b/components/pages/realtime/RealtimeLog.js index ddd357514e..fe12963c77 100644 --- a/components/pages/realtime/RealtimeLog.js +++ b/components/pages/realtime/RealtimeLog.js @@ -3,7 +3,7 @@ import { StatusLight, Icon, Text } from 'react-basics'; import { FixedSizeList } from 'react-window'; import firstBy from 'thenby'; import FilterButtons from 'components/common/FilterButtons'; -import NoData from 'components/common/NoData'; +import Empty from 'components/common/Empty'; import useLocale from 'hooks/useLocale'; import useCountryNames from 'hooks/useCountryNames'; import { BROWSERS } from 'lib/constants'; @@ -144,7 +144,7 @@ export function RealtimeLog({ data, websiteDomain }) {
{formatMessage(labels.activityLog)}
- {logs?.length === 0 && } + {logs?.length === 0 && } {logs?.length > 0 && ( {Row} diff --git a/components/pages/realtime/RealtimeDashboard.js b/components/pages/realtime/RealtimePage.js similarity index 83% rename from components/pages/realtime/RealtimeDashboard.js rename to components/pages/realtime/RealtimePage.js index a1b862cb24..2d2ecebae6 100644 --- a/components/pages/realtime/RealtimeDashboard.js +++ b/components/pages/realtime/RealtimePage.js @@ -1,22 +1,20 @@ import { useState, useEffect, useMemo } from 'react'; import { subMinutes, startOfMinute } from 'date-fns'; -import { useRouter } from 'next/router'; import firstBy from 'thenby'; import { GridRow, GridColumn } from 'components/layout/Grid'; import Page from 'components/layout/Page'; import RealtimeChart from 'components/metrics/RealtimeChart'; -import PageHeader from 'components/layout/PageHeader'; import WorldMap from 'components/common/WorldMap'; import RealtimeLog from 'components/pages/realtime/RealtimeLog'; import RealtimeHeader from 'components/pages/realtime/RealtimeHeader'; import RealtimeUrls from 'components/pages/realtime/RealtimeUrls'; import RealtimeCountries from 'components/pages/realtime/RealtimeCountries'; -import WebsiteSelect from 'components/input/WebsiteSelect'; +import WebsiteHeader from 'components/pages/websites/WebsiteHeader'; import useApi from 'hooks/useApi'; -import useMessages from 'hooks/useMessages'; import { percentFilter } from 'lib/filters'; import { REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants'; -import styles from './RealtimeDashboard.module.css'; +import styles from './RealtimePage.module.css'; +import { useWebsite } from 'hooks'; function mergeData(state = [], data = [], time) { const ids = state.map(({ __id }) => __id); @@ -25,12 +23,10 @@ function mergeData(state = [], data = [], time) { .filter(({ timestamp }) => timestamp >= time); } -export function RealtimeDashboard({ websiteId }) { - const { formatMessage, labels } = useMessages(); - const router = useRouter(); +export function RealtimePage({ websiteId }) { const [currentData, setCurrentData] = useState(); const { get, useQuery } = useApi(); - const { data: website } = useQuery(['websites', websiteId], () => get(`/websites/${websiteId}`)); + const { data: website } = useWebsite(websiteId); const { data, isLoading, error } = useQuery( ['realtime', websiteId], () => get(`/realtime/${websiteId}`, { startAt: currentData?.timestamp || 0 }), @@ -93,15 +89,9 @@ export function RealtimeDashboard({ websiteId }) { return currentData; }, [currentData]); - const handleSelect = id => { - router.push(`/realtime/${id}`); - }; - return ( - - - +
@@ -126,4 +116,4 @@ export function RealtimeDashboard({ websiteId }) { ); } -export default RealtimeDashboard; +export default RealtimePage; diff --git a/components/pages/realtime/RealtimeDashboard.module.css b/components/pages/realtime/RealtimePage.module.css similarity index 100% rename from components/pages/realtime/RealtimeDashboard.module.css rename to components/pages/realtime/RealtimePage.module.css diff --git a/components/pages/realtime/RealtimeUrls.js b/components/pages/realtime/RealtimeUrls.js index dfbf1fda6c..18d8f2f659 100644 --- a/components/pages/realtime/RealtimeUrls.js +++ b/components/pages/realtime/RealtimeUrls.js @@ -10,6 +10,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) { const { formatMessage, labels } = useMessages(); const { pageviews } = data; const [filter, setFilter] = useState(FILTER_REFERRERS); + const limit = 15; const buttons = [ { @@ -47,7 +48,8 @@ export function RealtimeUrls({ websiteDomain, data = {} }) { } return arr; }, []) - .sort(firstBy('y', -1)), + .sort(firstBy('y', -1)) + .slice(0, limit), ); const pages = percentFilter( @@ -62,7 +64,8 @@ export function RealtimeUrls({ websiteDomain, data = {} }) { } return arr; }, []) - .sort(firstBy('y', -1)), + .sort(firstBy('y', -1)) + .slice(0, limit), ); return [referrers, pages]; diff --git a/components/pages/reports/BaseParameters.js b/components/pages/reports/BaseParameters.js new file mode 100644 index 0000000000..394432cfa8 --- /dev/null +++ b/components/pages/reports/BaseParameters.js @@ -0,0 +1,42 @@ +import { FormRow } from 'react-basics'; +import DateFilter from 'components/input/DateFilter'; +import WebsiteSelect from 'components/input/WebsiteSelect'; +import { parseDateRange } from 'lib/date'; +import { useContext } from 'react'; +import { ReportContext } from './Report'; +import { useMessages } from 'hooks'; + +export function BaseParameters() { + const { report, updateReport } = useContext(ReportContext); + const { formatMessage, labels } = useMessages(); + + const { parameters } = report || {}; + const { websiteId, dateRange } = parameters || {}; + const { value, startDate, endDate } = dateRange || {}; + + const handleWebsiteSelect = websiteId => { + updateReport({ websiteId, parameters: { websiteId } }); + }; + + const handleDateChange = value => { + updateReport({ parameters: { dateRange: { ...parseDateRange(value) } } }); + }; + + return ( + <> + + + + + + + + ); +} + +export default BaseParameters; diff --git a/components/pages/reports/FieldAggregateForm.js b/components/pages/reports/FieldAggregateForm.js new file mode 100644 index 0000000000..cdcbdacc7d --- /dev/null +++ b/components/pages/reports/FieldAggregateForm.js @@ -0,0 +1,41 @@ +import { Form, FormRow, Menu, Item } from 'react-basics'; +import { useMessages } from 'hooks'; + +export default function FieldAggregateForm({ name, type, onSelect }) { + const { formatMessage, labels } = useMessages(); + + const options = { + number: [ + { label: formatMessage(labels.sum), value: 'sum' }, + { label: formatMessage(labels.average), value: 'average' }, + { label: formatMessage(labels.min), value: 'min' }, + { label: formatMessage(labels.max), value: 'max' }, + ], + date: [ + { label: formatMessage(labels.min), value: 'min' }, + { label: formatMessage(labels.max), value: 'max' }, + ], + string: [ + { label: formatMessage(labels.total), value: 'total' }, + { label: formatMessage(labels.unique), value: 'unique' }, + ], + }; + + const items = options[type]; + + const handleSelect = value => { + onSelect({ name, type, value }); + }; + + return ( +
+ + + {items.map(({ label, value }) => { + return {label}; + })} + + +
+ ); +} diff --git a/components/pages/reports/FieldFilterForm.js b/components/pages/reports/FieldFilterForm.js new file mode 100644 index 0000000000..021ea97ef8 --- /dev/null +++ b/components/pages/reports/FieldFilterForm.js @@ -0,0 +1,57 @@ +import { useState } from 'react'; +import { Form, FormRow, Menu, Item, Flexbox, Dropdown, TextField, Button } from 'react-basics'; +import { useFilters } from 'hooks'; +import styles from './FieldFilterForm.module.css'; + +export default function FieldFilterForm({ name, type, onSelect }) { + const [filter, setFilter] = useState(''); + const [value, setValue] = useState(''); + const { filters, types } = useFilters(); + const items = types[type]; + + const renderValue = value => { + return filters[value]; + }; + + if (type === 'boolean') { + return ( +
+ + onSelect({ name, type, value: ['eq', value] })}> + {items.map(value => { + return {filters[value]}; + })} + + +
+ ); + } + + return ( +
+ + + + {value => { + return {filters[value]}; + }} + + setValue(e.target.value)} autoFocus={true} /> + + + +
+ ); +} diff --git a/components/pages/reports/FieldFilterForm.module.css b/components/pages/reports/FieldFilterForm.module.css new file mode 100644 index 0000000000..15baa582c2 --- /dev/null +++ b/components/pages/reports/FieldFilterForm.module.css @@ -0,0 +1,17 @@ +.selected { + font-weight: bold; +} + +.popup { + display: flex; +} + +.filter { + display: flex; + flex-direction: column; + gap: 20px; +} + +.dropdown { + min-width: 180px; +} diff --git a/components/pages/reports/FieldSelectForm.js b/components/pages/reports/FieldSelectForm.js new file mode 100644 index 0000000000..1ff6412a9b --- /dev/null +++ b/components/pages/reports/FieldSelectForm.js @@ -0,0 +1,24 @@ +import { Menu, Item, Form, FormRow } from 'react-basics'; +import { useMessages } from 'hooks'; +import styles from './FieldSelectForm.module.css'; + +export default function FieldSelectForm({ fields, onSelect }) { + const { formatMessage, labels } = useMessages(); + + return ( +
+ + onSelect(fields[key])}> + {fields.map(({ name, type }, index) => { + return ( + +
{name}
+
{type}
+
+ ); + })} +
+
+
+ ); +} diff --git a/components/pages/reports/FieldSelectForm.module.css b/components/pages/reports/FieldSelectForm.module.css new file mode 100644 index 0000000000..3a5ed9b87a --- /dev/null +++ b/components/pages/reports/FieldSelectForm.module.css @@ -0,0 +1,20 @@ +.menu { + width: 360px; + max-height: 300px; + overflow: auto; +} + +.item { + display: flex; + flex-direction: row; + justify-content: space-between; + border-radius: var(--border-radius); +} + +.item:hover { + background: var(--base75); +} + +.type { + color: var(--font-color300); +} diff --git a/components/pages/reports/ParameterList.js b/components/pages/reports/ParameterList.js new file mode 100644 index 0000000000..a097cbdc3a --- /dev/null +++ b/components/pages/reports/ParameterList.js @@ -0,0 +1,33 @@ +import { Icon, TooltipPopup } from 'react-basics'; +import Icons from 'components/icons'; +import Empty from 'components/common/Empty'; +import { useMessages } from 'hooks'; +import styles from './ParameterList.module.css'; + +export function ParameterList({ items = [], children, onRemove }) { + const { formatMessage, labels } = useMessages(); + + return ( +
+ {!items.length && } + {items.map((item, index) => { + return ( +
+ {typeof children === 'function' ? children(item) : item} + + + + + +
+ ); + })} +
+ ); +} + +export default ParameterList; diff --git a/components/pages/reports/ParameterList.module.css b/components/pages/reports/ParameterList.module.css new file mode 100644 index 0000000000..0f85fa0fcd --- /dev/null +++ b/components/pages/reports/ParameterList.module.css @@ -0,0 +1,21 @@ +.list { + display: flex; + flex-direction: column; + gap: 16px; +} + +.item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 12px; + border: 1px solid var(--base400); + border-radius: var(--border-radius); + box-shadow: 1px 1px 1px var(--base400); + gap: 10px; +} + +.icon { + align-self: center; +} diff --git a/components/pages/reports/PopupForm.js b/components/pages/reports/PopupForm.js new file mode 100644 index 0000000000..0f0ead369f --- /dev/null +++ b/components/pages/reports/PopupForm.js @@ -0,0 +1,30 @@ +import { createPortal } from 'react-dom'; +import { useDocumentClick, useKeyDown } from 'react-basics'; +import classNames from 'classnames'; +import styles from './PopupForm.module.css'; + +export function PopupForm({ element, className, children, onClose }) { + const { right, top } = element.getBoundingClientRect(); + const style = { position: 'absolute', left: right, top }; + + useKeyDown('Escape', onClose); + + useDocumentClick(e => { + if (e.target !== element && !element?.parentElement?.contains(e.target)) { + onClose(); + } + }); + + const handleClick = e => { + e.stopPropagation(); + }; + + return createPortal( +
+ {children} +
, + document.body, + ); +} + +export default PopupForm; diff --git a/components/pages/reports/PopupForm.module.css b/components/pages/reports/PopupForm.module.css new file mode 100644 index 0000000000..4daf199ac1 --- /dev/null +++ b/components/pages/reports/PopupForm.module.css @@ -0,0 +1,10 @@ +.form { + position: absolute; + background: var(--base50); + min-width: 300px; + padding: 20px; + margin-left: 30px; + border: 1px solid var(--base400); + border-radius: var(--border-radius); + box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.1); +} diff --git a/components/pages/reports/Report.js b/components/pages/reports/Report.js new file mode 100644 index 0000000000..685ebb9fe1 --- /dev/null +++ b/components/pages/reports/Report.js @@ -0,0 +1,22 @@ +import { createContext } from 'react'; +import Page from 'components/layout/Page'; +import styles from './reports.module.css'; +import { useReport } from 'hooks'; + +export const ReportContext = createContext(null); + +export function Report({ reportId, defaultParameters, children, ...props }) { + const report = useReport(reportId, defaultParameters); + + //console.log({ report }); + + return ( + + + {children} + + + ); +} + +export default Report; diff --git a/components/pages/reports/ReportBody.js b/components/pages/reports/ReportBody.js new file mode 100644 index 0000000000..2310c8af82 --- /dev/null +++ b/components/pages/reports/ReportBody.js @@ -0,0 +1,7 @@ +import styles from './reports.module.css'; + +export function ReportBody({ children }) { + return
{children}
; +} + +export default ReportBody; diff --git a/components/pages/reports/ReportDetails.js b/components/pages/reports/ReportDetails.js new file mode 100644 index 0000000000..c41d12f6a5 --- /dev/null +++ b/components/pages/reports/ReportDetails.js @@ -0,0 +1,13 @@ +import FunnelReport from './funnel/FunnelReport'; +import EventDataReport from './event-data/EventDataReport'; + +const reports = { + funnel: FunnelReport, + 'event-data': EventDataReport, +}; + +export default function ReportDetails({ reportId, reportType }) { + const Report = reports[reportType]; + + return ; +} diff --git a/components/pages/reports/ReportHeader.js b/components/pages/reports/ReportHeader.js new file mode 100644 index 0000000000..394b1951f3 --- /dev/null +++ b/components/pages/reports/ReportHeader.js @@ -0,0 +1,89 @@ +import { useContext } from 'react'; +import { useRouter } from 'next/router'; +import { Icon, LoadingButton, InlineEditField, useToasts } from 'react-basics'; +import PageHeader from 'components/layout/PageHeader'; +import { useMessages, useApi } from 'hooks'; +import { ReportContext } from './Report'; +import styles from './ReportHeader.module.css'; +import reportStyles from './reports.module.css'; + +export function ReportHeader({ icon }) { + const { report, updateReport } = useContext(ReportContext); + const { formatMessage, labels, messages } = useMessages(); + const { showToast } = useToasts(); + const { post, useMutation } = useApi(); + const router = useRouter(); + const { mutate: create, isLoading: isCreating } = useMutation(data => post(`/reports`, data)); + const { mutate: update, isLoading: isUpdating } = useMutation(data => + post(`/reports/${data.id}`, data), + ); + + const { name, description, parameters } = report || {}; + const { websiteId, dateRange } = parameters || {}; + + const handleSave = async () => { + if (!report.id) { + create(report, { + onSuccess: async ({ id }) => { + showToast({ message: formatMessage(messages.saved), variant: 'success' }); + router.push(`/reports/${id}`, null, { shallow: true }); + }, + }); + } else { + update(report, { + onSuccess: async () => { + showToast({ message: formatMessage(messages.saved), variant: 'success' }); + }, + }); + } + }; + + const handleNameChange = name => { + updateReport({ name: name || 'Untitled' }); + }; + + const handleDescriptionChange = description => { + updateReport({ description }); + }; + + const Title = () => { + return ( + <> + {icon} + + + ); + }; + + return ( +
+ }> + + {formatMessage(labels.save)} + + +
+ +
+
+ ); +} + +export default ReportHeader; diff --git a/components/pages/reports/ReportHeader.module.css b/components/pages/reports/ReportHeader.module.css new file mode 100644 index 0000000000..01e483a0dd --- /dev/null +++ b/components/pages/reports/ReportHeader.module.css @@ -0,0 +1,3 @@ +.description { + color: var(--font-color300); +} diff --git a/components/pages/reports/ReportMenu.js b/components/pages/reports/ReportMenu.js new file mode 100644 index 0000000000..abfea6fe20 --- /dev/null +++ b/components/pages/reports/ReportMenu.js @@ -0,0 +1,7 @@ +import styles from './reports.module.css'; + +export function ReportMenu({ children }) { + return
{children}
; +} + +export default ReportMenu; diff --git a/components/pages/reports/ReportTemplates.js b/components/pages/reports/ReportTemplates.js new file mode 100644 index 0000000000..35ba6f0144 --- /dev/null +++ b/components/pages/reports/ReportTemplates.js @@ -0,0 +1,59 @@ +import Link from 'next/link'; +import { Button, Icons, Text, Icon } from 'react-basics'; +import Page from 'components/layout/Page'; +import PageHeader from 'components/layout/PageHeader'; +import Funnel from 'assets/funnel.svg'; +import Nodes from 'assets/nodes.svg'; +import Lightbulb from 'assets/lightbulb.svg'; +import styles from './ReportTemplates.module.css'; +import { useMessages } from 'hooks'; + +const reports = [ + { + title: 'Funnel', + description: 'Understand the conversion and drop-off rate of users.', + url: '/reports/funnel', + icon: , + }, +]; + +function ReportItem({ title, description, url, icon }) { + return ( +
+
+ {icon} + {title} +
+
{description}
+
+ + + +
+
+ ); +} + +export function ReportTemplates() { + const { formatMessage, labels } = useMessages(); + + return ( + + +
+ {reports.map(({ title, description, url, icon }) => { + return ( + + ); + })} +
+
+ ); +} + +export default ReportTemplates; diff --git a/components/pages/reports/ReportTemplates.module.css b/components/pages/reports/ReportTemplates.module.css new file mode 100644 index 0000000000..331835051b --- /dev/null +++ b/components/pages/reports/ReportTemplates.module.css @@ -0,0 +1,33 @@ +.reports { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); + gap: 20px; + width: 360px; +} + +.report { + display: flex; + flex-direction: column; + gap: 20px; + padding: 20px; + border: 1px solid var(--base500); + border-radius: var(--border-radius); +} + +.title { + display: flex; + gap: 10px; + align-items: center; + font-size: var(--font-size-lg); + font-weight: 700; +} + +.description { + flex: 1; +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/components/pages/reports/ReportsPage.js b/components/pages/reports/ReportsPage.js new file mode 100644 index 0000000000..470e1b08d1 --- /dev/null +++ b/components/pages/reports/ReportsPage.js @@ -0,0 +1,29 @@ +import Page from 'components/layout/Page'; +import PageHeader from 'components/layout/PageHeader'; +import Link from 'next/link'; +import { Button, Icon, Icons, Text } from 'react-basics'; +import { useMessages, useReports } from 'hooks'; +import ReportsTable from './ReportsTable'; + +export function ReportsPage() { + const { formatMessage, labels } = useMessages(); + const { reports, error, isLoading } = useReports(); + + return ( + + + + + + + + + ); +} + +export default ReportsPage; diff --git a/components/pages/reports/ReportsTable.js b/components/pages/reports/ReportsTable.js new file mode 100644 index 0000000000..bcc97204df --- /dev/null +++ b/components/pages/reports/ReportsTable.js @@ -0,0 +1,36 @@ +import Link from 'next/link'; +import { Button, Text, Icon, Icons } from 'react-basics'; +import SettingsTable from 'components/common/SettingsTable'; +import useMessages from 'hooks/useMessages'; + +export function ReportsTable({ data = [] }) { + const { formatMessage, labels } = useMessages(); + + const columns = [ + { name: 'name', label: formatMessage(labels.name) }, + { name: 'description', label: formatMessage(labels.description) }, + { name: 'type', label: formatMessage(labels.type) }, + { name: 'action', label: ' ' }, + ]; + + return ( + + {row => { + const { id } = row; + + return ( + + + + ); + }} + + ); +} + +export default ReportsTable; diff --git a/components/pages/reports/event-data/EventDataParameters.js b/components/pages/reports/event-data/EventDataParameters.js new file mode 100644 index 0000000000..09358be2ea --- /dev/null +++ b/components/pages/reports/event-data/EventDataParameters.js @@ -0,0 +1,143 @@ +import { useContext, useRef } from 'react'; +import { useApi, useMessages } from 'hooks'; +import { Form, FormRow, FormButtons, SubmitButton, PopupTrigger, Icon, Popup } from 'react-basics'; +import { ReportContext } from 'components/pages/reports/Report'; +import Empty from 'components/common/Empty'; +import { DATA_TYPES, REPORT_PARAMETERS } from 'lib/constants'; +import Icons from 'components/icons'; +import FieldAddForm from './FieldAddForm'; +import BaseParameters from '../BaseParameters'; +import ParameterList from '../ParameterList'; +import styles from './EventDataParameters.module.css'; + +function useFields(websiteId, startDate, endDate) { + const { get, useQuery } = useApi(); + const { data, error, isLoading } = useQuery( + ['fields', websiteId, startDate, endDate], + () => + get('/reports/event-data', { + websiteId, + startAt: +startDate, + endAt: +endDate, + }), + { enabled: !!(websiteId && startDate && endDate) }, + ); + + return { data, error, isLoading }; +} + +export function EventDataParameters() { + const { report, runReport, updateReport, isRunning } = useContext(ReportContext); + const { formatMessage, labels, messages } = useMessages(); + const ref = useRef(null); + const { parameters } = report || {}; + const { websiteId, dateRange, fields, filters, groups } = parameters || {}; + const { startDate, endDate } = dateRange || {}; + const queryEnabled = websiteId && dateRange && fields?.length; + const { data, error } = useFields(websiteId, startDate, endDate); + const parametersSelected = websiteId && startDate && endDate; + const hasData = data?.length !== 0; + + const parameterGroups = [ + { label: formatMessage(labels.fields), group: REPORT_PARAMETERS.fields }, + { label: formatMessage(labels.filters), group: REPORT_PARAMETERS.filters }, + ]; + + const parameterData = { + fields, + filters, + groups, + }; + + const handleSubmit = values => { + runReport(values); + }; + + const handleAdd = (group, value) => { + const data = parameterData[group].filter(({ name }) => name !== value.name); + + updateReport({ parameters: { [group]: data.concat(value) } }); + }; + + const handleRemove = (group, index) => { + const data = [...parameterData[group]]; + data.splice(index, 1); + updateReport({ parameters: { [group]: data } }); + }; + + const AddButton = ({ group }) => { + return ( + + + + + + {(close, element) => { + return ( + ({ + name: eventKey, + type: DATA_TYPES[eventDataType], + }))} + group={group} + element={element} + onAdd={handleAdd} + onClose={close} + /> + ); + }} + + + ); + }; + + return ( +
+ + {!hasData && } + {parametersSelected && + hasData && + parameterGroups.map(({ label, group }) => { + return ( + } + > + handleRemove(group, index)} + > + {({ name, value }) => { + return ( +
+ {group === REPORT_PARAMETERS.fields && ( + <> +
{name}
+
{value}
+ + )} + {group === REPORT_PARAMETERS.filters && ( + <> +
{name}
+
{value[0]}
+
{value[1]}
+ + )} +
+ ); + }} +
+
+ ); + })} + + + {formatMessage(labels.runQuery)} + + + + ); +} + +export default EventDataParameters; diff --git a/components/pages/reports/event-data/EventDataParameters.module.css b/components/pages/reports/event-data/EventDataParameters.module.css new file mode 100644 index 0000000000..06b6241497 --- /dev/null +++ b/components/pages/reports/event-data/EventDataParameters.module.css @@ -0,0 +1,12 @@ +.parameter { + display: flex; + gap: 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + min-width: 0; +} + +.op { + font-weight: bold; +} diff --git a/components/pages/reports/event-data/EventDataReport.js b/components/pages/reports/event-data/EventDataReport.js new file mode 100644 index 0000000000..eb49a29df8 --- /dev/null +++ b/components/pages/reports/event-data/EventDataReport.js @@ -0,0 +1,26 @@ +import Report from '../Report'; +import ReportHeader from '../ReportHeader'; +import ReportMenu from '../ReportMenu'; +import ReportBody from '../ReportBody'; +import EventDataParameters from './EventDataParameters'; +import EventDataTable from './EventDataTable'; +import Nodes from 'assets/nodes.svg'; + +const defaultParameters = { + type: 'event-data', + parameters: { fields: [], filters: [] }, +}; + +export default function EventDataReport({ reportId }) { + return ( + + } /> + + + + + + + + ); +} diff --git a/components/pages/reports/event-data/EventDataTable.js b/components/pages/reports/event-data/EventDataTable.js new file mode 100644 index 0000000000..81b9cad142 --- /dev/null +++ b/components/pages/reports/event-data/EventDataTable.js @@ -0,0 +1,19 @@ +import { useContext } from 'react'; +import { GridTable, GridColumn } from 'react-basics'; +import { useMessages } from 'hooks'; +import { ReportContext } from '../Report'; + +export function EventDataTable() { + const { report } = useContext(ReportContext); + const { formatMessage, labels } = useMessages(); + + return ( + + + + + + ); +} + +export default EventDataTable; diff --git a/components/pages/reports/event-data/FieldAddForm.js b/components/pages/reports/event-data/FieldAddForm.js new file mode 100644 index 0000000000..c95fcac34d --- /dev/null +++ b/components/pages/reports/event-data/FieldAddForm.js @@ -0,0 +1,44 @@ +import { useState } from 'react'; +import { createPortal } from 'react-dom'; +import { REPORT_PARAMETERS } from 'lib/constants'; +import PopupForm from '../PopupForm'; +import FieldSelectForm from '../FieldSelectForm'; +import FieldAggregateForm from '../FieldAggregateForm'; +import FieldFilterForm from '../FieldFilterForm'; +import styles from './FieldAddForm.module.css'; + +export function FieldAddForm({ fields = [], group, element, onAdd, onClose }) { + const [selected, setSelected] = useState(); + + const handleSelect = value => { + const { type } = value; + + if (group === REPORT_PARAMETERS.groups || type === 'array' || type === 'boolean') { + value.value = group === REPORT_PARAMETERS.groups ? '' : 'total'; + handleSave(value); + return; + } + + setSelected(value); + }; + + const handleSave = value => { + onAdd(group, value); + onClose(); + }; + + return createPortal( + + {!selected && } + {selected && group === REPORT_PARAMETERS.fields && ( + + )} + {selected && group === REPORT_PARAMETERS.filters && ( + + )} + , + document.body, + ); +} + +export default FieldAddForm; diff --git a/components/pages/reports/event-data/FieldAddForm.module.css b/components/pages/reports/event-data/FieldAddForm.module.css new file mode 100644 index 0000000000..5c5aaa4f01 --- /dev/null +++ b/components/pages/reports/event-data/FieldAddForm.module.css @@ -0,0 +1,38 @@ +.menu { + width: 360px; + max-height: 300px; + overflow: auto; +} + +.item { + display: flex; + flex-direction: row; + justify-content: space-between; + border-radius: var(--border-radius); +} + +.item:hover { + background: var(--base75); +} + +.type { + color: var(--font-color300); +} + +.selected { + font-weight: bold; +} + +.popup { + display: flex; +} + +.filter { + display: flex; + flex-direction: column; + gap: 20px; +} + +.dropdown { + min-width: 60px; +} diff --git a/components/pages/reports/funnel/FunnelChart.js b/components/pages/reports/funnel/FunnelChart.js new file mode 100644 index 0000000000..7253c3fa56 --- /dev/null +++ b/components/pages/reports/funnel/FunnelChart.js @@ -0,0 +1,63 @@ +import { useCallback, useContext, useMemo } from 'react'; +import { Loading } from 'react-basics'; +import useMessages from 'hooks/useMessages'; +import useTheme from 'hooks/useTheme'; +import BarChart from 'components/metrics/BarChart'; +import { formatLongNumber } from 'lib/format'; +import styles from './FunnelChart.module.css'; +import { ReportContext } from '../Report'; + +export function FunnelChart({ className, loading }) { + const { report } = useContext(ReportContext); + const { formatMessage, labels } = useMessages(); + const { colors } = useTheme(); + + const { parameters, data } = report || {}; + + const renderXLabel = useCallback( + (label, index) => { + return parameters.urls[index]; + }, + [parameters], + ); + + const renderTooltipPopup = useCallback((setTooltipPopup, model) => { + const { opacity, dataPoints } = model.tooltip; + + if (!dataPoints?.length || !opacity) { + setTooltipPopup(null); + return; + } + + setTooltipPopup(`${formatLongNumber(dataPoints[0].raw.y)} ${formatMessage(labels.visitors)}`); + }, []); + + const datasets = useMemo(() => { + return [ + { + label: formatMessage(labels.uniqueVisitors), + data: data, + borderWidth: 1, + ...colors.chart.visitors, + }, + ]; + }, [data]); + + if (loading) { + return ; + } + + return ( + + ); +} + +export default FunnelChart; diff --git a/components/pages/reports/funnel/FunnelChart.module.css b/components/pages/reports/funnel/FunnelChart.module.css new file mode 100644 index 0000000000..9e1690b319 --- /dev/null +++ b/components/pages/reports/funnel/FunnelChart.module.css @@ -0,0 +1,3 @@ +.loading { + height: 300px; +} diff --git a/components/pages/reports/funnel/FunnelParameters.js b/components/pages/reports/funnel/FunnelParameters.js new file mode 100644 index 0000000000..ae4981761f --- /dev/null +++ b/components/pages/reports/funnel/FunnelParameters.js @@ -0,0 +1,86 @@ +import { useContext, useRef } from 'react'; +import { useMessages } from 'hooks'; +import { + Icon, + Form, + FormButtons, + FormInput, + FormRow, + PopupTrigger, + Popup, + SubmitButton, + TextField, +} from 'react-basics'; +import Icons from 'components/icons'; +import UrlAddForm from './UrlAddForm'; +import { ReportContext } from 'components/pages/reports/Report'; +import BaseParameters from '../BaseParameters'; +import ParameterList from '../ParameterList'; + +export function FunnelParameters() { + const { report, runReport, updateReport, isRunning } = useContext(ReportContext); + const { formatMessage, labels } = useMessages(); + const ref = useRef(null); + + const { parameters } = report || {}; + const { websiteId, dateRange, urls } = parameters || {}; + const queryDisabled = !websiteId || !dateRange || urls?.length < 2; + + const handleSubmit = (data, e) => { + e.stopPropagation(); + e.preventDefault(); + if (!queryDisabled) { + runReport(data); + } + }; + + const handleAddUrl = url => { + updateReport({ parameters: { urls: parameters.urls.concat(url) } }); + }; + + const handleRemoveUrl = (index, e) => { + e.stopPropagation(); + const urls = [...parameters.urls]; + urls.splice(index, 1); + updateReport({ parameters: { urls } }); + }; + + const AddUrlButton = () => { + return ( + + + + + + {(close, element) => { + return ; + }} + + + ); + }; + + return ( +
+ + + + + + + }> + + + + + {formatMessage(labels.runQuery)} + + + + ); +} + +export default FunnelParameters; diff --git a/components/pages/reports/funnel/FunnelReport.js b/components/pages/reports/funnel/FunnelReport.js new file mode 100644 index 0000000000..7b4d8ecede --- /dev/null +++ b/components/pages/reports/funnel/FunnelReport.js @@ -0,0 +1,28 @@ +import FunnelChart from './FunnelChart'; +import FunnelTable from './FunnelTable'; +import FunnelParameters from './FunnelParameters'; +import Report from '../Report'; +import ReportHeader from '../ReportHeader'; +import ReportMenu from '../ReportMenu'; +import ReportBody from '../ReportBody'; +import Funnel from 'assets/funnel.svg'; + +const defaultParameters = { + type: 'funnel', + parameters: { window: 60, urls: [] }, +}; + +export default function FunnelReport({ reportId }) { + return ( + + } /> + + + + + + + + + ); +} diff --git a/components/pages/reports/funnel/FunnelReport.module.css b/components/pages/reports/funnel/FunnelReport.module.css new file mode 100644 index 0000000000..aed66b74ef --- /dev/null +++ b/components/pages/reports/funnel/FunnelReport.module.css @@ -0,0 +1,10 @@ +.filters { + display: flex; + flex-direction: column; + justify-content: space-between; + border: 1px solid var(--base400); + border-radius: var(--border-radius); + line-height: 32px; + padding: 10px; + overflow: hidden; +} diff --git a/components/pages/reports/funnel/FunnelTable.js b/components/pages/reports/funnel/FunnelTable.js new file mode 100644 index 0000000000..ff6bdfb523 --- /dev/null +++ b/components/pages/reports/funnel/FunnelTable.js @@ -0,0 +1,20 @@ +import { useContext } from 'react'; +import DataTable from 'components/metrics/DataTable'; +import { useMessages } from 'hooks'; +import { ReportContext } from '../Report'; + +export function FunnelTable() { + const { report } = useContext(ReportContext); + const { formatMessage, labels } = useMessages(); + + return ( + + ); +} + +export default FunnelTable; diff --git a/components/pages/reports/funnel/UrlAddForm.js b/components/pages/reports/funnel/UrlAddForm.js new file mode 100644 index 0000000000..0fb78b3d29 --- /dev/null +++ b/components/pages/reports/funnel/UrlAddForm.js @@ -0,0 +1,51 @@ +import { useState } from 'react'; +import { useMessages } from 'hooks'; +import { Button, Form, FormRow, TextField, Flexbox } from 'react-basics'; +import styles from './UrlAddForm.module.css'; +import PopupForm from '../PopupForm'; + +export function UrlAddForm({ defaultValue = '', element, onAdd, onClose }) { + const [url, setUrl] = useState(defaultValue); + const { formatMessage, labels } = useMessages(); + + const handleSave = () => { + onAdd(url); + setUrl(''); + onClose(); + }; + + const handleChange = e => { + setUrl(e.target.value); + }; + + const handleKeyDown = e => { + if (e.key === 'Enter') { + e.stopPropagation(); + handleSave(); + } + }; + + return ( + +
+ + + + + + +
+
+ ); +} + +export default UrlAddForm; diff --git a/components/pages/reports/funnel/UrlAddForm.module.css b/components/pages/reports/funnel/UrlAddForm.module.css new file mode 100644 index 0000000000..6a3e03b5e4 --- /dev/null +++ b/components/pages/reports/funnel/UrlAddForm.module.css @@ -0,0 +1,14 @@ +.form { + position: absolute; + background: var(--base50); + width: 300px; + padding: 30px; + margin-top: 10px; + border: 1px solid var(--base400); + border-radius: var(--border-radius); + box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.1); +} + +.input { + width: 100%; +} diff --git a/components/pages/reports/insights/FieldAddForm.js b/components/pages/reports/insights/FieldAddForm.js new file mode 100644 index 0000000000..c95fcac34d --- /dev/null +++ b/components/pages/reports/insights/FieldAddForm.js @@ -0,0 +1,44 @@ +import { useState } from 'react'; +import { createPortal } from 'react-dom'; +import { REPORT_PARAMETERS } from 'lib/constants'; +import PopupForm from '../PopupForm'; +import FieldSelectForm from '../FieldSelectForm'; +import FieldAggregateForm from '../FieldAggregateForm'; +import FieldFilterForm from '../FieldFilterForm'; +import styles from './FieldAddForm.module.css'; + +export function FieldAddForm({ fields = [], group, element, onAdd, onClose }) { + const [selected, setSelected] = useState(); + + const handleSelect = value => { + const { type } = value; + + if (group === REPORT_PARAMETERS.groups || type === 'array' || type === 'boolean') { + value.value = group === REPORT_PARAMETERS.groups ? '' : 'total'; + handleSave(value); + return; + } + + setSelected(value); + }; + + const handleSave = value => { + onAdd(group, value); + onClose(); + }; + + return createPortal( + + {!selected && } + {selected && group === REPORT_PARAMETERS.fields && ( + + )} + {selected && group === REPORT_PARAMETERS.filters && ( + + )} + , + document.body, + ); +} + +export default FieldAddForm; diff --git a/components/pages/reports/insights/FieldAddForm.module.css b/components/pages/reports/insights/FieldAddForm.module.css new file mode 100644 index 0000000000..5c5aaa4f01 --- /dev/null +++ b/components/pages/reports/insights/FieldAddForm.module.css @@ -0,0 +1,38 @@ +.menu { + width: 360px; + max-height: 300px; + overflow: auto; +} + +.item { + display: flex; + flex-direction: row; + justify-content: space-between; + border-radius: var(--border-radius); +} + +.item:hover { + background: var(--base75); +} + +.type { + color: var(--font-color300); +} + +.selected { + font-weight: bold; +} + +.popup { + display: flex; +} + +.filter { + display: flex; + flex-direction: column; + gap: 20px; +} + +.dropdown { + min-width: 60px; +} diff --git a/components/pages/reports/insights/InsightsParameters.js b/components/pages/reports/insights/InsightsParameters.js new file mode 100644 index 0000000000..39bfc2e8d4 --- /dev/null +++ b/components/pages/reports/insights/InsightsParameters.js @@ -0,0 +1,151 @@ +import { useContext, useRef } from 'react'; +import { useApi, useMessages } from 'hooks'; +import { Form, FormRow, FormButtons, SubmitButton, PopupTrigger, Icon, Popup } from 'react-basics'; +import { ReportContext } from 'components/pages/reports/Report'; +import Empty from 'components/common/Empty'; +import { DATA_TYPES, REPORT_PARAMETERS } from 'lib/constants'; +import Icons from 'components/icons'; +import FieldAddForm from './FieldAddForm'; +import BaseParameters from '../BaseParameters'; +import ParameterList from '../ParameterList'; +import styles from './InsightsParameters.module.css'; + +function useFields(websiteId, startDate, endDate) { + const { get, useQuery } = useApi(); + const { data, error, isLoading } = useQuery( + ['fields', websiteId, startDate, endDate], + () => + get('/reports/event-data', { + websiteId, + startAt: +startDate, + endAt: +endDate, + }), + { enabled: !!(websiteId && startDate && endDate) }, + ); + + return { data, error, isLoading }; +} + +export function InsightsParameters() { + const { report, runReport, updateReport, isRunning } = useContext(ReportContext); + const { formatMessage, labels, messages } = useMessages(); + const ref = useRef(null); + const { parameters } = report || {}; + const { websiteId, dateRange, fields, filters, groups } = parameters || {}; + const { startDate, endDate } = dateRange || {}; + const queryEnabled = websiteId && dateRange && fields?.length; + const { data, error } = useFields(websiteId, startDate, endDate); + const parametersSelected = websiteId && startDate && endDate; + const hasData = data?.length !== 0; + + const parameterGroups = [ + { label: formatMessage(labels.fields), group: REPORT_PARAMETERS.fields }, + { label: formatMessage(labels.filters), group: REPORT_PARAMETERS.filters }, + { label: formatMessage(labels.breakdown), group: REPORT_PARAMETERS.groups }, + ]; + + const parameterData = { + fields, + filters, + groups, + }; + + const handleSubmit = values => { + runReport(values); + }; + + const handleAdd = (group, value) => { + const data = parameterData[group]; + + if (!data.find(({ name }) => name === value.name)) { + updateReport({ parameters: { [group]: data.concat(value) } }); + } + }; + + const handleRemove = (group, index) => { + const data = [...parameterData[group]]; + data.splice(index, 1); + updateReport({ parameters: { [group]: data } }); + }; + + const AddButton = ({ group }) => { + return ( + + + + + + {(close, element) => { + return ( + ({ + name: eventKey, + type: DATA_TYPES[InsightsType], + }))} + group={group} + element={element} + onAdd={handleAdd} + onClose={close} + /> + ); + }} + + + ); + }; + + return ( +
+ + {!hasData && } + {parametersSelected && + hasData && + parameterGroups.map(({ label, group }) => { + return ( + } + > + handleRemove(group, index)} + > + {({ name, value }) => { + return ( +
+ {group === REPORT_PARAMETERS.fields && ( + <> +
{name}
+
{value}
+ + )} + {group === REPORT_PARAMETERS.filters && ( + <> +
{name}
+
{value[0]}
+
{value[1]}
+ + )} + {group === REPORT_PARAMETERS.groups && ( + <> +
{name}
+ + )} +
+ ); + }} +
+
+ ); + })} + + + {formatMessage(labels.runQuery)} + + + + ); +} + +export default InsightsParameters; diff --git a/components/pages/reports/insights/InsightsParameters.module.css b/components/pages/reports/insights/InsightsParameters.module.css new file mode 100644 index 0000000000..06b6241497 --- /dev/null +++ b/components/pages/reports/insights/InsightsParameters.module.css @@ -0,0 +1,12 @@ +.parameter { + display: flex; + gap: 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + min-width: 0; +} + +.op { + font-weight: bold; +} diff --git a/components/pages/reports/insights/InsightsReport.js b/components/pages/reports/insights/InsightsReport.js new file mode 100644 index 0000000000..88f12304c5 --- /dev/null +++ b/components/pages/reports/insights/InsightsReport.js @@ -0,0 +1,26 @@ +import Report from '../Report'; +import ReportHeader from '../ReportHeader'; +import ReportMenu from '../ReportMenu'; +import ReportBody from '../ReportBody'; +import InsightsParameters from './InsightsParameters'; +import InsightsTable from './InsightsTable'; +import Lightbulb from 'assets/lightbulb.svg'; + +const defaultParameters = { + type: 'insights', + parameters: { fields: [], filters: [], groups: [] }, +}; + +export default function InsightsReport({ reportId }) { + return ( + + } /> + + + + + + + + ); +} diff --git a/components/pages/reports/insights/InsightsTable.js b/components/pages/reports/insights/InsightsTable.js new file mode 100644 index 0000000000..a767468e47 --- /dev/null +++ b/components/pages/reports/insights/InsightsTable.js @@ -0,0 +1,19 @@ +import { useContext } from 'react'; +import { GridTable, GridColumn } from 'react-basics'; +import { useMessages } from 'hooks'; +import { ReportContext } from '../Report'; + +export function InsightsTable() { + const { report } = useContext(ReportContext); + const { formatMessage, labels } = useMessages(); + + return ( + + + + + + ); +} + +export default InsightsTable; diff --git a/components/pages/reports/reports.module.css b/components/pages/reports/reports.module.css new file mode 100644 index 0000000000..6fa542819b --- /dev/null +++ b/components/pages/reports/reports.module.css @@ -0,0 +1,25 @@ +.container { + display: grid; + grid-template-rows: max-content 1fr; + grid-template-columns: max-content 1fr; +} + +.header { + grid-row: 1 / 2; + grid-column: 1 / 3; + margin-bottom: 40px; +} + +.menu { + width: 300px; + padding-right: 20px; + border-right: 1px solid var(--base300); + grid-row: 2/3; + grid-column: 1 / 2; +} + +.body { + padding-left: 20px; + grid-row: 2/3; + grid-column: 2 / 3; +} diff --git a/components/pages/settings/profile/DateRangeSetting.js b/components/pages/settings/profile/DateRangeSetting.js index 152aba1d87..16db3c07a2 100644 --- a/components/pages/settings/profile/DateRangeSetting.js +++ b/components/pages/settings/profile/DateRangeSetting.js @@ -7,13 +7,19 @@ import useMessages from 'hooks/useMessages'; export function DateRangeSetting() { const { formatMessage, labels } = useMessages(); const [dateRange, setDateRange] = useDateRange(); - const { startDate, endDate, value } = dateRange; + const { value } = dateRange; + const handleChange = value => setDateRange(value); const handleReset = () => setDateRange(DEFAULT_DATE_RANGE); return ( - + ); diff --git a/components/pages/settings/profile/PasswordChangeButton.js b/components/pages/settings/profile/PasswordChangeButton.js index 9aa6fdcaed..03bf74bc6f 100644 --- a/components/pages/settings/profile/PasswordChangeButton.js +++ b/components/pages/settings/profile/PasswordChangeButton.js @@ -1,11 +1,11 @@ -import { Button, Icon, Text, useToast, ModalTrigger, Modal } from 'react-basics'; +import { Button, Icon, Text, useToasts, ModalTrigger, Modal } from 'react-basics'; import PasswordEditForm from 'components/pages/settings/profile/PasswordEditForm'; import Icons from 'components/icons'; import useMessages from 'hooks/useMessages'; export function PasswordChangeButton() { const { formatMessage, labels, messages } = useMessages(); - const { toast, showToast } = useToast(); + const { showToast } = useToasts(); const handleSave = () => { showToast({ message: formatMessage(messages.saved), variant: 'success' }); @@ -13,7 +13,6 @@ export function PasswordChangeButton() { return ( <> - {toast} - - {close => } - - + <> + {user.role !== ROLES.viewOnly && ( + + + + {close => } + + + )} + ); return ( - {toast} {hasData && ( diff --git a/components/pages/settings/users/UserAddForm.js b/components/pages/settings/users/UserAddForm.js index 63b247a131..bb40874955 100644 --- a/components/pages/settings/users/UserAddForm.js +++ b/components/pages/settings/users/UserAddForm.js @@ -35,6 +35,9 @@ export function UserAddForm({ onSave, onClose }) { if (value === ROLES.admin) { return formatMessage(labels.admin); } + if (value === ROLES.viewOnly) { + return formatMessage(labels.viewOnly); + } }; return ( @@ -52,6 +55,7 @@ export function UserAddForm({ onSave, onClose }) { + {formatMessage(labels.viewOnly)} {formatMessage(labels.user)} {formatMessage(labels.admin)} diff --git a/components/pages/settings/users/UserEditForm.js b/components/pages/settings/users/UserEditForm.js index d5e671e07e..887531e870 100644 --- a/components/pages/settings/users/UserEditForm.js +++ b/components/pages/settings/users/UserEditForm.js @@ -35,6 +35,9 @@ export function UserEditForm({ userId, data, onSave }) { if (value === ROLES.admin) { return formatMessage(labels.admin); } + if (value === ROLES.viewOnly) { + return formatMessage(labels.viewOnly); + } }; return ( @@ -57,6 +60,7 @@ export function UserEditForm({ userId, data, onSave }) { + {formatMessage(labels.viewOnly)} {formatMessage(labels.user)} {formatMessage(labels.admin)} diff --git a/components/pages/settings/users/UserSettings.js b/components/pages/settings/users/UserSettings.js index cd3bea42ae..d703e96467 100644 --- a/components/pages/settings/users/UserSettings.js +++ b/components/pages/settings/users/UserSettings.js @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Breadcrumbs, Item, Tabs, useToast } from 'react-basics'; +import { Breadcrumbs, Item, Tabs, useToasts } from 'react-basics'; import Link from 'next/link'; import UserEditForm from 'components/pages/settings/users//UserEditForm'; import Page from 'components/layout/Page'; @@ -14,7 +14,7 @@ export function UserSettings({ userId }) { const [values, setValues] = useState(null); const [tab, setTab] = useState('details'); const { get, useQuery } = useApi(); - const { toast, showToast } = useToast(); + const { showToast } = useToasts(); const { data, isLoading } = useQuery( ['user', userId], () => { @@ -44,7 +44,6 @@ export function UserSettings({ userId }) { return ( - {toast} diff --git a/components/pages/settings/users/UsersList.js b/components/pages/settings/users/UsersList.js index 1605cb5118..8886203b58 100644 --- a/components/pages/settings/users/UsersList.js +++ b/components/pages/settings/users/UsersList.js @@ -1,4 +1,4 @@ -import { useToast } from 'react-basics'; +import { useToasts } from 'react-basics'; import Page from 'components/layout/Page'; import PageHeader from 'components/layout/PageHeader'; import EmptyPlaceholder from 'components/common/EmptyPlaceholder'; @@ -15,7 +15,7 @@ export function UsersList() { const { data, isLoading, error, refetch } = useQuery(['user'], () => get(`/users`), { enabled: !!user, }); - const { toast, showToast } = useToast(); + const { showToast } = useToasts(); const hasData = data && data.length !== 0; const handleSave = () => { @@ -30,7 +30,6 @@ export function UsersList() { return ( - {toast} diff --git a/components/pages/settings/websites/TrackingCode.js b/components/pages/settings/websites/TrackingCode.js index 425dda06fe..c847ed0d2e 100644 --- a/components/pages/settings/websites/TrackingCode.js +++ b/components/pages/settings/websites/TrackingCode.js @@ -4,10 +4,12 @@ import useConfig from 'hooks/useConfig'; export function TrackingCode({ websiteId }) { const { formatMessage, messages } = useMessages(); - const { trackerScriptName } = useConfig(); + const { basePath, trackerScriptName } = useConfig(); const url = trackerScriptName?.startsWith('http') ? trackerScriptName - : `${location.origin}/${trackerScriptName?.split(',')?.map(n => n.trim())?.[0] || 'script.js'}`; + : `${location.origin}${basePath}/${ + trackerScriptName?.split(',')?.map(n => n.trim())?.[0] || 'script.js' + }`; const code = ``; diff --git a/components/pages/settings/websites/WebsiteResetForm.js b/components/pages/settings/websites/WebsiteResetForm.js index 4ac241690e..5fc0acf7ae 100644 --- a/components/pages/settings/websites/WebsiteResetForm.js +++ b/components/pages/settings/websites/WebsiteResetForm.js @@ -13,7 +13,7 @@ import useMessages from 'hooks/useMessages'; const CONFIRM_VALUE = 'RESET'; export function WebsiteResetForm({ websiteId, onSave, onClose }) { - const { formatMessage, labels, messages } = useMessages(); + const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { post, useMutation } = useApi(); const { mutate, error } = useMutation(data => post(`/websites/${websiteId}/reset`, data)); @@ -28,7 +28,12 @@ export function WebsiteResetForm({ websiteId, onSave, onClose }) { return ( -

{formatMessage(messages.resetWebsite, { confirmation: CONFIRM_VALUE })}

+

+ {CONFIRM_VALUE} }} + /> +

value === CONFIRM_VALUE }}> diff --git a/components/pages/settings/websites/WebsiteSettings.js b/components/pages/settings/websites/WebsiteSettings.js index 0a70a73117..5a96fa8c55 100644 --- a/components/pages/settings/websites/WebsiteSettings.js +++ b/components/pages/settings/websites/WebsiteSettings.js @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Breadcrumbs, Item, Tabs, useToast, Button, Text, Icon, Icons } from 'react-basics'; +import { Breadcrumbs, Item, Tabs, useToasts, Button, Text, Icon, Icons } from 'react-basics'; import { useRouter } from 'next/router'; import Link from 'next/link'; import Page from 'components/layout/Page'; @@ -17,7 +17,7 @@ export function WebsiteSettings({ websiteId }) { const { formatMessage, labels, messages } = useMessages(); const { openExternal } = useConfig(); const { get, useQuery } = useApi(); - const { toast, showToast } = useToast(); + const { showToast } = useToasts(); const { data, isLoading } = useQuery( ['website', websiteId], () => get(`/websites/${websiteId}`), @@ -51,7 +51,6 @@ export function WebsiteSettings({ websiteId }) { return ( - {toast} diff --git a/components/pages/settings/websites/WebsitesList.js b/components/pages/settings/websites/WebsitesList.js index 84f96c8380..de423d0b2b 100644 --- a/components/pages/settings/websites/WebsitesList.js +++ b/components/pages/settings/websites/WebsitesList.js @@ -1,4 +1,4 @@ -import { Button, Icon, Text, Modal, ModalTrigger, useToast, Icons } from 'react-basics'; +import { Button, Icon, Text, Modal, ModalTrigger, useToasts, Icons } from 'react-basics'; import Page from 'components/layout/Page'; import PageHeader from 'components/layout/PageHeader'; import EmptyPlaceholder from 'components/common/EmptyPlaceholder'; @@ -7,6 +7,7 @@ import WebsitesTable from 'components/pages/settings/websites/WebsitesTable'; import useApi from 'hooks/useApi'; import useUser from 'hooks/useUser'; import useMessages from 'hooks/useMessages'; +import { ROLES } from 'lib/constants'; export function WebsitesList() { const { formatMessage, labels, messages } = useMessages(); @@ -17,7 +18,7 @@ export function WebsitesList() { () => get(`/users/${user?.id}/websites`), { enabled: !!user }, ); - const { toast, showToast } = useToast(); + const { showToast } = useToasts(); const hasData = data && data.length !== 0; const handleSave = async () => { @@ -26,22 +27,25 @@ export function WebsitesList() { }; const addButton = ( - - - - {close => } - - + <> + {user.role !== ROLES.viewOnly && ( + + + + {close => } + + + )} + ); return ( - {toast} {addButton} {hasData && } {!hasData && ( diff --git a/components/pages/websites/WebsiteChart.js b/components/pages/websites/WebsiteChart.js new file mode 100644 index 0000000000..12b8ab4b31 --- /dev/null +++ b/components/pages/websites/WebsiteChart.js @@ -0,0 +1,59 @@ +import { useMemo } from 'react'; +import PageviewsChart from 'components/metrics/PageviewsChart'; +import { useApi, useDateRange, useTimezone, usePageQuery } from 'hooks'; +import { getDateArray, getDateLength } from 'lib/date'; + +export function WebsiteChart({ websiteId }) { + const [dateRange] = useDateRange(websiteId); + const { startDate, endDate, unit, modified } = dateRange; + const [timezone] = useTimezone(); + const { + query: { url, referrer, os, browser, device, country, region, city, title }, + } = usePageQuery(); + const { get, useQuery } = useApi(); + + const { data, isLoading } = useQuery( + [ + 'websites:pageviews', + { websiteId, modified, url, referrer, os, browser, device, country, region, city, title }, + ], + () => + get(`/websites/${websiteId}/pageviews`, { + startAt: +startDate, + endAt: +endDate, + unit, + timezone, + url, + referrer, + os, + browser, + device, + country, + region, + city, + title, + }), + ); + + const chartData = useMemo(() => { + if (data) { + return { + pageviews: getDateArray(data.pageviews, startDate, endDate, unit), + sessions: getDateArray(data.sessions, startDate, endDate, unit), + }; + } + return { pageviews: [], sessions: [] }; + }, [data, startDate, endDate, unit, modified]); + + return ( + + ); +} + +export default WebsiteChart; diff --git a/components/pages/websites/WebsiteChart.module.css b/components/pages/websites/WebsiteChart.module.css new file mode 100644 index 0000000000..b795047a9e --- /dev/null +++ b/components/pages/websites/WebsiteChart.module.css @@ -0,0 +1,17 @@ +.container { + position: relative; + display: flex; + flex-direction: column; + align-self: stretch; +} + +.chart { + position: relative; + overflow: hidden; +} + +.title { + font-size: var(--font-size-lg); + line-height: 60px; + font-weight: 600; +} diff --git a/components/pages/websites/WebsiteChartList.js b/components/pages/websites/WebsiteChartList.js index 0c55270419..42079a53ad 100644 --- a/components/pages/websites/WebsiteChartList.js +++ b/components/pages/websites/WebsiteChartList.js @@ -1,11 +1,19 @@ +import { Button, Text, Icon } from 'react-basics'; import { useMemo } from 'react'; import { firstBy } from 'thenby'; -import WebsiteChart from 'components/metrics/WebsiteChart'; +import Link from 'next/link'; +import WebsiteChart from 'components/pages/websites/WebsiteChart'; import useDashboard from 'store/dashboard'; import styles from './WebsiteList.module.css'; +import WebsiteHeader from './WebsiteHeader'; +import { WebsiteMetricsBar } from './WebsiteMetricsBar'; +import { useMessages, useLocale } from 'hooks'; +import Icons from 'components/icons'; export default function WebsiteChartList({ websites, showCharts, limit }) { + const { formatMessage, labels } = useMessages(); const { websiteOrder } = useDashboard(); + const { dir } = useLocale(); const ordered = useMemo( () => @@ -17,16 +25,23 @@ export default function WebsiteChartList({ websites, showCharts, limit }) { return (
- {ordered.map(({ id, name, domain }, index) => { + {ordered.map(({ id }, index) => { return index < limit ? (
- + + + + + + +
) : null; })} diff --git a/components/pages/websites/WebsiteDetails.js b/components/pages/websites/WebsiteDetails.js deleted file mode 100644 index ba80bcf802..0000000000 --- a/components/pages/websites/WebsiteDetails.js +++ /dev/null @@ -1,47 +0,0 @@ -import { useState } from 'react'; -import { Loading } from 'react-basics'; -import Page from 'components/layout/Page'; -import WebsiteChart from 'components/metrics/WebsiteChart'; -import useApi from 'hooks/useApi'; -import usePageQuery from 'hooks/usePageQuery'; -import { DEFAULT_ANIMATION_DURATION } from 'lib/constants'; -import WebsiteTableView from './WebsiteTableView'; -import WebsiteMenuView from './WebsiteMenuView'; - -export default function WebsiteDetails({ websiteId }) { - const { get, useQuery } = useApi(); - const { data, isLoading, error } = useQuery(['websites', websiteId], () => - get(`/websites/${websiteId}`), - ); - const [chartLoaded, setChartLoaded] = useState(false); - - const { - query: { view }, - } = usePageQuery(); - - function handleDataLoad() { - if (!chartLoaded) { - setTimeout(() => setChartLoaded(true), DEFAULT_ANIMATION_DURATION); - } - } - - return ( - - - {!chartLoaded && } - {chartLoaded && ( - <> - {!view && } - {view && } - - )} - - ); -} diff --git a/components/pages/websites/WebsiteDetails.module.css b/components/pages/websites/WebsiteDetails.module.css deleted file mode 100644 index b0632be6fe..0000000000 --- a/components/pages/websites/WebsiteDetails.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.chart { - margin-bottom: 30px; -} - -.view { - border-top: 1px solid var(--base300); -} - -.menu { - font-size: var(--font-size-sm); -} - -.content { - min-height: 600px; - padding: 20px 0; -} - -.backButton { - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 16px; -} - -.backButton svg { - transform: rotate(180deg); -} - -.hidden { - display: none; -} diff --git a/components/pages/websites/WebsiteDetailsPage.js b/components/pages/websites/WebsiteDetailsPage.js new file mode 100644 index 0000000000..9e0519b251 --- /dev/null +++ b/components/pages/websites/WebsiteDetailsPage.js @@ -0,0 +1,40 @@ +import { Loading } from 'react-basics'; +import { useRouter } from 'next/router'; +import Page from 'components/layout/Page'; +import WebsiteChart from 'components/pages/websites/WebsiteChart'; +import FilterTags from 'components/metrics/FilterTags'; +import usePageQuery from 'hooks/usePageQuery'; +import WebsiteTableView from './WebsiteTableView'; +import WebsiteMenuView from './WebsiteMenuView'; +import { useWebsite } from 'hooks'; +import WebsiteHeader from './WebsiteHeader'; +import { WebsiteMetricsBar } from './WebsiteMetricsBar'; + +export default function WebsiteDetailsPage({ websiteId }) { + const { data: website, isLoading, error } = useWebsite(websiteId); + const { pathname } = useRouter(); + const showLinks = !pathname.includes('/share/'); + + const { + query: { view, url, referrer, os, browser, device, country, region, city, title }, + } = usePageQuery(); + + return ( + + + + + + {!website && } + {website && ( + <> + {!view && } + {view && } + + )} + + ); +} diff --git a/components/pages/websites/WebsiteEventData.js b/components/pages/websites/WebsiteEventData.js new file mode 100644 index 0000000000..7dc68d41fe --- /dev/null +++ b/components/pages/websites/WebsiteEventData.js @@ -0,0 +1,40 @@ +import { Flexbox } from 'react-basics'; +import EventDataTable from 'components/pages/event-data/EventDataTable'; +import EventDataValueTable from 'components/pages/event-data/EventDataValueTable'; +import { EventDataMetricsBar } from 'components/pages/event-data/EventDataMetricsBar'; +import { useDateRange, useApi, usePageQuery } from 'hooks'; +import styles from './WebsiteEventData.module.css'; + +function useFields(websiteId, field) { + const [dateRange] = useDateRange(websiteId); + const { startDate, endDate } = dateRange; + const { get, useQuery } = useApi(); + const { data, error, isLoading } = useQuery( + ['event-data:fields', { websiteId, startDate, endDate, field }], + () => + get('/event-data/fields', { + websiteId, + startAt: +startDate, + endAt: +endDate, + field, + }), + { enabled: !!(websiteId && startDate && endDate) }, + ); + + return { data, error, isLoading }; +} + +export default function WebsiteEventData({ websiteId }) { + const { + query: { view }, + } = usePageQuery(); + const { data } = useFields(websiteId, view); + + return ( + + + {!view && } + {view && } + + ); +} diff --git a/components/pages/websites/WebsiteEventData.module.css b/components/pages/websites/WebsiteEventData.module.css new file mode 100644 index 0000000000..1d1782312d --- /dev/null +++ b/components/pages/websites/WebsiteEventData.module.css @@ -0,0 +1,7 @@ +.container a { + color: var(--font-color100); +} + +.container a:hover { + color: var(--primary400); +} diff --git a/components/pages/websites/WebsiteEventDataPage.js b/components/pages/websites/WebsiteEventDataPage.js new file mode 100644 index 0000000000..08acafb587 --- /dev/null +++ b/components/pages/websites/WebsiteEventDataPage.js @@ -0,0 +1,12 @@ +import Page from 'components/layout/Page'; +import WebsiteHeader from './WebsiteHeader'; +import WebsiteEventData from './WebsiteEventData'; + +export default function WebsiteEventDataPage({ websiteId }) { + return ( + + + + + ); +} diff --git a/components/pages/websites/WebsiteHeader.js b/components/pages/websites/WebsiteHeader.js new file mode 100644 index 0000000000..76f4e4f74d --- /dev/null +++ b/components/pages/websites/WebsiteHeader.js @@ -0,0 +1,75 @@ +import classNames from 'classnames'; +import { Flexbox, Row, Column, Text, Button, Icon } from 'react-basics'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import Favicon from 'components/common/Favicon'; +import ActiveUsers from 'components/metrics/ActiveUsers'; +import styles from './WebsiteHeader.module.css'; +import Icons from 'components/icons'; +import { useMessages, useWebsite } from 'hooks'; + +export function WebsiteHeader({ websiteId, showLinks = true, children }) { + const { formatMessage, labels } = useMessages(); + const { pathname } = useRouter(); + const { data: website } = useWebsite(websiteId); + const { name, domain } = website || {}; + + const links = [ + { + label: formatMessage(labels.overview), + icon: , + path: '', + }, + { + label: formatMessage(labels.realtime), + icon: , + path: '/realtime', + }, + { + label: formatMessage(labels.reports), + icon: , + path: '/reports', + }, + { + label: formatMessage(labels.eventData), + icon: , + path: '/event-data', + }, + ]; + + return ( + + + + {name} + + + + {showLinks && ( + + {links.map(({ label, icon, path }) => { + const selected = path ? pathname.endsWith(path) : pathname === '/websites/[id]'; + + return ( + + + + ); + })} + + )} + {children} + + + ); +} + +export default WebsiteHeader; diff --git a/components/metrics/WebsiteHeader.module.css b/components/pages/websites/WebsiteHeader.module.css similarity index 70% rename from components/metrics/WebsiteHeader.module.css rename to components/pages/websites/WebsiteHeader.module.css index e5ebcca714..89f78e5249 100644 --- a/components/metrics/WebsiteHeader.module.css +++ b/components/pages/websites/WebsiteHeader.module.css @@ -1,3 +1,9 @@ +.header { + display: flex; + flex-direction: row; + align-items: center; +} + .title { display: flex; flex-direction: row; @@ -9,7 +15,7 @@ height: 100px; } -.info { +.actions { display: flex; flex-direction: row; align-items: center; @@ -17,3 +23,7 @@ gap: 30px; min-height: 0; } + +.selected { + font-weight: bold; +} diff --git a/components/pages/websites/WebsiteMenuView.js b/components/pages/websites/WebsiteMenuView.js index 4f70512412..39adb188fd 100644 --- a/components/pages/websites/WebsiteMenuView.js +++ b/components/pages/websites/WebsiteMenuView.js @@ -102,7 +102,7 @@ export default function WebsiteMenuView({ websiteId, websiteDomain }) { }, { key: 'query', - label: formatMessage(labels.query), + label: formatMessage(labels.queryParameters), url: resolveUrl({ view: 'query' }), }, ]; diff --git a/components/pages/websites/WebsiteMetricsBar.js b/components/pages/websites/WebsiteMetricsBar.js new file mode 100644 index 0000000000..3683310c1a --- /dev/null +++ b/components/pages/websites/WebsiteMetricsBar.js @@ -0,0 +1,121 @@ +import classNames from 'classnames'; +import { Row, Column } from 'react-basics'; +import { formatShortTime } from 'lib/format'; +import MetricCard from 'components/metrics/MetricCard'; +import RefreshButton from 'components/input/RefreshButton'; +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import MetricsBar from 'components/metrics/MetricsBar'; +import { useApi, useDateRange, usePageQuery, useMessages, useSticky } from 'hooks'; +import styles from './WebsiteMetricsBar.module.css'; + +export function WebsiteMetricsBar({ websiteId, sticky }) { + const { formatMessage, labels } = useMessages(); + const { get, useQuery } = useApi(); + const [dateRange] = useDateRange(websiteId); + const { startDate, endDate, modified } = dateRange; + const { ref, isSticky } = useSticky({ enabled: sticky }); + const { + query: { url, referrer, title, os, browser, device, country, region, city }, + } = usePageQuery(); + + const { data, error, isLoading, isFetched } = useQuery( + [ + 'websites:stats', + { websiteId, modified, url, referrer, title, os, browser, device, country, region, city }, + ], + () => + get(`/websites/${websiteId}/stats`, { + startAt: +startDate, + endAt: +endDate, + url, + referrer, + title, + os, + browser, + device, + country, + region, + city, + }), + ); + + const { pageviews, uniques, bounces, totaltime } = data || {}; + const num = Math.min(data && uniques.value, data && bounces.value); + const diffs = data && { + pageviews: pageviews.value - pageviews.change, + uniques: uniques.value - uniques.change, + bounces: bounces.value - bounces.change, + totaltime: totaltime.value - totaltime.change, + }; + + return ( + + + + {!error && isFetched && ( + <> + + + Number(n).toFixed(0) + '%'} + reverseColors + /> + + `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}` + } + /> + + )} + + + +
+ + +
+
+
+ ); +} + +export default WebsiteMetricsBar; diff --git a/components/metrics/WebsiteChart.module.css b/components/pages/websites/WebsiteMetricsBar.module.css similarity index 67% rename from components/metrics/WebsiteChart.module.css rename to components/pages/websites/WebsiteMetricsBar.module.css index c9334a272a..52decfc65a 100644 --- a/components/metrics/WebsiteChart.module.css +++ b/components/pages/websites/WebsiteMetricsBar.module.css @@ -1,22 +1,4 @@ .container { - position: relative; - display: flex; - flex-direction: column; - align-self: stretch; -} - -.chart { - position: relative; - overflow: hidden; -} - -.title { - font-size: var(--font-size-lg); - line-height: 60px; - font-weight: 600; -} - -.header { display: flex; justify-content: space-between; align-items: center; @@ -35,8 +17,10 @@ gap: 10px; } -.dropdown { - min-width: 200px; +@media only screen and (max-width: 1200px) { + .actions { + margin-top: 40px; + } } @media only screen and (min-width: 992px) { @@ -49,9 +33,3 @@ border-bottom: 1px solid var(--base300); } } - -@media only screen and (max-width: 1200px) { - .actions { - margin-top: 40px; - } -} diff --git a/components/pages/websites/WebsiteReportsPage.js b/components/pages/websites/WebsiteReportsPage.js new file mode 100644 index 0000000000..b6f41bac78 --- /dev/null +++ b/components/pages/websites/WebsiteReportsPage.js @@ -0,0 +1,30 @@ +import Page from 'components/layout/Page'; +import Link from 'next/link'; +import { Button, Icon, Icons, Text, Flexbox } from 'react-basics'; +import { useMessages, useReports } from 'hooks'; +import ReportsTable from 'components/pages/reports/ReportsTable'; +import WebsiteHeader from './WebsiteHeader'; + +export function WebsiteReportsPage({ websiteId }) { + const { formatMessage, labels } = useMessages(); + const { reports, error, isLoading } = useReports(websiteId); + + return ( + + + + + + + + + + ); +} + +export default WebsiteReportsPage; diff --git a/db/clickhouse/migrations/01_edit_keys.sql b/db/clickhouse/migrations/01_edit_keys.sql new file mode 100644 index 0000000000..3fc7dd79ce --- /dev/null +++ b/db/clickhouse/migrations/01_edit_keys.sql @@ -0,0 +1,18 @@ +-- edit event_data values +ALTER TABLE "event_data" RENAME COLUMN "event_date_value" TO "date_value"; +ALTER TABLE "event_data" RENAME COLUMN "event_numeric_value" TO "number_value"; +ALTER TABLE "event_data" RENAME COLUMN "event_string_value" TO "string_value"; +ALTER TABLE "event_data" RENAME COLUMN "event_data_type" TO "data_type"; + +-- add job_id +ALTER TABLE "website_event" ADD COLUMN "job_id" UUID AFTER "created_at"; +ALTER TABLE "event_data" ADD COLUMN "job_id" UUID AFTER "created_at"; + +-- update event_data string +alter table umami.event_data +update string_value = number_value +where data_type = 2 + +alter table umami.event_data +update string_value = replaceOne(concat(CAST(toDateTime(date_value, 'UTC'), 'String'),'Z'), ' ', 'T') +where data_type = 4 \ No newline at end of file diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 77176413e3..a33db3c43e 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -27,7 +27,8 @@ CREATE TABLE umami.website_event --event event_type UInt32, event_name String, - created_at DateTime('UTC') + created_at DateTime('UTC'), + job_id UUID ) engine = MergeTree ORDER BY (website_id, session_id, created_at) @@ -58,7 +59,10 @@ CREATE TABLE umami.website_event_queue ( --event event_type UInt32, event_name String, - created_at DateTime('UTC') + created_at DateTime('UTC'), + --virtual columns + _error String, + _raw_message String ) ENGINE = Kafka SETTINGS kafka_broker_list = 'domain:9092,domain:9093,domain:9094', -- input broker list @@ -66,7 +70,7 @@ SETTINGS kafka_broker_list = 'domain:9092,domain:9093,domain:9094', -- input bro kafka_group_name = 'event_consumer_group', kafka_format = 'JSONEachRow', kafka_max_block_size = 1048576, - kafka_skip_broken_messages = 100; + kafka_handle_error_mode = 'stream' CREATE MATERIALIZED VIEW umami.website_event_queue_mv TO umami.website_event AS SELECT website_id, @@ -93,6 +97,19 @@ SELECT website_id, created_at FROM umami.website_event_queue; +CREATE MATERIALIZED VIEW umami.website_event_errors_mv +( + error String, + raw String +) +ENGINE = MergeTree +ORDER BY (error, raw) +SETTINGS index_granularity = 8192 AS +SELECT _error AS error, + _raw_message AS raw +FROM umami.website_event_queue +WHERE length(_error) > 0 + CREATE TABLE umami.event_data ( website_id UUID, @@ -101,11 +118,12 @@ CREATE TABLE umami.event_data url_path String, event_name String, event_key String, - event_string_value Nullable(String), - event_numeric_value Nullable(Decimal64(4)), --922337203685477.5625 - event_date_value Nullable(DateTime('UTC')), - event_data_type UInt32, - created_at DateTime('UTC') + string_value Nullable(String), + number_value Nullable(Decimal64(4)), --922337203685477.5625 + date_value Nullable(DateTime('UTC')), + data_type UInt32, + created_at DateTime('UTC'), + job_id UUID ) engine = MergeTree ORDER BY (website_id, event_id, event_key, created_at) @@ -118,11 +136,14 @@ CREATE TABLE umami.event_data_queue ( url_path String, event_name String, event_key String, - event_string_value Nullable(String), - event_numeric_value Nullable(Decimal64(4)), --922337203685477.5625 - event_date_value Nullable(DateTime('UTC')), - event_data_type UInt32, - created_at DateTime('UTC') + string_value Nullable(String), + number_value Nullable(Decimal64(4)), --922337203685477.5625 + date_value Nullable(DateTime('UTC')), + data_type UInt32, + created_at DateTime('UTC'), + --virtual columns + _error String, + _raw_message String ) ENGINE = Kafka SETTINGS kafka_broker_list = 'domain:9092,domain:9093,domain:9094', -- input broker list @@ -130,7 +151,7 @@ SETTINGS kafka_broker_list = 'domain:9092,domain:9093,domain:9094', -- input bro kafka_group_name = 'event_data_consumer_group', kafka_format = 'JSONEachRow', kafka_max_block_size = 1048576, - kafka_skip_broken_messages = 100; + kafka_handle_error_mode = 'stream' CREATE MATERIALIZED VIEW umami.event_data_queue_mv TO umami.event_data AS SELECT website_id, @@ -139,9 +160,22 @@ SELECT website_id, url_path, event_name, event_key, - event_string_value, - event_numeric_value, - event_date_value, - event_data_type, + string_value, + number_value, + date_value, + data_type, created_at -FROM umami.event_data_queue; \ No newline at end of file +FROM umami.event_data_queue; + +CREATE MATERIALIZED VIEW umami.event_data_errors_mv +( + error String, + raw String +) +ENGINE = MergeTree +ORDER BY (error, raw) +SETTINGS index_granularity = 8192 AS +SELECT _error AS error, + _raw_message AS raw +FROM umami.event_data_queue +WHERE length(_error) > 0 \ No newline at end of file diff --git a/db/mysql/migrations/02_report_schema_session_data/migration.sql b/db/mysql/migrations/02_report_schema_session_data/migration.sql new file mode 100644 index 0000000000..4970889935 --- /dev/null +++ b/db/mysql/migrations/02_report_schema_session_data/migration.sql @@ -0,0 +1,53 @@ +-- AlterTable +ALTER TABLE `event_data` RENAME COLUMN `event_data_type` TO `data_type`; +ALTER TABLE `event_data` RENAME COLUMN `event_date_value` TO `date_value`; +ALTER TABLE `event_data` RENAME COLUMN `event_id` TO `event_data_id`; +ALTER TABLE `event_data` RENAME COLUMN `event_numeric_value` TO `number_value`; +ALTER TABLE `event_data` RENAME COLUMN `event_string_value` TO `string_value`; + +-- CreateTable +CREATE TABLE `session_data` ( + `session_data_id` VARCHAR(36) NOT NULL, + `website_id` VARCHAR(36) NOT NULL, + `session_id` VARCHAR(36) NOT NULL, + `event_key` VARCHAR(500) NOT NULL, + `string_value` VARCHAR(500) NULL, + `number_value` DECIMAL(19, 4) NULL, + `date_value` TIMESTAMP(0) NULL, + `data_type` INTEGER UNSIGNED NOT NULL, + `created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0), + + INDEX `session_data_created_at_idx`(`created_at`), + INDEX `session_data_website_id_idx`(`website_id`), + INDEX `session_data_session_id_idx`(`session_id`), + PRIMARY KEY (`session_data_id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `report` ( + `report_id` VARCHAR(36) NOT NULL, + `user_id` VARCHAR(36) NOT NULL, + `website_id` VARCHAR(36) NOT NULL, + `type` VARCHAR(200) NOT NULL, + `name` VARCHAR(200) NOT NULL, + `description` VARCHAR(500) NOT NULL, + `parameters` VARCHAR(6000) NOT NULL, + `created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0), + `updated_at` TIMESTAMP(0) NULL, + + UNIQUE INDEX `report_report_id_key`(`report_id`), + INDEX `report_user_id_idx`(`user_id`), + INDEX `report_website_id_idx`(`website_id`), + INDEX `report_type_idx`(`type`), + INDEX `report_name_idx`(`name`), + PRIMARY KEY (`report_id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- EventData migration +UPDATE event_data +SET string_value = number_value +WHERE data_type = 2; + +UPDATE event_data +SET string_value = CONCAT(REPLACE(DATE_FORMAT(date_value, '%Y-%m-%d %T'), ' ', 'T'), 'Z') +WHERE data_type = 4; \ No newline at end of file diff --git a/db/mysql/schema.prisma b/db/mysql/schema.prisma index 6455c8c099..98ee8ae009 100644 --- a/db/mysql/schema.prisma +++ b/db/mysql/schema.prisma @@ -14,11 +14,12 @@ model User { password String @db.VarChar(60) role String @map("role") @db.VarChar(50) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamp(0) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0) deletedAt DateTime? @map("deleted_at") @db.Timestamp(0) website Website[] teamUser TeamUser[] + report Report[] @@map("user") } @@ -39,6 +40,7 @@ model Session { createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) websiteEvent WebsiteEvent[] + sessionData SessionData[] @@index([createdAt]) @@index([websiteId]) @@ -53,12 +55,14 @@ model Website { resetAt DateTime? @map("reset_at") @db.Timestamp(0) userId String? @map("user_id") @db.VarChar(36) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamp(0) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0) deletedAt DateTime? @map("deleted_at") @db.Timestamp(0) user User? @relation(fields: [userId], references: [id]) teamWebsite TeamWebsite[] eventData EventData[] + report Report[] + sessionData SessionData[] @@index([userId]) @@index([createdAt]) @@ -92,15 +96,15 @@ model WebsiteEvent { } model EventData { - id String @id() @map("event_id") @db.VarChar(36) - websiteEventId String @map("website_event_id") @db.VarChar(36) - websiteId String @map("website_id") @db.VarChar(36) - eventKey String @map("event_key") @db.VarChar(500) - eventStringValue String? @map("event_string_value") @db.VarChar(500) - eventNumericValue Decimal? @map("event_numeric_value") @db.Decimal(19, 4) - eventDateValue DateTime? @map("event_date_value") @db.Timestamp(0) - eventDataType Int @map("event_data_type") @db.UnsignedInt - createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) + id String @id() @map("event_data_id") @db.VarChar(36) + websiteEventId String @map("website_event_id") @db.VarChar(36) + websiteId String @map("website_id") @db.VarChar(36) + eventKey String @map("event_key") @db.VarChar(500) + stringValue String? @map("string_value") @db.VarChar(500) + numberValue Decimal? @map("number_value") @db.Decimal(19, 4) + dateValue DateTime? @map("date_value") @db.Timestamp(0) + dataType Int @map("data_type") @db.UnsignedInt + createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) website Website @relation(fields: [websiteId], references: [id]) websiteEvent WebsiteEvent @relation(fields: [websiteEventId], references: [id]) @@ -112,12 +116,32 @@ model EventData { @@map("event_data") } +model SessionData { + id String @id() @map("session_data_id") @db.VarChar(36) + websiteId String @map("website_id") @db.VarChar(36) + sessionId String @map("session_id") @db.VarChar(36) + eventKey String @map("event_key") @db.VarChar(500) + stringValue String? @map("string_value") @db.VarChar(500) + numberValue Decimal? @map("number_value") @db.Decimal(19, 4) + dateValue DateTime? @map("date_value") @db.Timestamp(0) + dataType Int @map("data_type") @db.UnsignedInt + createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) + + website Website @relation(fields: [websiteId], references: [id]) + session Session @relation(fields: [sessionId], references: [id]) + + @@index([createdAt]) + @@index([websiteId]) + @@index([sessionId]) + @@map("session_data") +} + model Team { id String @id() @unique() @map("team_id") @db.VarChar(36) name String @db.VarChar(50) accessCode String? @unique @map("access_code") @db.VarChar(50) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamp(0) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0) teamUser TeamUser[] teamWebsite TeamWebsite[] @@ -132,7 +156,7 @@ model TeamUser { userId String @map("user_id") @db.VarChar(36) role String @map("role") @db.VarChar(50) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamp(0) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0) team Team @relation(fields: [teamId], references: [id]) user User @relation(fields: [userId], references: [id]) @@ -155,3 +179,24 @@ model TeamWebsite { @@index([websiteId]) @@map("team_website") } + +model Report { + id String @id() @unique() @map("report_id") @db.VarChar(36) + userId String @map("user_id") @db.VarChar(36) + websiteId String @map("website_id") @db.VarChar(36) + type String @map("type") @db.VarChar(200) + name String @map("name") @db.VarChar(200) + description String @map("description") @db.VarChar(500) + parameters String @map("parameters") @db.VarChar(6000) + createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0) + + user User @relation(fields: [userId], references: [id]) + website Website @relation(fields: [websiteId], references: [id]) + + @@index([userId]) + @@index([websiteId]) + @@index([type]) + @@index([name]) + @@map("report") +} diff --git a/db/postgresql/migrations/02_report_schema_session_data/migration.sql b/db/postgresql/migrations/02_report_schema_session_data/migration.sql new file mode 100644 index 0000000000..5fe6ef9d9f --- /dev/null +++ b/db/postgresql/migrations/02_report_schema_session_data/migration.sql @@ -0,0 +1,70 @@ +-- AlterTable +ALTER TABLE "event_data" RENAME COLUMN "event_data_type" TO "data_type"; +ALTER TABLE "event_data" RENAME COLUMN "event_date_value" TO "date_value"; +ALTER TABLE "event_data" RENAME COLUMN "event_id" TO "event_data_id"; +ALTER TABLE "event_data" RENAME COLUMN "event_numeric_value" TO "number_value"; +ALTER TABLE "event_data" RENAME COLUMN "event_string_value" TO "string_value"; + +-- CreateTable +CREATE TABLE "session_data" ( + "session_data_id" UUID NOT NULL, + "website_id" UUID NOT NULL, + "session_id" UUID NOT NULL, + "session_key" VARCHAR(500) NOT NULL, + "string_value" VARCHAR(500), + "number_value" DECIMAL(19,4), + "date_value" TIMESTAMPTZ(6), + "data_type" INTEGER NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "session_data_pkey" PRIMARY KEY ("session_data_id") +); + +-- CreateTable +CREATE TABLE "report" ( + "report_id" UUID NOT NULL, + "user_id" UUID NOT NULL, + "website_id" UUID NOT NULL, + "type" VARCHAR(200) NOT NULL, + "name" VARCHAR(200) NOT NULL, + "description" VARCHAR(500) NOT NULL, + "parameters" VARCHAR(6000) NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6), + + CONSTRAINT "report_pkey" PRIMARY KEY ("report_id") +); + +-- CreateIndex +CREATE INDEX "session_data_created_at_idx" ON "session_data"("created_at"); + +-- CreateIndex +CREATE INDEX "session_data_website_id_idx" ON "session_data"("website_id"); + +-- CreateIndex +CREATE INDEX "session_data_session_id_idx" ON "session_data"("session_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "report_report_id_key" ON "report"("report_id"); + +-- CreateIndex +CREATE INDEX "report_user_id_idx" ON "report"("user_id"); + +-- CreateIndex +CREATE INDEX "report_website_id_idx" ON "report"("website_id"); + +-- CreateIndex +CREATE INDEX "report_type_idx" ON "report"("type"); + +-- CreateIndex +CREATE INDEX "report_name_idx" ON "report"("name"); + +-- EventData migration +UPDATE "event_data" +SET string_value = number_value +WHERE data_type = 2; + +UPDATE "event_data" +SET string_value = CONCAT(REPLACE(TO_CHAR(date_value, 'YYYY-MM-DD HH24:MI:SS'), ' ', 'T'), 'Z') +WHERE data_type = 4; \ No newline at end of file diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma index b336bce4b7..5753c6ef7e 100644 --- a/db/postgresql/schema.prisma +++ b/db/postgresql/schema.prisma @@ -14,11 +14,12 @@ model User { password String @db.VarChar(60) role String @map("role") @db.VarChar(50) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamptz(6) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6) website Website[] teamUser TeamUser[] + report Report[] @@map("user") } @@ -39,6 +40,7 @@ model Session { createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) websiteEvent WebsiteEvent[] + sessionData SessionData[] @@index([createdAt]) @@index([websiteId]) @@ -53,12 +55,14 @@ model Website { resetAt DateTime? @map("reset_at") @db.Timestamptz(6) userId String? @map("user_id") @db.Uuid createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamptz(6) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6) user User? @relation(fields: [userId], references: [id]) teamWebsite TeamWebsite[] eventData EventData[] + report Report[] + sessionData SessionData[] @@index([userId]) @@index([createdAt]) @@ -92,15 +96,15 @@ model WebsiteEvent { } model EventData { - id String @id() @map("event_id") @db.Uuid - websiteId String @map("website_id") @db.Uuid - websiteEventId String @map("website_event_id") @db.Uuid - eventKey String @map("event_key") @db.VarChar(500) - eventStringValue String? @map("event_string_value") @db.VarChar(500) - eventNumericValue Decimal? @map("event_numeric_value") @db.Decimal(19, 4) - eventDateValue DateTime? @map("event_date_value") @db.Timestamptz(6) - eventDataType Int @map("event_data_type") @db.Integer - createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) + id String @id() @map("event_data_id") @db.Uuid + websiteId String @map("website_id") @db.Uuid + websiteEventId String @map("website_event_id") @db.Uuid + eventKey String @map("event_key") @db.VarChar(500) + stringValue String? @map("string_value") @db.VarChar(500) + numberValue Decimal? @map("number_value") @db.Decimal(19, 4) + dateValue DateTime? @map("date_value") @db.Timestamptz(6) + dataType Int @map("data_type") @db.Integer + createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) website Website @relation(fields: [websiteId], references: [id]) websiteEvent WebsiteEvent @relation(fields: [websiteEventId], references: [id]) @@ -111,12 +115,33 @@ model EventData { @@map("event_data") } +model SessionData { + id String @id() @map("session_data_id") @db.Uuid + websiteId String @map("website_id") @db.Uuid + sessionId String @map("session_id") @db.Uuid + sessionKey String @map("session_key") @db.VarChar(500) + stringValue String? @map("string_value") @db.VarChar(500) + numberValue Decimal? @map("number_value") @db.Decimal(19, 4) + dateValue DateTime? @map("date_value") @db.Timestamptz(6) + dataType Int @map("data_type") @db.Integer + createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) + deletedAt DateTime? @default(now()) @map("deleted_at") @db.Timestamptz(6) + + website Website @relation(fields: [websiteId], references: [id]) + session Session @relation(fields: [sessionId], references: [id]) + + @@index([createdAt]) + @@index([websiteId]) + @@index([sessionId]) + @@map("session_data") +} + model Team { id String @id() @unique() @map("team_id") @db.Uuid name String @db.VarChar(50) accessCode String? @unique @map("access_code") @db.VarChar(50) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamptz(6) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) teamUser TeamUser[] teamWebsite TeamWebsite[] @@ -131,7 +156,7 @@ model TeamUser { userId String @map("user_id") @db.Uuid role String @map("role") @db.VarChar(50) createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) - updatedAt DateTime? @map("updated_at") @updatedAt @db.Timestamptz(6) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) team Team @relation(fields: [teamId], references: [id]) user User @relation(fields: [userId], references: [id]) @@ -154,3 +179,24 @@ model TeamWebsite { @@index([websiteId]) @@map("team_website") } + +model Report { + id String @id() @unique() @map("report_id") @db.Uuid + userId String @map("user_id") @db.Uuid + websiteId String @map("website_id") @db.Uuid + type String @map("type") @db.VarChar(200) + name String @map("name") @db.VarChar(200) + description String @map("description") @db.VarChar(500) + parameters String @map("parameters") @db.VarChar(6000) + createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) + + user User @relation(fields: [userId], references: [id]) + website Website @relation(fields: [websiteId], references: [id]) + + @@index([userId]) + @@index([websiteId]) + @@index([type]) + @@index([name]) + @@map("report") +} diff --git a/docker-compose.yml b/docker-compose.yml index bd63c68b2f..b8da9373ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,8 @@ services: DATABASE_TYPE: postgresql APP_SECRET: replace-me-with-a-random-string depends_on: - - db + db: + condition: service_healthy restart: always db: image: postgres:15-alpine @@ -19,8 +20,12 @@ services: POSTGRES_USER: umami POSTGRES_PASSWORD: umami volumes: - - ./sql/schema.postgresql.sql:/docker-entrypoint-initdb.d/schema.postgresql.sql:ro - umami-db-data:/var/lib/postgresql/data restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 volumes: umami-db-data: diff --git a/hooks/index.js b/hooks/index.js new file mode 100644 index 0000000000..6a9b3b353a --- /dev/null +++ b/hooks/index.js @@ -0,0 +1,21 @@ +export * from './useApi'; +export * from './useConfig'; +export * from './useCountryNames'; +export * from './useDateRange'; +export * from './useDocumentClick'; +export * from './useEscapeKey'; +export * from './useFilters'; +export * from './useForceUpdate'; +export * from './useLanguageNames'; +export * from './useLocale'; +export * from './useMessages'; +export * from './usePageQuery'; +export * from './useReport'; +export * from './useReports'; +export * from './useRequireLogin'; +export * from './useShareToken'; +export * from './useSticky'; +export * from './useTheme'; +export * from './useTimezone'; +export * from './useUser'; +export * from './useWebsite'; diff --git a/hooks/useApi.ts b/hooks/useApi.ts index 9e1e7e20c1..f41547a9e9 100644 --- a/hooks/useApi.ts +++ b/hooks/useApi.ts @@ -7,7 +7,7 @@ import useStore from 'store/app'; const selector = state => state.shareToken; -export default function useApi() { +export function useApi() { const { basePath } = useRouter(); const shareToken = useStore(selector); @@ -18,3 +18,5 @@ export default function useApi() { return { get, post, put, del, ...reactQuery }; } + +export default useApi; diff --git a/hooks/useConfig.js b/hooks/useConfig.js index b395829cbc..6dda7b740f 100644 --- a/hooks/useConfig.js +++ b/hooks/useConfig.js @@ -4,7 +4,7 @@ import useApi from 'hooks/useApi'; let loading = false; -export default function useConfig() { +export function useConfig() { const { config } = useStore(); const { get } = useApi(); @@ -23,3 +23,5 @@ export default function useConfig() { return config || {}; } + +export default useConfig; diff --git a/hooks/useCountryNames.js b/hooks/useCountryNames.js index 0834202bde..51cabf34cf 100644 --- a/hooks/useCountryNames.js +++ b/hooks/useCountryNames.js @@ -1,18 +1,18 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; -import { get } from 'next-basics'; +import { httpGet } from 'next-basics'; import enUS from 'public/intl/country/en-US.json'; const countryNames = { 'en-US': enUS, }; -export default function useCountryNames(locale) { +export function useCountryNames(locale) { const [list, setList] = useState(countryNames[locale] || enUS); const { basePath } = useRouter(); async function loadData(locale) { - const { data } = await get(`${basePath}/intl/country/${locale}.json`); + const { data } = await httpGet(`${basePath}/intl/country/${locale}.json`); if (data) { countryNames[locale] = data; @@ -32,3 +32,5 @@ export default function useCountryNames(locale) { return list; } + +export default useCountryNames; diff --git a/hooks/useDateRange.js b/hooks/useDateRange.js index a9896065c4..17552805d7 100644 --- a/hooks/useDateRange.js +++ b/hooks/useDateRange.js @@ -5,21 +5,23 @@ import useLocale from './useLocale'; import websiteStore, { setWebsiteDateRange } from 'store/websites'; import appStore, { setDateRange } from 'store/app'; -export default function useDateRange(websiteId) { +export function useDateRange(websiteId) { const { locale } = useLocale(); const websiteConfig = websiteStore(state => state[websiteId]?.dateRange); const defaultConfig = DEFAULT_DATE_RANGE; const globalConfig = appStore(state => state.dateRange); const dateRange = parseDateRange(websiteConfig || globalConfig || defaultConfig, locale); - function saveDateRange(value) { + const saveDateRange = value => { if (websiteId) { setWebsiteDateRange(websiteId, value); } else { setItem(DATE_RANGE_CONFIG, value); setDateRange(value); } - } + }; return [dateRange, saveDateRange]; } + +export default useDateRange; diff --git a/hooks/useDocumentClick.js b/hooks/useDocumentClick.js index e1baae7edf..be3d09bed5 100644 --- a/hooks/useDocumentClick.js +++ b/hooks/useDocumentClick.js @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -export default function useDocumentClick(handler) { +export function useDocumentClick(handler) { useEffect(() => { document.addEventListener('click', handler); @@ -11,3 +11,5 @@ export default function useDocumentClick(handler) { return null; } + +export default useDocumentClick; diff --git a/hooks/useEscapeKey.js b/hooks/useEscapeKey.js index b8020c3198..1a17f18f3f 100644 --- a/hooks/useEscapeKey.js +++ b/hooks/useEscapeKey.js @@ -1,6 +1,6 @@ import { useEffect, useCallback } from 'react'; -export default function useEscapeKey(handler) { +export function useEscapeKey(handler) { const escFunction = useCallback(event => { if (event.keyCode === 27) { handler(event); @@ -17,3 +17,5 @@ export default function useEscapeKey(handler) { return null; } + +export default useEscapeKey; diff --git a/hooks/useFilters.js b/hooks/useFilters.js new file mode 100644 index 0000000000..ae01aadb30 --- /dev/null +++ b/hooks/useFilters.js @@ -0,0 +1,32 @@ +import { useMessages } from 'hooks'; + +export function useFilters() { + const { formatMessage, labels } = useMessages(); + + const filters = { + eq: formatMessage(labels.equals), + neq: formatMessage(labels.doesNotEqual), + c: formatMessage(labels.contains), + dnc: formatMessage(labels.doesNotContain), + t: formatMessage(labels.true), + f: formatMessage(labels.false), + gt: formatMessage(labels.greaterThan), + lt: formatMessage(labels.lessThan), + gte: formatMessage(labels.greaterThanEquals), + lte: formatMessage(labels.lessThanEquals), + be: formatMessage(labels.before), + af: formatMessage(labels.after), + }; + + const types = { + string: ['eq', 'neq'], + array: ['c', 'dnc'], + boolean: ['t', 'f'], + number: ['eq', 'neq', 'gt', 'lt', 'gte', 'lte'], + date: ['be', 'af'], + }; + + return { filters, types }; +} + +export default useFilters; diff --git a/hooks/useForceUpdate.js b/hooks/useForceUpdate.js index 2b8d610162..35f7fe1624 100644 --- a/hooks/useForceUpdate.js +++ b/hooks/useForceUpdate.js @@ -1,9 +1,11 @@ import { useCallback, useState } from 'react'; -export default function useForceUpdate() { +export function useForceUpdate() { const [, update] = useState(Object.create(null)); return useCallback(() => { update(Object.create(null)); }, [update]); } + +export default useForceUpdate; diff --git a/hooks/useLanguageNames.js b/hooks/useLanguageNames.js index 3b153f2844..ff59e93dcf 100644 --- a/hooks/useLanguageNames.js +++ b/hooks/useLanguageNames.js @@ -1,18 +1,18 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; -import { get } from 'next-basics'; +import { httpGet } from 'next-basics'; import enUS from 'public/intl/language/en-US.json'; const languageNames = { 'en-US': enUS, }; -export default function useLanguageNames(locale) { +export function useLanguageNames(locale) { const [list, setList] = useState(languageNames[locale] || enUS); const { basePath } = useRouter(); async function loadData(locale) { - const data = await get(`${basePath}/intl/language/${locale}.json`); + const { data } = await httpGet(`${basePath}/intl/language/${locale}.json`); if (data) { languageNames[locale] = data; @@ -32,3 +32,5 @@ export default function useLanguageNames(locale) { return list; } + +export default useLanguageNames; diff --git a/hooks/useLocale.js b/hooks/useLocale.js index 5cece3470a..86ca990422 100644 --- a/hooks/useLocale.js +++ b/hooks/useLocale.js @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; -import { get, setItem } from 'next-basics'; +import { httpGet, setItem } from 'next-basics'; import { LOCALE_CONFIG } from 'lib/constants'; import { getDateLocale, getTextDirection } from 'lib/lang'; import useStore, { setLocale } from 'store/app'; @@ -13,7 +13,7 @@ const messages = { const selector = state => state.locale; -export default function useLocale() { +export function useLocale() { const locale = useStore(selector); const { basePath } = useRouter(); const forceUpdate = useForceUpdate(); @@ -21,7 +21,7 @@ export default function useLocale() { const dateLocale = getDateLocale(locale); async function loadMessages(locale) { - const { ok, data } = await get(`${basePath}/intl/messages/${locale}.json`); + const { ok, data } = await httpGet(`${basePath}/intl/messages/${locale}.json`); if (ok) { messages[locale] = data; @@ -61,3 +61,5 @@ export default function useLocale() { return { locale, saveLocale, messages, dir, dateLocale }; } + +export default useLocale; diff --git a/hooks/useMessages.js b/hooks/useMessages.js index 1bb657787f..0719afd8dd 100644 --- a/hooks/useMessages.js +++ b/hooks/useMessages.js @@ -1,7 +1,7 @@ import { useIntl, FormattedMessage } from 'react-intl'; import { messages, labels } from 'components/messages'; -export default function useMessages() { +export function useMessages() { const { formatMessage } = useIntl(); function getMessage(id) { @@ -12,3 +12,5 @@ export default function useMessages() { return { formatMessage, FormattedMessage, messages, labels, getMessage }; } + +export default useMessages; diff --git a/hooks/usePageQuery.js b/hooks/usePageQuery.js index b2f0acf126..b275d5807c 100644 --- a/hooks/usePageQuery.js +++ b/hooks/usePageQuery.js @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { useRouter } from 'next/router'; import { buildUrl } from 'next-basics'; -export default function usePageQuery() { +export function usePageQuery() { const router = useRouter(); const { pathname, search } = location; const { asPath } = router; @@ -29,3 +29,5 @@ export default function usePageQuery() { return { pathname, query, resolveUrl, router }; } + +export default usePageQuery; diff --git a/hooks/useReport.js b/hooks/useReport.js new file mode 100644 index 0000000000..e036fc3ad2 --- /dev/null +++ b/hooks/useReport.js @@ -0,0 +1,84 @@ +import { produce } from 'immer'; +import { useCallback, useEffect, useState } from 'react'; +import useApi from './useApi'; + +const baseParameters = { + name: 'Untitled', + description: '', + parameters: {}, +}; + +export function useReport(reportId, defaultParameters) { + const [report, setReport] = useState(null); + const [isRunning, setIsRunning] = useState(false); + const { get, post } = useApi(); + + const loadReport = async id => { + const data = await get(`/reports/${id}`); + + const { dateRange } = data?.parameters || {}; + const { startDate, endDate } = dateRange || {}; + + if (startDate && endDate) { + dateRange.startDate = new Date(startDate); + dateRange.endDate = new Date(endDate); + } + + setReport(data); + }; + + const runReport = useCallback( + async parameters => { + setIsRunning(true); + + const { type } = report; + + const data = await post(`/reports/${type}`, parameters); + + setReport( + produce(state => { + state.parameters = parameters; + state.data = data; + + return state; + }), + ); + + setIsRunning(false); + }, + [report], + ); + + const updateReport = useCallback( + async data => { + setReport( + produce(state => { + const { parameters, ...rest } = data; + + if (parameters) { + state.parameters = { ...state.parameters, ...parameters }; + } + + for (const key in rest) { + state[key] = rest[key]; + } + + return state; + }), + ); + }, + [report], + ); + + useEffect(() => { + if (!reportId) { + setReport({ ...baseParameters, ...defaultParameters }); + } else { + loadReport(reportId); + } + }, []); + + return { report, runReport, updateReport, isRunning }; +} + +export default useReport; diff --git a/hooks/useReports.js b/hooks/useReports.js new file mode 100644 index 0000000000..90aa5cf560 --- /dev/null +++ b/hooks/useReports.js @@ -0,0 +1,10 @@ +import useApi from './useApi'; + +export function useReports(websiteId) { + const { get, useQuery } = useApi(); + const { data, error, isLoading } = useQuery(['reports'], () => get(`/reports`, { websiteId })); + + return { reports: data, error, isLoading }; +} + +export default useReports; diff --git a/hooks/useRequireLogin.js b/hooks/useRequireLogin.js index 24cfdf0b39..3a95c9888a 100644 --- a/hooks/useRequireLogin.js +++ b/hooks/useRequireLogin.js @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import useApi from 'hooks/useApi'; import useUser from 'hooks/useUser'; -export default function useRequireLogin() { +export function useRequireLogin() { const router = useRouter(); const { get } = useApi(); const { user, setUser } = useUser(); @@ -26,3 +26,5 @@ export default function useRequireLogin() { return { user }; } + +export default useRequireLogin; diff --git a/hooks/useShareToken.js b/hooks/useShareToken.js index bac7ec97ed..3d6b9698b6 100644 --- a/hooks/useShareToken.js +++ b/hooks/useShareToken.js @@ -4,7 +4,7 @@ import useApi from './useApi'; const selector = state => state.shareToken; -export default function useShareToken(shareId) { +export function useShareToken(shareId) { const shareToken = useStore(selector); const { get } = useApi(); @@ -24,3 +24,5 @@ export default function useShareToken(shareId) { return shareToken; } + +export default useShareToken; diff --git a/hooks/useSticky.js b/hooks/useSticky.js index ae4dce7284..be33f6ed13 100644 --- a/hooks/useSticky.js +++ b/hooks/useSticky.js @@ -1,6 +1,6 @@ import { useState, useEffect, useRef } from 'react'; -export default function useSticky({ enabled = true, threshold = 1 }) { +export function useSticky({ enabled = true, threshold = 1 }) { const [isSticky, setIsSticky] = useState(false); const ref = useRef(null); @@ -21,3 +21,5 @@ export default function useSticky({ enabled = true, threshold = 1 }) { return { ref, isSticky }; } + +export default useSticky; diff --git a/hooks/useTheme.js b/hooks/useTheme.js index c50f442f07..7e40f60151 100644 --- a/hooks/useTheme.js +++ b/hooks/useTheme.js @@ -1,11 +1,12 @@ import { useEffect } from 'react'; import useStore, { setTheme } from 'store/app'; import { getItem, setItem } from 'next-basics'; -import { THEME_CONFIG } from 'lib/constants'; +import { THEME_COLORS, THEME_CONFIG } from 'lib/constants'; +import { colord } from 'colord'; const selector = state => state.theme; -export default function useTheme() { +export function useTheme() { const defaultTheme = typeof window !== 'undefined' ? window?.matchMedia('(prefers-color-scheme: dark)')?.matches @@ -13,6 +14,35 @@ export default function useTheme() { : 'light' : 'light'; const theme = useStore(selector) || getItem(THEME_CONFIG) || defaultTheme; + const primaryColor = colord(THEME_COLORS[theme].primary); + + const colors = { + theme: { + ...THEME_COLORS[theme], + }, + chart: { + text: THEME_COLORS[theme].gray700, + line: THEME_COLORS[theme].gray200, + views: { + hoverBackgroundColor: primaryColor.alpha(0.7).toRgbString(), + backgroundColor: primaryColor.alpha(0.4).toRgbString(), + borderColor: primaryColor.alpha(0.7).toRgbString(), + hoverBorderColor: primaryColor.toRgbString(), + }, + visitors: { + hoverBackgroundColor: primaryColor.alpha(0.9).toRgbString(), + backgroundColor: primaryColor.alpha(0.6).toRgbString(), + borderColor: primaryColor.alpha(0.9).toRgbString(), + hoverBorderColor: primaryColor.toRgbString(), + }, + }, + map: { + baseColor: THEME_COLORS[theme].primary, + fillColor: THEME_COLORS[theme].gray100, + strokeColor: THEME_COLORS[theme].primary, + hoverColor: THEME_COLORS[theme].primary, + }, + }; function saveTheme(value) { setItem(THEME_CONFIG, value); @@ -32,5 +62,7 @@ export default function useTheme() { } }, []); - return [theme, saveTheme]; + return { theme, saveTheme, colors }; } + +export default useTheme; diff --git a/hooks/useTimezone.js b/hooks/useTimezone.js index 8eb5d5f82d..fb347c4d1c 100644 --- a/hooks/useTimezone.js +++ b/hooks/useTimezone.js @@ -3,7 +3,7 @@ import { getTimezone } from 'lib/date'; import { getItem, setItem } from 'next-basics'; import { TIMEZONE_CONFIG } from 'lib/constants'; -export default function useTimezone() { +export function useTimezone() { const [timezone, setTimezone] = useState(getItem(TIMEZONE_CONFIG) || getTimezone()); const saveTimezone = useCallback( @@ -16,3 +16,5 @@ export default function useTimezone() { return [timezone, saveTimezone]; } + +export default useTimezone; diff --git a/hooks/useUser.js b/hooks/useUser.js index 6b73c113e2..c5f1a8267b 100644 --- a/hooks/useUser.js +++ b/hooks/useUser.js @@ -2,8 +2,10 @@ import useStore, { setUser } from 'store/app'; const selector = state => state.user; -export default function useUser() { +export function useUser() { const user = useStore(selector); return { user, setUser }; } + +export default useUser; diff --git a/hooks/useWebsite.js b/hooks/useWebsite.js new file mode 100644 index 0000000000..5315f0dc84 --- /dev/null +++ b/hooks/useWebsite.js @@ -0,0 +1,10 @@ +import useApi from './useApi'; + +export function useWebsite(websiteId) { + const { get, useQuery } = useApi(); + return useQuery(['websites', websiteId], () => get(`/websites/${websiteId}`), { + enabled: !!websiteId, + }); +} + +export default useWebsite; diff --git a/lang-ignore.json b/lang-ignore.json index d4707cda2b..4a0f9d41d7 100644 --- a/lang-ignore.json +++ b/lang-ignore.json @@ -1,16 +1,32 @@ { "cs-CZ": ["label.reset", "metrics.device.tablet"], - "de-DE": [ - "label.administrator", + "de-CH": [ + "label.admin", + "label.analytics", + "label.desktop", + "label.details", + "label.domain", + "label.laptop", + "label.tablet", "label.name", + "label.sessions", + "label.team", + "label.team-id", + "label.teams" + ], + "de-DE": [ + "label.admin", + "label.analytics", + "label.desktop", + "label.details", "label.domain", - "label.theme", - "metrics.device.desktop", - "metrics.device.laptop", - "metrics.device.tablet", - "metrics.referrers", - "metrics.utm", - "metrics.utm_medium" + "label.laptop", + "label.tablet", + "label.name", + "label.sessions", + "label.team", + "label.team-id", + "label.teams" ], "en-GB": "*", "fr-FR": ["metrics.actions", "metrics.pages"], @@ -22,12 +38,15 @@ ], "nb-NO": ["label.administrator", "label.dashboard"], "nl-NL": [ - "label.administrator", - "label.websites", - "metrics.browsers", - "metrics.device.desktop", - "metrics.device.laptop", - "metrics.device.tablet" + "label.analytics", + "label.browsers", + "label.laptop", + "label.tablet", + "label.team", + "label.team-id", + "label.teams", + "label.website-id", + "label.websites" ], "it-IT": [ "label.password", @@ -37,9 +56,5 @@ "metrics.device.tablet", "metrics.filter.raw" ], - "pt-PT": [ - "label.websites", - "metrics.device.desktop", - "metrics.device.tablet" - ] + "pt-PT": ["label.websites", "metrics.device.desktop", "metrics.device.tablet"] } diff --git a/lang/am-ET.json b/lang/am-ET.json index f58a25cdf3..66e0bc6f4a 100644 --- a/lang/am-ET.json +++ b/lang/am-ET.json @@ -1,120 +1,191 @@ { - "label.accounts": "Accounts", - "label.add-account": "Add account", - "label.add-column": "Add column", - "label.add-filter": "Add filter", + "label.access-code": "Access code", + "label.actions": "Actions", + "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Add website", - "label.administrator": "Administrator", + "label.admin": "Administrator", "label.all": "All", "label.all-time": "All time", - "label.all-websites": "All websites", + "label.analytics": "Analytics", + "label.average-visit-time": "Average visit time", "label.back": "Back", + "label.bounce-rate": "Bounce rate", + "label.browsers": "Browsers", "label.cancel": "Cancel", "label.change-password": "Change password", + "label.cities": "Cities", + "label.clear-all": "Clear all", + "label.confirm": "Confirm", "label.confirm-password": "Confirm password", - "label.copy-to-clipboard": "Copy to clipboard", + "label.continue": "Continue", + "label.countries": "Countries", + "label.create-team": "Create team", + "label.create-user": "Create user", + "label.created": "Created", "label.current-password": "Current password", "label.custom-range": "Custom range", "label.dashboard": "Dashboard", + "label.data": "Data", "label.date-range": "Date range", "label.default-date-range": "Default date range", "label.delete": "Delete", - "label.delete-account": "Delete account", + "label.delete-team": "Delete team", + "label.delete-user": "Delete user", "label.delete-website": "Delete website", + "label.desktop": "Desktop", + "label.details": "Details", + "label.devices": "Devices", "label.dismiss": "Dismiss", "label.domain": "Domain", "label.edit": "Edit", - "label.edit-account": "Edit account", - "label.edit-website": "Edit website", + "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Enable share URL", "label.event-data": "Event Data", - "label.field-name": "Field Name", - "label.invalid": "Invalid", - "label.invalid-domain": "Invalid domain", + "label.events": "Events", + "label.field": "Field", + "label.fields": "Fields", + "label.filter-combined": "Combined", + "label.filter-raw": "Raw", + "label.funnel": "Funnel", + "label.join": "Join", + "label.join-team": "Join team", "label.language": "Language", + "label.languages": "Languages", + "label.laptop": "Laptop", "label.last-days": "Last {x} days", "label.last-hours": "Last {x} hours", - "label.logged-in-as": "Logged in as {username}", + "label.leave": "Leave", + "label.leave-team": "Leave team", "label.login": "Login", "label.logout": "Logout", + "label.members": "Members", + "label.mobile": "Mobile", "label.more": "More", "label.name": "Name", "label.new-password": "New password", "label.none": "None", + "label.operating-systems": "Operating systems", "label.owner": "Owner", + "label.page-views": "Page views", + "label.pages": "Pages", "label.password": "Password", - "label.passwords-dont-match": "Passwords don't match", + "label.powered-by": "Powered by {name}", "label.profile": "Profile", + "label.queries": "Queries", + "label.query": "Query", + "label.query-parameters": "Query parameters", "label.realtime": "Realtime", - "label.realtime-logs": "Realtime logs", + "label.referrers": "Referrers", "label.refresh": "Refresh", + "label.regenerate": "Regenerate", + "label.regions": "Regions", + "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Required", "label.reset": "Reset", "label.reset-website": "Reset statistics", + "label.role": "Role", + "label.run-query": "Run query", "label.save": "Save", - "label.search": "Search", + "label.screens": "Screens", + "label.select-date": "Select date", + "label.select-website": "Select website", + "label.sessions": "Sessions", "label.settings": "Settings", "label.share-url": "Share URL", "label.single-day": "Single day", + "label.tablet": "Tablet", + "label.team": "Team", + "label.team-guest": "Team guest", + "label.team-id": "Team ID", + "label.team-member": "Team member", + "label.team-owner": "Team owner", + "label.teams": "Teams", "label.theme": "Theme", "label.this-month": "This month", "label.this-week": "This week", "label.this-year": "This year", "label.timezone": "Timezone", + "label.title": "Title", "label.today": "Today", + "label.toggle-charts": "Toggle charts", "label.tracking-code": "Tracking code", - "label.type": "Type", + "label.unique-visitors": "Unique visitors", "label.unknown": "Unknown", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "User", "label.username": "Username", - "label.value": "Value", + "label.users": "Users", + "label.view": "View", "label.view-details": "View details", + "label.view-only": "View only", + "label.views": "Views", + "label.visitors": "Visitors", + "label.website": "Website", + "label.website-id": "Website ID", "label.websites": "Websites", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}", "message.confirm-delete": "Are you sure you want to delete {target}?", + "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are you sure you want to reset {target}'s statistics?", - "message.copied": "Copied!", - "message.delete-warning": "All associated data will be deleted as well.", - "message.edit-dashboard": "Edit dashboard", - "message.failure": "Something went wrong.", - "message.get-share-url": "Get share URL", - "message.get-tracking-code": "Get tracking code", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", + "message.delete-website-warning": "All website data will be deleted.", + "message.error": "Something went wrong.", + "message.event-log": "{event} on {url}", "message.go-to-settings": "Go to settings", "message.incorrect-username-password": "Incorrect username/password.", - "message.log.visitor": "Visitor from {country} using {browser} on {os} {device}", - "message.new-version-available": "A new version of umami {version} is available!", + "message.invalid-domain": "Invalid domain. Do not include http/https.", + "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "No data available.", - "message.no-websites-configured": "You don't have any websites configured.", + "message.no-event-data": "No event data is available.", + "message.no-match-password": "Passwords do not match.", + "message.no-teams": "You have not created any teams.", + "message.no-users": "There are no users.", "message.page-not-found": "Page not found.", - "message.powered-by": "Powered by {name}", - "message.reset-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", - "message.save-success": "Saved successfully.", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", + "message.reset-website-warning": "All statistics for this website will be deleted, but your settings will remain intact.", + "message.saved": "Saved.", "message.share-url": "This is the publicly shared URL for {target}.", - "message.toggle-charts": "Toggle charts", - "message.track-stats": "To track stats for {target}, place the following code in the {head} section of your website.", - "message.type-delete": "Type {delete} in the box below to confirm.", - "message.type-reset": "Type {reset} in the box below to confirm.", - "metrics.actions": "Actions", - "metrics.average-visit-time": "Average visit time", - "metrics.bounce-rate": "Bounce rate", - "metrics.browsers": "Browsers", - "metrics.countries": "Countries", - "metrics.device.desktop": "Desktop", - "metrics.device.laptop": "Laptop", - "metrics.device.mobile": "Mobile", - "metrics.device.tablet": "Tablet", - "metrics.devices": "Devices", - "metrics.events": "Events", - "metrics.filter.combined": "Combined", - "metrics.filter.raw": "Raw", - "metrics.languages": "Languages", - "metrics.operating-systems": "Operating systems", - "metrics.page-views": "Page views", - "metrics.pages": "Pages", - "metrics.query-parameters": "Query parameters", - "metrics.referrers": "Referrers", - "metrics.screens": "Screens", - "metrics.unique-visitors": "Unique visitors", - "metrics.views": "Views", - "metrics.visitors": "Visitors" + "message.team-already-member": "You are already a member of the team.", + "message.team-not-found": "Team not found.", + "message.tracking-code": "To track stats for this website, place the following code in the ... section of your HTML.", + "message.user-deleted": "User deleted.", + "message.visitor-log": "Visitor from {country} using {browser} on {os} {device}", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "This team does not have any websites.", + "messages.no-websites-configured": "You do not have any websites configured.", + "messages.team-websites-info": "Websites can be viewed by anyone on the team." } diff --git a/lang/ar-SA.json b/lang/ar-SA.json index 6bb3943984..0e7408b315 100644 --- a/lang/ar-SA.json +++ b/lang/ar-SA.json @@ -2,6 +2,8 @@ "label.access-code": "كود الدعوة", "label.actions": "الإجراءات", "label.activity-log": "سجل الأحداث", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "إضافة موقع", "label.admin": "مدير", "label.all": "الكل", @@ -40,9 +42,13 @@ "label.edit": "تعديل", "label.edit-dashboard": "تعديل لوحة التحكم", "label.enable-share-url": "تفعيل مشاركة الرابط", + "label.event-data": "Event data", "label.events": "الأحداث", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "مجمعة", "label.filter-raw": "مفصلة", + "label.funnel": "Funnel", "label.join": "انضمام", "label.join-team": "الانضمام للمجموعة", "label.language": "اللغة", @@ -68,6 +74,7 @@ "label.powered-by": "مشغل بواسطة {name}", "label.profile": "الملف الشخصي", "label.queries": "استعلامات", + "label.query": "Query", "label.query-parameters": "متغيرات الرابط", "label.realtime": "الوقت الفعلي", "label.referrers": "التحويلات", @@ -75,12 +82,15 @@ "label.regenerate": "اعادة انشاء", "label.regions": "المناطق", "label.remove": "إزالة", + "label.reports": "Reports", "label.required": "اجباري", "label.reset": "اعادة تعيين", "label.reset-website": "اعادة تعيين الإحصائيات", "label.role": "الصلاحية", + "label.run-query": "Run query", "label.save": "حفظ", "label.screens": "الشاشات", + "label.select-date": "Select date", "label.select-website": "اختيار موقع", "label.sessions": "الزيارات", "label.settings": "اعدادات", @@ -104,21 +114,54 @@ "label.tracking-code": "كود التتبع", "label.unique-visitors": "زائرون فريدون", "label.unknown": "غير معروف", + "label.url": "URL", + "label.urls": "URLs", "label.user": "مستخدم", "label.username": "اسم المستخدم", "label.users": "المستخدمين", "label.view": "عرض", "label.view-details": "عرض التفاصيل", + "label.view-only": "View only", "label.views": "المشاهدات", "label.visitors": "الزوار", + "label.website": "Website", "label.website-id": "معرف الموقع", "label.websites": "المواقع", + "label.window": "Window", "label.yesterday": "الأمس", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} حاليا {x, plural, one {زائر واحد} other {زوار}}", "message.confirm-delete": "هل أنت متأكد من حذف {target}?", "message.confirm-leave": "هل أنت متأكد من مغادرة {target}?", "message.confirm-reset": "هل أنت متأكد من اعادة تعيين الإحصائيات لـ {target}؟", - "message.delete-website": "حذف الموقع", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "كافة البيانات المرتبطة سيم حذفها ايضا.", "message.error": "حدث خطأ ما.", "message.event-log": "{event} في {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "النطاق غير صحيح", "message.min-password-length": "اقل عدد مسموح به {n} حرف/أحرف", "message.no-data-available": "لا توجد بيانات متاحة.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "كلمة المرور غير متطابقة", "message.no-teams": "لم تقم بإنشاء اي مجموعة.", "message.no-users": "لا يوجد مستخدمين.", "message.page-not-found": "الصفحة غير موجودة.", - "message.reset-website": "اعادة تعيين الإحصائيات", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "سيتم اعادة تعيين كافة الإحصائيات لهذا الموقع، لكن لن يتم تعيير كود التتبع", "message.saved": "تم الحفظ بنجاح.", "message.share-url": "هذا الرابط الذي تم مشاركته بشكل عام لـ {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "كود التتبع", "message.user-deleted": "تم حذف المستخدم.", "message.visitor-log": "زائر من {country} يستخدم {browser} على {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "هذه المجموعة ليس لديه اي موقع.", "messages.no-websites-configured": "لم تقم بإعداد اي موقع.", "messages.team-websites-info": "يمكن مشاهدة الموقع من اي عضو في المجموعة." diff --git a/lang/be-BY.json b/lang/be-BY.json index ed8b9be8e0..13f40a0ec7 100644 --- a/lang/be-BY.json +++ b/lang/be-BY.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Дзеянні", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Дадаць сайт", "label.admin": "Адміністратар", "label.all": "Усё", @@ -40,9 +42,13 @@ "label.edit": "Змяніць", "label.edit-dashboard": "Змяніць інфармацыйную панэль", "label.enable-share-url": "Дазволіць дзяліцца спасылкай", + "label.event-data": "Event data", "label.events": "Падзеі", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Камбініаваны", "label.filter-raw": "Сырыя", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Мова", @@ -68,6 +74,7 @@ "label.powered-by": "Зроблена {name}", "label.profile": "Профіль", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "У рэяльным часе", "label.referrers": "Referrers", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Абавязкова", "label.reset": "Скінуць", "label.reset-website": "Скінуць статыстыку", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Захаваць", "label.screens": "Экраны", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Налады", @@ -104,21 +114,54 @@ "label.tracking-code": "Код адсочвання", "label.unique-visitors": "Унікальныя наведвальнікі", "label.unknown": "Невядома", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Імя карыстальніка", "label.users": "Users", "label.view": "View", "label.view-details": "Пабачыць дэталі", + "label.view-only": "View only", "label.views": "Прагляды", "label.visitors": "Наведвальнікі", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Сайты", + "label.window": "Window", "label.yesterday": "Учора", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} тякучых {x, plural, one {наведвальнік} other {наведвальнікаў}}", "message.confirm-delete": "Вы дакладна хочаце выдаліць {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Вы дакладна хочаце скінуць {target} статыстыку?", - "message.delete-website": "Выдаліць сайт", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Усе асацыяваныя дадзеныя будуць таксама выдалены.", "message.error": "Нешта пайшло не так.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Некарэктны дамен", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Няма дадзеных.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Паролі не супадаюць", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Старонка не знойдзена.", - "message.reset-website": "Скінуць статыстыку", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Уся статыстыка для гэтага сайту будзе выдалена, але код адсочвання будзе працягваць працаваць.", "message.saved": "Захавана паспяхова.", "message.share-url": "Гэта публічная спасылка для {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Код адсочвання", "message.user-deleted": "User deleted.", "message.visitor-log": "Наведвальнік з {country} праз {browser} на {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Вы не наладзілі ніводнага сайту.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/bn-BD.json b/lang/bn-BD.json index eca1363bfe..d955cfc6a1 100644 --- a/lang/bn-BD.json +++ b/lang/bn-BD.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "অ্যাকশনস", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "ওয়েবসাইট যুক্ত করুন", "label.admin": "অ্যাডমিন", "label.all": "সবগুলো", @@ -40,9 +42,13 @@ "label.edit": "সম্পাদনা করুন", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "শেয়ার ইউআরএল শেয়ার করুন", + "label.event-data": "Event data", "label.events": "ঘটনা", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "সম্মিলিত", "label.filter-raw": "অপরিশোধিত", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "ভাষা", @@ -68,6 +74,7 @@ "label.powered-by": "{name} দ্বারা চালিত", "label.profile": "প্রোফাইল", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "সরাসরি", "label.referrers": "রেফারার্স", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "প্রয়োজনীয়", "label.reset": "রিসেট", "label.reset-website": "ওয়েবসাইট রিসেট করুন", "label.role": "Role", + "label.run-query": "Run query", "label.save": "সংরক্ষণ", "label.screens": "স্ক্রিনগুলি", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "সেটিংস", @@ -104,21 +114,54 @@ "label.tracking-code": "ট্র্যাকিং কোড", "label.unique-visitors": "অনন্য ভিজিটর", "label.unknown": "অজানা", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "ব্যবহারকারীর নাম", "label.users": "Users", "label.view": "View", "label.view-details": "বিস্তারিত দেখুন", + "label.view-only": "View only", "label.views": "ভিউস", "label.visitors": "পরিদর্শনার্থী", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "সবগুলো ওয়েবসাইট", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} বর্তমান {x, plural, one {visitor} other {visitors}}", "message.confirm-delete": "আপনি কি নিশ্চিত যে আপনি {target} মুছতে চান?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "আপনি কি নিশ্চিত যে আপনি {target} এর পরিসংখ্যান পুনরায় সেট করতে চান?", - "message.delete-website": "ওয়েবসাইট মুছুন", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "সমস্ত সম্পর্কিত ডেটা পাশাপাশি মুছে ফেলা হবে।", "message.error": "কিছু ভুল হয়েছে।", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "ভুল ডোমেন", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "কোন তথ্য নেই।", + "message.no-event-data": "No event data is available.", "message.no-match-password": "পাসওয়ার্ড মেলে না", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "পৃষ্ঠা খুঁজে পাওয়া যায়নি।", - "message.reset-website": "ওয়েবসাইট রিসেট করুন", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "এই ওয়েবসাইটের সমস্ত পরিসংখ্যান মুছে ফেলা হবে, তবে আপনার ট্র্যাকিং কোডটি অক্ষত থাকবে।", "message.saved": "সংরক্ষিত হয়েছে।", "message.share-url": "এটি {target} এর জন্য প্রকাশ্যে শেয়ার করার ইউআরএল।", @@ -140,6 +184,7 @@ "message.tracking-code": "ট্র্যাকিং কোড", "message.user-deleted": "User deleted.", "message.visitor-log": "{country} থেকে একজন ভিসিটর {ব্রাউজার}, ব্যবহার করছেন {os} {device} এর মধ্যে।", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "কোনও ওয়েবসাইট কনফিগার করা নেই।", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/ca-ES.json b/lang/ca-ES.json index 3f1d791075..441ce6fb28 100644 --- a/lang/ca-ES.json +++ b/lang/ca-ES.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Accions", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Afegeix lloc web", "label.admin": "Administrador", "label.all": "Tots", @@ -40,9 +42,13 @@ "label.edit": "Edita", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Activa l'enllaç per compartir", + "label.event-data": "Event data", "label.events": "Esdeveniments", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combinat", "label.filter-raw": "En cru", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Funciona amb {name}", "label.profile": "Perfil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Temps real", "label.referrers": "Referents", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Obligatori", "label.reset": "Restableix", "label.reset-website": "Restableix estadístiques", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Desa", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Configuració", @@ -104,21 +114,54 @@ "label.tracking-code": "Codi de seguiment", "label.unique-visitors": "Visitants únics", "label.unknown": "Desconegut", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Nom d'usuari", "label.users": "Users", "label.view": "View", "label.view-details": "Veure els detalls", + "label.view-only": "View only", "label.views": "Vistes", "label.visitors": "Visitants", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Llocs web", + "label.window": "Window", "label.yesterday": "Ahir", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {visitant actual} other {visitants actuals}}", "message.confirm-delete": "Segur que vols esborrar {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Segur que vols restablir les estadístiques de {target}?", - "message.delete-website": "Esborra el lloc web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "També s'esborraran totes les dades relacionades.", "message.error": "S'ha produït un error.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Domini invàlid", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "No hi ha dades disponibles.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Les contrasenyes no coincideixen", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "No s'ha trobat la pàgina.", - "message.reset-website": "Restableix estadístiques", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "S'esborraran totes les estadístiques per aquest lloc web, però el codi de seguiment es mantindrà.", "message.saved": "S'ha desat amb èxit.", "message.share-url": "Aquest és l'enllaç públic per compartir de {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Codi de seguiment", "message.user-deleted": "User deleted.", "message.visitor-log": "Visitant de {country} usant {browser} a {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "No hi ha cap lloc web configurat.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/cs-CZ.json b/lang/cs-CZ.json index baf8d5e149..2187268591 100644 --- a/lang/cs-CZ.json +++ b/lang/cs-CZ.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Akce", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Přidat web", "label.admin": "Administrátor", "label.all": "Vše", @@ -40,9 +42,13 @@ "label.edit": "Upravit", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Povolit sdílení URL", + "label.event-data": "Event data", "label.events": "Události", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombinace", "label.filter-raw": "Nezpracované", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Běží na {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Aktuálně", "label.referrers": "Odkazy", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Vyžadováno", "label.reset": "Reset", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Uložit", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Nastavení", @@ -104,21 +114,54 @@ "label.tracking-code": "Sledovací kód", "label.unique-visitors": "Jedinečné návštěvy", "label.unknown": "Neznámý", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Uživatelské jméno", "label.users": "Users", "label.view": "View", "label.view-details": "Zobrazit detaily", + "label.view-only": "View only", "label.views": "Zobrazení", "label.visitors": "Návštěvy", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Weby", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} aktuálně {x, plural, one {návštěvník} other {návštěvníci}}", "message.confirm-delete": "Opravdu smazat {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Smazat web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Všechna související data budou také smazána.", "message.error": "Něco se pokazilo.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Neplatná doména", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Žádná data.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Hesla se neschodují", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Stránka nenalezena.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Úspěšně uloženo.", "message.share-url": "Toto je sdílené URL pro {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Sledovací kód", "message.user-deleted": "User deleted.", "message.visitor-log": "Návštěvník z {country} s prohlížečem {browser} na {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Nemáte nastavený žádný web.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/da-DK.json b/lang/da-DK.json index d8c94f84b2..f64145dcc0 100644 --- a/lang/da-DK.json +++ b/lang/da-DK.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Handlinger", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Tilføj hjemmeside", "label.admin": "Administrator", "label.all": "Alle", @@ -40,9 +42,13 @@ "label.edit": "Rediger", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Aktivér delings-URL", + "label.event-data": "Event data", "label.events": "Hændelser", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombineret", "label.filter-raw": "Rå", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Sprog", @@ -68,6 +74,7 @@ "label.powered-by": "Drevet af {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Realtid", "label.referrers": "Henvisninger", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Påkrævet", "label.reset": "Nulstil", "label.reset-website": "Nulstil statistikker", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Gem", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Indstillinger", @@ -104,21 +114,54 @@ "label.tracking-code": "Sporingskode", "label.unique-visitors": "Unikke besøgende", "label.unknown": "Ukendt", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Brugernavn", "label.users": "Users", "label.view": "View", "label.view-details": "Vis detajler", + "label.view-only": "View only", "label.views": "Visninger", "label.visitors": "Besøgende", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Hjemmesider", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} nuværende {x, plural, one {bruger} other {brugere}}", "message.confirm-delete": "Er du sikker på at du vil slette {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Er du sikker på at du ville nulstille {target}'s statistikker?", - "message.delete-website": "Slet hjemmeside", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Alle tilknyttede data slettes også.", "message.error": "Noget gik galt.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Ugyldigt domæne", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Ingen data tilgængelig.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Adgangskoderne matcher ikke", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Side ikke fundet.", - "message.reset-website": "Nulstil statistikker", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Alle statistikker for denne hjemmeside ville blive slettet, men sporingskode ville forblive intakt.", "message.saved": "Gemt!", "message.share-url": "Dette er den offentlige delings-URL til {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Sporingskode", "message.user-deleted": "User deleted.", "message.visitor-log": "Besøgende fra {country} bruger {browser} på {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Du har ikke konfigureret nogen hjemmesider.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/de-CH.json b/lang/de-CH.json index 03980e5ca0..69864ce919 100644 --- a/lang/de-CH.json +++ b/lang/de-CH.json @@ -1,7 +1,9 @@ { - "label.access-code": "Access code", + "label.access-code": "Zuegangscode", "label.actions": "Aktione", - "label.activity-log": "Activity log", + "label.activity-log": "Aktivitätsverlauf", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Websiite hinzuefüege", "label.admin": "Administrator", "label.all": "Alli", @@ -13,24 +15,24 @@ "label.browsers": "Browser", "label.cancel": "Abbreche", "label.change-password": "Passwort ändere", - "label.cities": "Cities", - "label.clear-all": "Clear all", - "label.confirm": "Confirm", + "label.cities": "Städt", + "label.clear-all": "Alles lösche", + "label.confirm": "Bestätige", "label.confirm-password": "Passwort widerhole", - "label.continue": "Continue", + "label.continue": "Wiiter", "label.countries": "Länder", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", + "label.create-team": "Team erstelle", + "label.create-user": "Benutzer erstelle", + "label.created": "Erstellt", "label.current-password": "Jetzigs Passwort", "label.custom-range": "Benutzerdefinierte Bereich", "label.dashboard": "Übersicht", - "label.data": "Data", + "label.data": "Datä", "label.date-range": "Datumsbereich", "label.default-date-range": "Vorigstellte Datumsbereich", "label.delete": "Lösche", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-team": "Team lösche", + "label.delete-user": "Benutzer lösche", "label.delete-website": "Websiite lösche", "label.desktop": "Desktop", "label.details": "Details", @@ -40,107 +42,150 @@ "label.edit": "Bearbeite", "label.edit-dashboard": "Dashboard bearbeite", "label.enable-share-url": "Freigab-URL aktiviere", + "label.event-data": "Event data", "label.events": "Ereigniss", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombiniert", "label.filter-raw": "Rohdate", - "label.join": "Join", - "label.join-team": "Join team", + "label.funnel": "Funnel", + "label.join": "Biträte", + "label.join-team": "Team biträte", "label.language": "Sprach", "label.languages": "Sprache", "label.laptop": "Laptop", "label.last-days": "Letzti {x} Täg", "label.last-hours": "Letzti {x} Stunde", - "label.leave": "Leave", - "label.leave-team": "Leave team", - "label.login": "Login", + "label.leave": "Verlah", + "label.leave-team": "Team verlah", + "label.login": "Aamelde", "label.logout": "Abmelde", - "label.members": "Members", + "label.members": "Mitglieder", "label.mobile": "Handy", "label.more": "Meh", "label.name": "Name", "label.new-password": "Neus Passwort", "label.none": "Keis", - "label.operating-systems": "Betriebssystem", + "label.operating-systems": "Betriibssystem", "label.owner": "Bsitzer", "label.page-views": "Siitenufrüef", "label.pages": "Siite", "label.password": "Passwort", "label.powered-by": "Betribe dur {name}", "label.profile": "Profil", - "label.queries": "Queries", + "label.queries": "Abfrage", + "label.query": "Query", "label.query-parameters": "Abfragparameter", "label.realtime": "Echtzit", "label.referrers": "Referrer", "label.refresh": "Aktualisiere", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "Erneuere", + "label.regions": "Regionä", + "label.remove": "Entferne", + "label.reports": "Reports", "label.required": "Erforderlich", "label.reset": "Zruggsetze", "label.reset-website": "Statistik zruggsetze", - "label.role": "Role", + "label.role": "Rollä", + "label.run-query": "Run query", "label.save": "Speichere", "label.screens": "Bildschirmuflösige", - "label.select-website": "Select website", + "label.select-date": "Select date", + "label.select-website": "Websiite uuswähle", "label.sessions": "Sessions", "label.settings": "Istellige", "label.share-url": "Freigab-URL", "label.single-day": "Ein Tag", "label.tablet": "Tablet", "label.team": "Team", - "label.team-guest": "Team guest", + "label.team-guest": "Team Gast", "label.team-id": "Team ID", - "label.team-member": "Team member", - "label.team-owner": "Team owner", + "label.team-member": "Team Mitglied", + "label.team-owner": "Team Bsitzer", "label.teams": "Teams", "label.theme": "Thema", "label.this-month": "De Monet", "label.this-week": "Die Wuche", "label.this-year": "Das Jahr", - "label.timezone": "Zitzone", - "label.title": "Title", + "label.timezone": "Ziitzone", + "label.title": "Titel", "label.today": "Hüt", "label.toggle-charts": "Schaubilder umschalte", "label.tracking-code": "Tracking Code", "label.unique-visitors": "Eidütigi Bsuecher", "label.unknown": "Unbekannt", - "label.user": "User", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "Benutzer", "label.username": "Benutzername", - "label.users": "Users", - "label.view": "View", + "label.users": "Benutzer", + "label.view": "Azeige", "label.view-details": "Details azeige", + "label.view-only": "View only", "label.views": "Ufrüef", "label.visitors": "Bsuecher", - "label.website-id": "Website ID", + "label.website": "Website", + "label.website-id": "Websiite ID", "label.websites": "Websiite", + "label.window": "Window", "label.yesterday": "Gester", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {aktive Bsuecher} other {aktivi Bsuecher}}", "message.confirm-delete": "Sind Sie sich sicher, {target} zlösche?", - "message.confirm-leave": "Are you sure you want to leave {target}?", + "message.confirm-leave": "Sind Sie sich sicher, {target} zverlah?", "message.confirm-reset": "Sind Sie sicher, dass Sie dStatistike vo {target} zruggsetze wend?", - "message.delete-website": "Websiite lösche", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Alli dezueghörige Date werdet ebefalls glöscht.", "message.error": "Es isch en Fehler uftrete.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} uf {url}", "message.go-to-settings": "Zu de Istellige", "message.incorrect-username-password": "Falschs Passwort oder Benutzername.", "message.invalid-domain": "Ungültigi Domain", - "message.min-password-length": "Minimum length of {n} characters", + "message.min-password-length": "Miminamli längi vo {n} Zeiche", "message.no-data-available": "Kei Date vorhande.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Passwörter stimmed ned überi", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "message.no-teams": "Bisher sind no kei Teams erstellt worde.", + "message.no-users": "Da gits kei Benutzer", "message.page-not-found": "Siite ned gfunde.", - "message.reset-website": "Statistik zruggsetze", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Alli Date für die Websiite werdet glöscht, nur de Tracking Code blibt bestah.", "message.saved": "Erfolgrich gspeichert.", - "message.share-url": "Das isch die öffentlichi URL zum Teile für {target}.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", + "message.share-url": "Ihri Websiitestatistik isch under de folgende URL öffentlich zuegänglich:", + "message.team-already-member": "Sie sind bereits es Mitglied vo dem Team.", + "message.team-not-found": "Team nöd gfunde.", "message.tracking-code": "Tracking Code", - "message.user-deleted": "User deleted.", + "message.user-deleted": "Benutzer glöscht.", "message.visitor-log": "Bsuecher us {country} benutzt {browser} uf {os} {device}", - "messages.no-team-websites": "This team does not have any websites.", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "Dem Team sind kei Websiite zuegordnet.", "messages.no-websites-configured": "Es isch kei Websiite vorhande.", - "messages.team-websites-info": "Websites can be viewed by anyone on the team." + "messages.team-websites-info": "Websiite chönd vo jedem im Team agluegt werde" } diff --git a/lang/de-DE.json b/lang/de-DE.json index 0190d13c76..4ce7282075 100644 --- a/lang/de-DE.json +++ b/lang/de-DE.json @@ -2,6 +2,8 @@ "label.access-code": "Zugangscode", "label.actions": "Aktionen", "label.activity-log": "Aktivitätsverlauf", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Webseite hinzufügen", "label.admin": "Administrator", "label.all": "Alle", @@ -19,8 +21,8 @@ "label.confirm-password": "Passwort wiederholen", "label.continue": "Weiter", "label.countries": "Länder", - "label.create-team": "Erstelle Team", - "label.create-user": "Erstelle Nutzer", + "label.create-team": "Team erstellen", + "label.create-user": "Benutzer erstellen", "label.created": "Erstellt", "label.current-password": "Derzeitiges Passwort", "label.custom-range": "Benutzerdefinierter Bereich", @@ -29,8 +31,8 @@ "label.date-range": "Datumsbereich", "label.default-date-range": "Voreingestellter Datumsbereich", "label.delete": "Löschen", - "label.delete-team": "Lösche Team", - "label.delete-user": "Lösche Nutzer", + "label.delete-team": "Team löschen", + "label.delete-user": "Benutzer löschen", "label.delete-website": "Webseite löschen", "label.desktop": "Desktop", "label.details": "Details", @@ -40,16 +42,20 @@ "label.edit": "Bearbeiten", "label.edit-dashboard": "Dashboard bearbeiten", "label.enable-share-url": "Freigabe-URL aktivieren", + "label.event-data": "Event data", "label.events": "Ereignisse", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombiniert", "label.filter-raw": "Rohdaten", + "label.funnel": "Funnel", "label.join": "Beitreten", "label.join-team": "Team beitreten", "label.language": "Sprache", "label.languages": "Sprachen", "label.laptop": "Laptop", - "label.last-days": "Letzten {x} Tage", - "label.last-hours": "Letzten {x} Stunden", + "label.last-days": "Letzte {x} Tage", + "label.last-hours": "Letzte {x} Stunden", "label.leave": "Verlassen", "label.leave-team": "Team verlassen", "label.login": "Anmelden", @@ -68,19 +74,23 @@ "label.powered-by": "Betrieben durch {name}", "label.profile": "Profil", "label.queries": "Abfragen", + "label.query": "Query", "label.query-parameters": "Abfrageparameter", "label.realtime": "Echtzeit", "label.referrers": "Referrer", "label.refresh": "Aktualisieren", "label.regenerate": "Erneuern", - "label.regions": "Regions", + "label.regions": "Regionen", "label.remove": "Entfernen", + "label.reports": "Reports", "label.required": "Erforderlich", "label.reset": "Zurücksetzen", "label.reset-website": "Statistik zurücksetzen", "label.role": "Rolle", + "label.run-query": "Run query", "label.save": "Speichern", "label.screens": "Bildschirmauflösungen", + "label.select-date": "Select date", "label.select-website": "Website auswählen", "label.sessions": "Sessions", "label.settings": "Einstellungen", @@ -101,45 +111,80 @@ "label.title": "Titel", "label.today": "Heute", "label.toggle-charts": "Schaubilder umschalten", - "label.tracking-code": "Tracking Kennung", + "label.tracking-code": "Tracking Code", "label.unique-visitors": "Eindeutige Besucher", "label.unknown": "Unbekannt", - "label.user": "User", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "Benutzer", "label.username": "Benutzername", - "label.users": "Users", - "label.view": "View", + "label.users": "Benutzer", + "label.view": "Anzeigen", "label.view-details": "Details anzeigen", + "label.view-only": "View only", "label.views": "Aufrufe", "label.visitors": "Besucher", - "label.website-id": "Website ID", + "label.website": "Website", + "label.website-id": "Webseite ID", "label.websites": "Webseiten", + "label.window": "Window", "label.yesterday": "Gestern", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {aktiver Besucher} other {aktive Besucher}}", "message.confirm-delete": "Sind Sie sich sicher, {target} zu löschen?", "message.confirm-leave": "Sind Sie sicher, dass die {target} verlassen möchten?", "message.confirm-reset": "Sind Sie sicher, dass Sie die Statistiken von {target} zurücksetzen wollen?", - "message.delete-website": "Webseite löschen", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Alle zugehörigen Daten werden ebenfalls gelöscht.", "message.error": "Es ist ein Fehler aufgetreten.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} auf {url}", "message.go-to-settings": "Zu den Einstellungen", "message.incorrect-username-password": "Falsches Passwort oder Benutzername.", "message.invalid-domain": "Ungültige Domain", "message.min-password-length": "Minimale länge von {n} Zeichen", "message.no-data-available": "Keine Daten vorhanden.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Passwörter stimmen nicht überein", "message.no-teams": "Bisher wurden keine Teams erstellt.", - "message.no-users": "Hier gibt es keine Nutzer.", + "message.no-users": "Hier gibt es keine Benutzer.", "message.page-not-found": "Seite nicht gefunden.", - "message.reset-website": "Statistik zurücksetzen", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Alle Daten für diese Webseite werden gelöscht, jedoch bleibt der Tracking Code bestehen.", "message.saved": "Erfolgreich gespeichert.", - "message.share-url": "Dies ist die öffentliche URL zum Teilen für {target}.", + "message.share-url": "Ihre Webseitenstatistik ist unter der folgenden URL öffentlich zugänglich:", "message.team-already-member": "Sie sind bereits Mitglied des Teams.", "message.team-not-found": "Team nicht gefunden.", - "message.tracking-code": "Tracking Kennung", - "message.user-deleted": "Nutzer gelöscht.", + "message.tracking-code": "Tracking Code", + "message.user-deleted": "Benutzer gelöscht.", "message.visitor-log": "Besucher aus {country} benutzt {browser} auf {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "Diesem Team sind keine Websites zugeordnet.", "messages.no-websites-configured": "Es ist keine Webseite vorhanden.", "messages.team-websites-info": "Webseiten können von jedem im Team eingesehen werden." diff --git a/lang/el-GR.json b/lang/el-GR.json index f32cd93a55..1e27fedf30 100644 --- a/lang/el-GR.json +++ b/lang/el-GR.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Ενέργειες", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Προσθήκη ιστότοπου", "label.admin": "Διαχειριστής", "label.all": "All", @@ -40,9 +42,13 @@ "label.edit": "Επεξεργασία", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Ενεργοποίηση κοινής χρήσης URL", + "label.event-data": "Event data", "label.events": "Γεγονότα", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Σε συνδυασμό", "label.filter-raw": "Ακατέργαστο", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Με την υποστήριξη του {name}", "label.profile": "Προφίλ", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Realtime", "label.referrers": "Παραπομπές", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Απαιτείται", "label.reset": "Επαναφορά", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Αποθήκευση", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Ρυθμίσεις", @@ -104,21 +114,54 @@ "label.tracking-code": "Κωδικός παρακολούθησης", "label.unique-visitors": "Μοναδικοί επισκέπτες", "label.unknown": "Άγνωστο", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Όνομα χρήστη", "label.users": "Users", "label.view": "View", "label.view-details": "Λεπτομέρειες", + "label.view-only": "View only", "label.views": "Προβολές", "label.visitors": "Επισκέπτες", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Ιστότοποι", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} ενεργοί {x, plural, one {επισκέπτης} other {επισκέπτες}}", "message.confirm-delete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το {target};", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Διαγραφή ιστότοπου", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Όλα τα σχετικά δεδομένα θα διαγραφούν επίσης.", "message.error": "Κάτι πήγε στραβά.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Μη έγκυρος τομέας", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Δεν υπάρχουν διαθέσιμα δεδομένα.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Οι κωδικοί πρόσβασης δεν ταιριάζουν", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Η σελίδα δεν βρέθηκε.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Αποθηκεύτηκε επιτυχώς.", "message.share-url": "Αυτό είναι το κοινόχρηστο URL για το {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Κωδικός παρακολούθησης", "message.user-deleted": "User deleted.", "message.visitor-log": "Visitor from {country} using {browser} on {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Δεν έχετε ρυθμίσει κανένα ιστότοπο.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/en-GB.json b/lang/en-GB.json index 296106f2b5..77f83a9249 100644 --- a/lang/en-GB.json +++ b/lang/en-GB.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Actions", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Add website", "label.admin": "Administrator", "label.all": "All", @@ -40,9 +42,13 @@ "label.edit": "Edit", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Enable share URL", + "label.event-data": "Event data", "label.events": "Events", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combined", "label.filter-raw": "Raw", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "Profile", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Realtime", "label.referrers": "Referrers", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Required", "label.reset": "Reset", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Save", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Settings", @@ -104,21 +114,54 @@ "label.tracking-code": "Tracking code", "label.unique-visitors": "Unique visitors", "label.unknown": "Unknown", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Username", "label.users": "Users", "label.view": "View", "label.view-details": "View details", + "label.view-only": "View only", "label.views": "Views", "label.visitors": "Visitors", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Websites", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}", "message.confirm-delete": "Are you sure you want to delete {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are you sure you want to reset {target}'s statistics?", - "message.delete-website": "Delete website", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "All associated data will be deleted as well.", "message.error": "Something went wrong.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Invalid domain", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "No data available.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Passwords don't match", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Page not found.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Saved successfully.", "message.share-url": "This is the publicly shared URL for {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Tracking code", "message.user-deleted": "User deleted.", "message.visitor-log": "Visitor from {country} using {browser} on {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "You don't have any websites configured.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/en-US.json b/lang/en-US.json index e80398c6bb..b651e8a33a 100644 --- a/lang/en-US.json +++ b/lang/en-US.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Actions", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Add website", "label.admin": "Administrator", "label.all": "All", @@ -23,7 +25,7 @@ "label.create-user": "Create user", "label.created": "Created", "label.current-password": "Current password", - "label.custom-range": "Custom-range", + "label.custom-range": "Custom range", "label.dashboard": "Dashboard", "label.data": "Data", "label.date-range": "Date range", @@ -40,9 +42,13 @@ "label.edit": "Edit", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Enable share URL", + "label.event-data": "Event data", "label.events": "Events", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combined", "label.filter-raw": "Raw", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "Profile", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Realtime", "label.referrers": "Referrers", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Required", "label.reset": "Reset", "label.reset-website": "Reset website", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Save", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Settings", @@ -104,20 +114,53 @@ "label.tracking-code": "Tracking code", "label.unique-visitors": "Unique visitors", "label.unknown": "Unknown", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Username", "label.users": "Users", "label.view": "View", "label.view-details": "View details", + "label.view-only": "View only", "label.views": "Views", "label.visitors": "Visitors", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Websites", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}", "message.confirm-delete": "Are you sure you want to delete {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are you sure you want to reset {target}?", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "All website data will be deleted.", "message.error": "Something went wrong.", @@ -127,6 +170,7 @@ "message.invalid-domain": "Invalid domain. Do not include http/https.", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "No data available.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Passwords do not match.", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", @@ -140,6 +184,7 @@ "message.tracking-code": "To track stats for this website, place the following code in the ... section of your HTML.", "message.user-deleted": "User deleted.", "message.visitor-log": "Visitor from {country} using {browser} on {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "You do not have any websites configured.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/es-MX.json b/lang/es-MX.json index 999c1d8f8c..f1bbb06da5 100644 --- a/lang/es-MX.json +++ b/lang/es-MX.json @@ -2,6 +2,8 @@ "label.access-code": "Código de acceso", "label.actions": "Acciones", "label.activity-log": "Registro de actividad", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Nuevo sitio web", "label.admin": "Administrador", "label.all": "Todos", @@ -40,9 +42,13 @@ "label.edit": "Editar", "label.edit-dashboard": "Editar panel", "label.enable-share-url": "Habilitar compartir URL", + "label.event-data": "Event data", "label.events": "Eventos", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combinado", "label.filter-raw": "Personalizado", + "label.funnel": "Funnel", "label.join": "Unir", "label.join-team": "Unir a equipo", "label.language": "Idioma", @@ -68,6 +74,7 @@ "label.powered-by": "Analíticas de {name}", "label.profile": "Perfil", "label.queries": "Consultas", + "label.query": "Query", "label.query-parameters": "Parámetros de petición", "label.realtime": "Tiempo real", "label.referrers": "Referido desde", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerar", "label.regions": "Regiones", "label.remove": "Quitar", + "label.reports": "Reports", "label.required": "Obligatorio", "label.reset": "Reiniciar", "label.reset-website": "Reiniciar estadísticas", "label.role": "Rol", + "label.run-query": "Run query", "label.save": "Guardar", "label.screens": "Pantallas", + "label.select-date": "Select date", "label.select-website": "Seleccionar sitio web", "label.sessions": "Sesiones", "label.settings": "Configuraciones", @@ -104,21 +114,54 @@ "label.tracking-code": "Código de rastreo", "label.unique-visitors": "Visitantes únicos", "label.unknown": "Desconocida", + "label.url": "URL", + "label.urls": "URLs", "label.user": "Usuario", "label.username": "Nombre de usuario", "label.users": "Usuarios", "label.view": "Visualizar", "label.view-details": "Ver detalles", + "label.view-only": "View only", "label.views": "Vistas", "label.visitors": "Visitantes", + "label.website": "Website", "label.website-id": "ID del sitio web", "label.websites": "Sitios", + "label.window": "Window", "label.yesterday": "Ayer", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {activo} other {activos}}", "message.confirm-delete": "¿Seguro que quieres eliminar {target}?", "message.confirm-leave": "¿Seguro que quieres abandonar {target}?", "message.confirm-reset": "¿Seguro que quieres BORRAR las analíticas de {target}?", - "message.delete-website": "Eliminar sitio web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Toda la información relacionada será eliminada.", "message.error": "Algo falló.", "message.event-log": "{event} en {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Dominio inválido", "message.min-password-length": "Longitud mínima de {n} caracteres", "message.no-data-available": "No hay información disponible.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Las contraseñas no coinciden", "message.no-teams": "No has creado ningún equipo.", "message.no-users": "No hay usuarios.", "message.page-not-found": "Página no encontrada", - "message.reset-website": "Reiniciar estadísticas", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Todas las estadísticas de esta página serán eliminadas, pero el código de rastreo permanecerá intacto.", "message.saved": "Guardado.", "message.share-url": "Esta es la URL compartida públicamente para {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Código de rastreo", "message.user-deleted": "Usuario eliminado.", "message.visitor-log": "Visitante desde {country} usando {browser} en {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "Este equipo no tiene ningún sitio web configurado.", "messages.no-websites-configured": "No tienes ningún sitio configurado.", "messages.team-websites-info": "Las analíticas de tus sitios pueden verse por cualquier miembro del equipo." diff --git a/lang/fa-IR.json b/lang/fa-IR.json index 94330ce35f..c9053f4e4e 100644 --- a/lang/fa-IR.json +++ b/lang/fa-IR.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "اقدامات", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "افزودن وب‌سایت", "label.admin": "مدیر", "label.all": "همه", @@ -40,9 +42,13 @@ "label.edit": "ویرایش", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "فعال کردن اشتراک گذاری URL", + "label.event-data": "Event data", "label.events": "رویدادها", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "ترکیب شده", "label.filter-raw": "خام", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "زبان", @@ -68,6 +74,7 @@ "label.powered-by": "قدرت گرفته توسط {name}", "label.profile": "پروفایل", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "آمار زنده", "label.referrers": "ارجاع دهندگان", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "ضروری", "label.reset": "بازنشانی", "label.reset-website": "بازنشانی آمار", "label.role": "Role", + "label.run-query": "Run query", "label.save": "ذخیره", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "تنظیمات", @@ -104,21 +114,54 @@ "label.tracking-code": "کد رهگیری", "label.unique-visitors": "بازدیدکننده‌های یکتا", "label.unknown": "ناشناخته", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "نام کاربری", "label.users": "Users", "label.view": "View", "label.view-details": "مشاهده‌ی جزئیات", + "label.view-only": "View only", "label.views": "بازدید", "label.visitors": "بازدیدکننده", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "وب‌سایت‌ها", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} هم اکنون {x, plural, one {یک} other {از میان}}", "message.confirm-delete": "آیا مطمئن هستید می‌خواهید {target} را حذف کنید?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "آیا از بازنشانی آمار {target} مطمئن هستید?", - "message.delete-website": "حذف وب‌سایت", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "همه‌ی داده‌های مرتبط هم حذف خواهد شد.", "message.error": "مشکلی پیش آمده است.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "دامنه‌ی نامعتبر", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "اطلاعاتی موجود نیست.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "رمزها یکسان نیستند", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "صفحه یافت نشد.", - "message.reset-website": "بازنشانی آمار", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "تمامی آمارهای این وب‌سایت حذف خواهد شد اما tracking code بدون تغییر باقی می‌ماند.", "message.saved": "با موفقیت ذخیره شد.", "message.share-url": "این URL به اشتراک گذاشته شده عمومی برای {target} است.", @@ -140,6 +184,7 @@ "message.tracking-code": "کد رهگیری", "message.user-deleted": "User deleted.", "message.visitor-log": "بازدیدکننده از کشور {country} با مروگر {browser} در {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "شما هیچ وب‌سایتی را پیکربندی نکرده‌اید.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/fi-FI.json b/lang/fi-FI.json index 28ef3e72a2..90350eef42 100644 --- a/lang/fi-FI.json +++ b/lang/fi-FI.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Toiminnat", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Lisää verkkosivu", "label.admin": "Järjestelmänvalvoja", "label.all": "Kaikki", @@ -40,9 +42,13 @@ "label.edit": "Muokkaa", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Ota jakamisen URL-osoite käyttöön", + "label.event-data": "Event data", "label.events": "Tapahtumat", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Yhdistetty", "label.filter-raw": "Käsittelemätön", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Kieli", @@ -68,6 +74,7 @@ "label.powered-by": "Voimanlähteenä {name}", "label.profile": "Profiili", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Juuri nyt", "label.referrers": "Viittaajat", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Vaaditaan", "label.reset": "Nollaa", "label.reset-website": "Nollaa tilastot", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Tallenna", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Asetukset", @@ -104,21 +114,54 @@ "label.tracking-code": "Seurantakoodi", "label.unique-visitors": "Yksittäiset kävijät", "label.unknown": "Tuntematon", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Käyttäjänimi", "label.users": "Users", "label.view": "View", "label.view-details": "Katso tiedot", + "label.view-only": "View only", "label.views": "Näyttökerrat", "label.visitors": "Vierailijat", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Verkkosivut", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {vierailija} other {vierailijaa}}", "message.confirm-delete": "Haluatko varmasti poistaa sivuston {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Haluatko varmasti poistaa sivuston {target} tilastot?", - "message.delete-website": "Poista verkkosivu", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Kaikki siihen liittyvät tiedot poistetaan.", "message.error": "Jotain meni pieleen.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Virheellinen verkkotunnus", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Tietoja ei ole käytettävissä.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Salasanat eivät täsmää", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Sivua ei löydetty.", - "message.reset-website": "Nollaa tilastot", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Kaikki sivuston tilastot poistetaan, mutta seurantakoodi pysyy muuttumattomana.", "message.saved": "Tallennettu onnistuneesti.", "message.share-url": "Tämä on julkisesti jaettu URL sivustolle {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Seurantakoodi", "message.user-deleted": "User deleted.", "message.visitor-log": "Vierailija maasta {country} selaimella {browser} laitteella {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Sinulla ei ole määritettyjä verkkosivustoja.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/fo-FO.json b/lang/fo-FO.json index 63e7742fe0..bddd5e9c36 100644 --- a/lang/fo-FO.json +++ b/lang/fo-FO.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Gerðir", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Legg heimasíðu afturat", "label.admin": "Fyrisitari", "label.all": "Alt", @@ -40,9 +42,13 @@ "label.edit": "Ger broyting", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Virkja deili leinki", + "label.event-data": "Event data", "label.events": "Hendingar/tiltøk", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Samansett", "label.filter-raw": "Óviðgjørt", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "Vangi", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Beinleiðis", "label.referrers": "Framsendingar", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Kravt", "label.reset": "Nulstilla", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Goym", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Stillingar", @@ -104,21 +114,54 @@ "label.tracking-code": "Spori kota", "label.unique-visitors": "Einsýna vitjanir", "label.unknown": "Ókent", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Brúkaranavn", "label.users": "Users", "label.view": "View", "label.view-details": "Vís frágreiðing", + "label.view-only": "View only", "label.views": "Sýningar", "label.visitors": "Vitjandi", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Heimasíður", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} í løtuni {x, plural, one {vitjandi} other { vitjandi }}", "message.confirm-delete": "Ert tú sikkur at tú ynskir at strika {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Sletta heimasíðu", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Øll data ið er knýtt at verður eisini strika.", "message.error": "Okkurt bleiv gali.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Ógilt økisnavn", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Einki data tøk.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Loyniorðini eru ikki eins", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Síðan bleiv ikki funnin.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Goymt.", "message.share-url": "Hettar er tann almenna leinkan av {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Spori kota", "message.user-deleted": "User deleted.", "message.visitor-log": "Vitjandi frá {country} brúkar {browser} á {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Tú hevur ongar heimasíður stillaða til.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/fr-FR.json b/lang/fr-FR.json index 162a6f8fe1..ff6de17bdb 100644 --- a/lang/fr-FR.json +++ b/lang/fr-FR.json @@ -2,6 +2,8 @@ "label.access-code": "Code d'accès", "label.actions": "Actions", "label.activity-log": "Journal d'activité", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Ajouter un site", "label.admin": "Administrateur", "label.all": "Tout", @@ -40,9 +42,13 @@ "label.edit": "Modifier", "label.edit-dashboard": "Modifier le tableau de bord", "label.enable-share-url": "Activer l'URL de partage", + "label.event-data": "Event data", "label.events": "Événements", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combiné", "label.filter-raw": "Brut", + "label.funnel": "Funnel", "label.join": "Rejoindre", "label.join-team": "Rejoindre une équipe", "label.language": "Langue", @@ -68,6 +74,7 @@ "label.powered-by": "Propulsé par {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Paramètres d'URL", "label.realtime": "Temps réel", "label.referrers": "Sources", @@ -75,12 +82,15 @@ "label.regenerate": "Régénérer", "label.regions": "Régions", "label.remove": "Retirer", + "label.reports": "Reports", "label.required": "Requis", "label.reset": "Réinitialiser", "label.reset-website": "Réinitialiser les statistiques", "label.role": "Rôle", + "label.run-query": "Run query", "label.save": "Enregistrer", "label.screens": "Résolutions d'écran", + "label.select-date": "Select date", "label.select-website": "Choisir un site", "label.sessions": "Sessions", "label.settings": "Paramètres", @@ -104,20 +114,53 @@ "label.tracking-code": "Code de suivi", "label.unique-visitors": "Visiteurs uniques", "label.unknown": "Inconnu", + "label.url": "URL", + "label.urls": "URLs", "label.user": "Utilisateur", "label.username": "Nom d'utilisateur", "label.users": "Utilisateurs", "label.view": "Voir", "label.view-details": "Voir les détails", + "label.view-only": "View only", "label.views": "Vues", "label.visitors": "Visiteurs", + "label.website": "Website", "label.website-id": "ID de site", "label.websites": "Sites", + "label.window": "Window", "label.yesterday": "Hier", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {visiteur} other {visiteurs}} actuellement", "message.confirm-delete": "Êtes-vous sûr de vouloir supprimer {target} ?", "message.confirm-leave": "Êtes-vous sûr de vouloir quitter {target} ?", "message.confirm-reset": "Êtes-vous sûr de vouloir réinitialiser les statistiques de {target} ?", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", "message.delete-website": "Pour supprimer ce site, taper {confirmation} ci-dessous pour confirmer.", "message.delete-website-warning": "Toutes les données associées seront supprimées.", "message.error": "Un problème est survenu.", @@ -127,11 +170,12 @@ "message.invalid-domain": "Domaine invalide", "message.min-password-length": "Taille minimale de {n} caractères", "message.no-data-available": "Aucune donnée disponible.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Les mots de passe ne correspondent pas", "message.no-teams": "Vous n'avez créé aucune équipe.", "message.no-users": "Il n'y aucun utilisateur.", "message.page-not-found": "Page non trouvée.", - "message.reset-website": "Réinitialiser les statistiques", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Toutes les statistiques pour ce site seront supprimées, mais votre code de suivi restera intact.", "message.saved": "Enregistré avec succès.", "message.share-url": "Les statistiques de votre site sont accessibles publiquement sur cette URL :", @@ -140,6 +184,7 @@ "message.tracking-code": "Code de suivi", "message.user-deleted": "Utilisateur supprimé.", "message.visitor-log": "Visiteur de {country} utilisant {browser} sur {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "Cette équipe n'a aucun site.", "messages.no-websites-configured": "Vous n'avez configuré aucun site.", "messages.team-websites-info": "Les sites peuvent être vus par tout utilisateur dans l'équipe." diff --git a/lang/ga-ES.json b/lang/ga-ES.json index a8aa038302..a811df48f4 100644 --- a/lang/ga-ES.json +++ b/lang/ga-ES.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Accións", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Engadir sitio web", "label.admin": "Administradora", "label.all": "Todo", @@ -40,9 +42,13 @@ "label.edit": "Editar", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Activar URL de compartición", + "label.event-data": "Event data", "label.events": "Eventos", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combinado", "label.filter-raw": "Raw", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Idioma", @@ -68,6 +74,7 @@ "label.powered-by": "Funciona grazas a {name}", "label.profile": "Perfil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Agora mesmo", "label.referrers": "Orixes", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Requerido", "label.reset": "Restablecer", - "label.reset-website": "Restablecer estatísticas", + "label.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Gardar", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Axustes", @@ -104,21 +114,54 @@ "label.tracking-code": "Código de seguimento", "label.unique-visitors": "Visitas únicas", "label.unknown": "Descoñecido", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Identificador", "label.users": "Users", "label.view": "View", "label.view-details": "Ver detalles", + "label.view-only": "View only", "label.views": "Visualizacións", "label.visitors": "Visitantes", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Sitios web", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} actual {x, plural, one {visitante} other {visitantes}}", "message.confirm-delete": "Tes a certeza de querer eliminar {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Tes a certeza de querer restablecer as estatísticas de {target}?", - "message.delete-website": "Eliminar sitio web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Tamén serán borrados tódolos datos asociados.", "message.error": "Houbo un fallo.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Dominio non válido", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Sen datos dispoñibles.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Non concordan os contrasinais", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Páxina non atopada.", - "message.reset-website": "Restablecer estatísticas", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Vanse eliminar tódalas estatísticas deste sitio web, pero o código de seguimento permanecerá sen cambios.", "message.saved": "Gardouse correctamente.", "message.share-url": "Este é o URL da compartición pública de {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Código de seguimento", "message.user-deleted": "User deleted.", "message.visitor-log": "Visitante desde {country} usando {browser} en {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Non tes sitios web configurados.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/he-IL.json b/lang/he-IL.json index f9284e1763..ddc1a86981 100644 --- a/lang/he-IL.json +++ b/lang/he-IL.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "פעולות", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "הוספת אתר", "label.admin": "מנהל", "label.all": "הכל", @@ -40,9 +42,13 @@ "label.edit": "עריכה", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "הפעלת URL שיתוף", + "label.event-data": "Event data", "label.events": "אירועים", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "משותף", "label.filter-raw": "גולמי", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "פרופיל", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "זמן אמת", "label.referrers": "מפנים", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "נדרש", "label.reset": "איפוס", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "שמירה", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "הגדרות", @@ -104,21 +114,54 @@ "label.tracking-code": "קוד מעקב", "label.unique-visitors": "מבקרים ייחודיים", "label.unknown": "לא ידוע", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "שם משתמש", "label.users": "Users", "label.view": "View", "label.view-details": "פרטים נוספים", + "label.view-only": "View only", "label.views": "צפיות", "label.visitors": "מבקרים", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "אתרים", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} נוכחיים {x, plural, one {מבקר} other {מבקרים}}", "message.confirm-delete": "האם באמת למחוק את {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "הסרת אתר", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "כל המידע המקושר יימחק", "message.error": "משהו השתבש", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "דומיין לא תקין", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "אין מידע זמין", + "message.no-event-data": "No event data is available.", "message.no-match-password": "סיסמאות לא תואמות", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "דף לא נמצא", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "נשמר בהצלחה", "message.share-url": "זהו URL ציבורי עבור {target}", @@ -140,6 +184,7 @@ "message.tracking-code": "קוד מעקב", "message.user-deleted": "User deleted.", "message.visitor-log": "מבקר ממדינת {country} משתמבש בדפדפן {browser} ב-{os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "לא מוגדרים אתרים", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/hi-IN.json b/lang/hi-IN.json index 794e0cd5df..d203ac621b 100644 --- a/lang/hi-IN.json +++ b/lang/hi-IN.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "कार्य", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "वेबसाइट", "label.admin": "प्रशासक", "label.all": "सब", @@ -40,9 +42,13 @@ "label.edit": "संपादित करें", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "शेयर URL सक्षम करें", + "label.event-data": "Event data", "label.events": "स्पर्धाएँ", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "संयुक्त", "label.filter-raw": "रॉ", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "{name} द्वारा संचालित", "label.profile": "प्रोफ़ाइल", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "वास्तव काल", "label.referrers": "सन्दर्भदाता", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "अपेक्षित", "label.reset": "रीसेट", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "सहेजें", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "समायोजन", @@ -104,21 +114,54 @@ "label.tracking-code": "ट्रैकिंग कोड", "label.unique-visitors": "अद्वितीय आगंतुकों", "label.unknown": "अज्ञात", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "उपयोगकर्ता नाम", "label.users": "Users", "label.view": "View", "label.view-details": "विवरण देखें", + "label.view-only": "View only", "label.views": "दृश्य", "label.visitors": "आगंतुकों", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "वेबसाइटों", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} मौजूद {x, plural, one {आगंतुक} other {आगंतुकों}}", "message.confirm-delete": "क्या आप वाकई में {target} हटाना चाहते हैं?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "वेबसाइट हटाएं", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "सभी संबद्ध डेटा को भी हटा दिया जाएगा।", "message.error": "कुछ गलत हो गया।", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "अमान्य डोमेन", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "कोई डेटा उपलब्ध नहीं है।", + "message.no-event-data": "No event data is available.", "message.no-match-password": "पासवर्ड मेल नहीं खाते", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "पृष्ठ नहीं मिला।", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "सफलतापूर्वक संचित कर लिया गया है।", "message.share-url": "यह {target} के लिए सार्वजनिक रूप से साझा किया गया URL है।", @@ -140,6 +184,7 @@ "message.tracking-code": "ट्रैकिंग कोड", "message.user-deleted": "User deleted.", "message.visitor-log": "{country} का आगंतुक, जो {browser} का उपयोग करता है, {os} यन्त्र पर", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "आपके पास कोई वेबसाइट कॉन्फ़िगर नहीं है।", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/hr-HR.json b/lang/hr-HR.json index e9072a89b0..f41637464f 100644 --- a/lang/hr-HR.json +++ b/lang/hr-HR.json @@ -1,120 +1,191 @@ { - "label.accounts": "Računi", - "label.add-account": "Dodaj račun", - "label.add-column": "Dodaj stupac", - "label.add-filter": "Dodaj filter", + "label.access-code": "Access code", + "label.actions": "Actions", + "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Dodaj web stranicu", - "label.administrator": "Administrator", + "label.admin": "Administrator", "label.all": "Sve", "label.all-time": "Svo vrijeme", - "label.all-websites": "Sve web stranice", + "label.analytics": "Analytics", + "label.average-visit-time": "Average visit time", "label.back": "Natrag ", + "label.bounce-rate": "Bounce rate", + "label.browsers": "Browsers", "label.cancel": "Odustani", "label.change-password": "Promijeni lozinku", + "label.cities": "Cities", + "label.clear-all": "Clear all", + "label.confirm": "Confirm", "label.confirm-password": "Potvrdi lozinku", - "label.copy-to-clipboard": "Kopiraj u međuspremnik", + "label.continue": "Continue", + "label.countries": "Countries", + "label.create-team": "Create team", + "label.create-user": "Create user", + "label.created": "Created", "label.current-password": "Trenutna lozinka", "label.custom-range": "Prilagođeni raspon", "label.dashboard": "Nadzorna ploča", + "label.data": "Data", "label.date-range": "Raspon datuma", "label.default-date-range": "Zadani datumski raspon", "label.delete": "Obriši", - "label.delete-account": "Obriši račun", + "label.delete-team": "Delete team", + "label.delete-user": "Delete user", "label.delete-website": "Obriši web stranicu", + "label.desktop": "Desktop", + "label.details": "Details", + "label.devices": "Devices", "label.dismiss": "Odbaci", "label.domain": "Domena", "label.edit": "Uredi", - "label.edit-account": "Uredi račun", - "label.edit-website": "Uredi web stranicu", + "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Omogući dijeljenje poveznice", "label.event-data": "Podaci događaja", - "label.field-name": "Naziv polja", - "label.invalid": "Neispravno", - "label.invalid-domain": "Neispravna domena", + "label.events": "Events", + "label.field": "Field", + "label.fields": "Fields", + "label.filter-combined": "Combined", + "label.filter-raw": "Raw", + "label.funnel": "Funnel", + "label.join": "Join", + "label.join-team": "Join team", "label.language": "Jezik", + "label.languages": "Languages", + "label.laptop": "Laptop", "label.last-days": "Zadnjih {x} dana", "label.last-hours": "Zadnjih {x} sati", - "label.logged-in-as": "Prijavljen kao {username}", + "label.leave": "Leave", + "label.leave-team": "Leave team", "label.login": "Prijava", "label.logout": "Odjava", + "label.members": "Members", + "label.mobile": "Mobile", "label.more": "Više", "label.name": "Ime", "label.new-password": "Nova lozinka", "label.none": "Ništa", + "label.operating-systems": "Operating systems", "label.owner": "Vlasnik", + "label.page-views": "Page views", + "label.pages": "Pages", "label.password": "Lozinka", - "label.passwords-dont-match": "Lozinke se ne podudaraju", + "label.powered-by": "Powered by {name}", "label.profile": "Profil", + "label.queries": "Queries", + "label.query": "Query", + "label.query-parameters": "Query parameters", "label.realtime": "Stvarno vrijeme", - "label.realtime-logs": "Trenutni zapisi", + "label.referrers": "Referrers", "label.refresh": "Osvježi", + "label.regenerate": "Regenerate", + "label.regions": "Regions", + "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Potrebna", "label.reset": "Resetirati", "label.reset-website": "Resetirati web stranicu", + "label.role": "Role", + "label.run-query": "Run query", "label.save": "Spremi", - "label.search": "Pretraži", + "label.screens": "Screens", + "label.select-date": "Select date", + "label.select-website": "Select website", + "label.sessions": "Sessions", "label.settings": "Postavke", "label.share-url": "Podijeli poveznicu", "label.single-day": "Jedan dan", + "label.tablet": "Tablet", + "label.team": "Team", + "label.team-guest": "Team guest", + "label.team-id": "Team ID", + "label.team-member": "Team member", + "label.team-owner": "Team owner", + "label.teams": "Teams", "label.theme": "Tema", "label.this-month": "Ovaj mjesec", "label.this-week": "Ovaj tjedan", "label.this-year": "Ova godina", "label.timezone": "Vremenska zona", + "label.title": "Title", "label.today": "Danas", + "label.toggle-charts": "Toggle charts", "label.tracking-code": "Kod za praćenje", - "label.type": "Tip", + "label.unique-visitors": "Unique visitors", "label.unknown": "Nepoznato", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "User", "label.username": "Korisničko ime", - "label.value": "Vrijednost", + "label.users": "Users", + "label.view": "View", "label.view-details": "Pogledaj detalje", + "label.view-only": "View only", + "label.views": "Views", + "label.visitors": "Visitors", + "label.website": "Website", + "label.website-id": "Website ID", "label.websites": "Web stranice", + "label.window": "Window", "label.yesterday": "Jučer", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} Trenutno {x, plural, one {posjetitelj} other {posjetitelja}}", "message.confirm-delete": "Jeste li sigurni da želite obrisati {target}?", + "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Jeste li sigurni da želite resetirati {target}'s statistiku?", - "message.copied": "Kopirano!", - "message.delete-warning": "Izbrisat će se svi povezani podaci.", - "message.edit-dashboard": "Uredi nadzornu ploču", - "message.failure": "Nešto je pošlo po zlu.", - "message.get-share-url": "Dohvati poveznicu za dijeljenje", - "message.get-tracking-code": "Dohvati kod za praćenje", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", + "message.delete-website-warning": "All website data will be deleted.", + "message.error": "Something went wrong.", + "message.event-log": "{event} on {url}", "message.go-to-settings": "Idi u postavke", "message.incorrect-username-password": "Neispravno korisničke ime/lozinka.", - "message.log.visitor": "Posjetitelj iz {country} koristi {browser} na {os} {device}", - "message.new-version-available": "Nova verzija umami {version} je dostupna!", + "message.invalid-domain": "Invalid domain. Do not include http/https.", + "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Nema dostupnih podataka.", - "message.no-websites-configured": "Nemate konfiguriranu nijednu web stranicu.", + "message.no-event-data": "No event data is available.", + "message.no-match-password": "Passwords do not match.", + "message.no-teams": "You have not created any teams.", + "message.no-users": "There are no users.", "message.page-not-found": "Stranica nije pronađena.", - "message.powered-by": "Pokreće {name}", - "message.reset-warning": "Sve statistike za ovu web stranicu bit će izbrisane, ali će vaš kod za praćenje ostati netaknut.", - "message.save-success": "Uspješno spremljeno.", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", + "message.reset-website-warning": "All statistics for this website will be deleted, but your settings will remain intact.", + "message.saved": "Saved.", "message.share-url": "Ovo je javno dijeljena poveznica za {target}.", - "message.toggle-charts": "Uključi/isključi grafikone", - "message.track-stats": "Da biste pratili statistiku za {target}, postavite sljedeći kod u odjeljak {head} svoje web stranice.", - "message.type-delete": "Upišite {delete} u donji okvir za potvrdu.", - "message.type-reset": " Upišite {reset} u donji okvir za potvrdu. ", - "metrics.actions": "Akcije", - "metrics.average-visit-time": "Prosječno vrijeme posjeta", - "metrics.bounce-rate": "Stopa napuštanja stranice", - "metrics.browsers": "Web preglednici", - "metrics.countries": "Zemlje", - "metrics.device.desktop": "Pc", - "metrics.device.laptop": "Laptop", - "metrics.device.mobile": "Mobitel", - "metrics.device.tablet": "Tablet", - "metrics.devices": "Uređaji", - "metrics.events": "Događaji", - "metrics.filter.combined": "Kombinirano", - "metrics.filter.raw": "Neobrađeni podaci", - "metrics.languages": "Jezici", - "metrics.operating-systems": "Operativni sustavi", - "metrics.page-views": "Pregledi stranice", - "metrics.pages": "Stranice", - "metrics.query-parameters": "Parametri upita", - "metrics.referrers": "Upučivaći", - "metrics.screens": "Zasloni", - "metrics.unique-visitors": "Jedinstveni posjetitelji", - "metrics.views": "Pregledi", - "metrics.visitors": "Posjetitelji" + "message.team-already-member": "You are already a member of the team.", + "message.team-not-found": "Team not found.", + "message.tracking-code": "To track stats for this website, place the following code in the ... section of your HTML.", + "message.user-deleted": "User deleted.", + "message.visitor-log": "Visitor from {country} using {browser} on {os} {device}", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "This team does not have any websites.", + "messages.no-websites-configured": "You do not have any websites configured.", + "messages.team-websites-info": "Websites can be viewed by anyone on the team." } diff --git a/lang/hu-HU.json b/lang/hu-HU.json index 803a23324b..064ae9810b 100644 --- a/lang/hu-HU.json +++ b/lang/hu-HU.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Műveletek", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Weboldal hozzáadása", "label.admin": "Adminisztrátor", "label.all": "Összes", @@ -40,9 +42,13 @@ "label.edit": "Módosítás", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "URL-megosztás engedélyezése", + "label.event-data": "Event data", "label.events": "Események", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Összevont", "label.filter-raw": "Nyers", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Működteti az {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Valós idejű", "label.referrers": "Hivatkozók", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Kötelező", "label.reset": "Visszaállítás", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Mentés", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Beállítások", @@ -104,21 +114,54 @@ "label.tracking-code": "Követési kód", "label.unique-visitors": "Egyedi látogatók", "label.unknown": "Ismeretlen", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Felhasználónév", "label.users": "Users", "label.view": "View", "label.view-details": "Részletek", + "label.view-only": "View only", "label.views": "Megtekintések", "label.visitors": "Látogatók", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Weboldalak", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "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.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Weboldal eltávolítása", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Minden társított adat törlésre kerül.", "message.error": "Valami baj történt.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Érvénytelen domain", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Nincs rendelkezésre álló adat.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "A jelszavak nem egyeznek", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Oldal nem található.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Sikeres mentés.", "message.share-url": "{target} nyilvánosan megosztott URL címe.", @@ -140,6 +184,7 @@ "message.tracking-code": "Követési kód", "message.user-deleted": "User deleted.", "message.visitor-log": "Látógató {country} területéről, {os} {device} eszközön, {browser} böngészőből.", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Még nem állítottál be egyetlen weboldalt sem.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/id-ID.json b/lang/id-ID.json index b93172cfb2..bcb24eb07b 100644 --- a/lang/id-ID.json +++ b/lang/id-ID.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Aksi", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Tambah situs web", "label.admin": "Pengelola", "label.all": "Semua", @@ -40,9 +42,13 @@ "label.edit": "Sunting", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Aktifkan URL berbagi", + "label.event-data": "Event data", "label.events": "Perihal", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Gabungan", "label.filter-raw": "Mentah", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Bahasa", @@ -68,6 +74,7 @@ "label.powered-by": "Didukung oleh {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Waktu nyata", "label.referrers": "Perujuk", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Wajib", "label.reset": "Atur ulang", "label.reset-website": "Atur ulang statistik", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Simpan", "label.screens": "Layar", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Pengaturan", @@ -104,21 +114,54 @@ "label.tracking-code": "Kode lacak", "label.unique-visitors": "Pengunjung unik", "label.unknown": "Tidak diketahui", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Nama pengguna", "label.users": "Users", "label.view": "View", "label.view-details": "Lihat Detil", + "label.view-only": "View only", "label.views": "Tampilan", "label.visitors": "Pengunjung", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Situs web", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} pengunjung saat ini", "message.confirm-delete": "Apakah kamu yakin ingin menghapus {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Anda yakin ingin mengatur ulang statistik {target}?", - "message.delete-website": "Hapus situs web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Semua data terkait juga akan dihapus.", "message.error": "Ada yang salah.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Domain tidak valid", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Tidak ada data.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Kata sandi tidak cocok", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Halaman tidak ditemukan.", - "message.reset-website": "Atur ulang statistik", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Semua statistik pada website ini akan dihapus, tetapi kode lacak akan tetap terpasang", "message.saved": "Berhasil disimpan.", "message.share-url": "Ini adalah URL yang dibagikan secara publik untuk {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Kode lacak", "message.user-deleted": "User deleted.", "message.visitor-log": "Pengunjung dari {country} dengan {browser} di {device} {os}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Anda tidak memiliki situs web yang dikonfigurasi.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/it-IT.json b/lang/it-IT.json index 87c2f7c3c0..8c4591bced 100644 --- a/lang/it-IT.json +++ b/lang/it-IT.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Azioni", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Aggiungi sito", "label.admin": "Amministratore", "label.all": "Tutto", @@ -40,9 +42,13 @@ "label.edit": "Modifica", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Abilita URL di condivisione", + "label.event-data": "Event data", "label.events": "Eventi", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Aggregati", "label.filter-raw": "Raw", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Lingua", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "Profilo", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Tempo reale", "label.referrers": "Referrers", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Obbligatorio", "label.reset": "Reset", "label.reset-website": "Resetta le statistiche", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Salva", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Impostazioni", @@ -104,21 +114,54 @@ "label.tracking-code": "Codice di tracking", "label.unique-visitors": "Visitatori unici", "label.unknown": "Sconosciuto", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Nome utente", "label.users": "Users", "label.view": "View", "label.view-details": "Vedi dettagli", + "label.view-only": "View only", "label.views": "Visualizzazioni", "label.visitors": "Visitatori", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Siti web", + "label.window": "Window", "label.yesterday": "Ieri", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {visitatore} other {visitatori}} online", "message.confirm-delete": "Sei sicuro di voler eliminare {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Sei sicuro di voler azzerare le statistiche di {target}?", - "message.delete-website": "Elimina sito", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Saranno eliminati anche tutti i dati associati.", "message.error": "Si è verificato un errore.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Dominio non valido", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Nessun dato disponibile.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Le password non corrispondono", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Pagina non trovata", - "message.reset-website": "Resetta le statistiche", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Tutte le statistiche verranno cancellate per questo sito, ma il tuo codice di tracciamento rimarrà invariato.", "message.saved": "Salvato!", "message.share-url": "Questo è l'URL di condivisione per {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Codice di tracking", "message.user-deleted": "User deleted.", "message.visitor-log": "Utenti da {country} tramite {browser} su {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Non hai ancora configurato alcun sito.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/ja-JP.json b/lang/ja-JP.json index 58a326bd89..07a68a9e92 100644 --- a/lang/ja-JP.json +++ b/lang/ja-JP.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "アクション", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Webサイトの追加", "label.admin": "管理者", "label.all": "すべて表示", @@ -40,9 +42,13 @@ "label.edit": "編集", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "共有リンクを有効にする", + "label.event-data": "Event data", "label.events": "イベント", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "パスまで", "label.filter-raw": "すべて表示", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "このシステムは {name} で実行されています。", "label.profile": "プロファイル", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "リアルタイム", "label.referrers": "リファラー", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "必須", "label.reset": "リセット", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "保存", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "設定", @@ -104,21 +114,54 @@ "label.tracking-code": "トラッキングコード", "label.unique-visitors": "ユニーク訪問者数", "label.unknown": "不明", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "ユーザー名", "label.users": "Users", "label.view": "View", "label.view-details": "詳細を見る", + "label.view-only": "View only", "label.views": "閲覧数", "label.visitors": "訪問者数", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Webサイト", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x}人が閲覧中です。", "message.confirm-delete": "{target}を削除してもよろしいですか?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Webサイトの削除", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "関連するすべてのデータも削除されます。", "message.error": "問題が発生しました。", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "無効なドメイン", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "データがありません。", + "message.no-event-data": "No event data is available.", "message.no-match-password": "パスワードが一致しません", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "ページが見つかりません。", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "正常に保存されました。", "message.share-url": "これは{target}の共有リンクです。", @@ -140,6 +184,7 @@ "message.tracking-code": "トラッキングコード", "message.user-deleted": "User deleted.", "message.visitor-log": "{os}({device})で{browser}を使用している{country}からの訪問者", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Webサイトが設定されていません。", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/km-KH.json b/lang/km-KH.json index 4eec564fab..c2d4920da7 100644 --- a/lang/km-KH.json +++ b/lang/km-KH.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "សកម្មភាព", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "បន្ថែមគេហទំព័រ", "label.admin": "អ្នកគ្រប់គ្រង", "label.all": "ទាំងអស់", @@ -40,9 +42,13 @@ "label.edit": "កែប្រែ", "label.edit-dashboard": "កែផ្ទាំងគ្រប់គ្រង", "label.enable-share-url": "បើកការចែករំលែក URL", + "label.event-data": "Event data", "label.events": "ព្រឹត្តិការណ៍", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "រួមបញ្ចូលគ្នា", "label.filter-raw": "ដើម", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "ភាសា", @@ -68,6 +74,7 @@ "label.powered-by": "ដំណើរការដោយ {name}", "label.profile": "ប្រវត្តិរូប", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "ប៉ារ៉ាម៉ែត្រ Query", "label.realtime": "ឥលូវនេះ", "label.referrers": "អ្នកណែនាំ", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "ទាមទារ", "label.reset": "កំណត់ឡើងវិញ", "label.reset-website": "កំណត់ស្ថិតិឡើងវិញ", "label.role": "Role", + "label.run-query": "Run query", "label.save": "រក្សាទុក", "label.screens": "ប្រភេទឧបករណ៍", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "ការកំណត់", @@ -104,21 +114,54 @@ "label.tracking-code": "លេខកូដតាមដាន", "label.unique-visitors": "អ្នកចូលមើលម្នាក់ៗ", "label.unknown": "មិនស្គាល់", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "ឈ្មោះ​អ្នកប្រើប្រាស់", "label.users": "Users", "label.view": "View", "label.view-details": "មើលព័ត៌មានលម្អិត", + "label.view-only": "View only", "label.views": "អ្នកចូលមើល", "label.visitors": "អ្នកទស្សនា", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "គេហទំព័រ", + "label.window": "Window", "label.yesterday": "ម្សិលមិញ", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "មានអ្នកមើល {x} នាក់ ឥលូវនេះ", "message.confirm-delete": "តើអ្នកប្រាកដថាចង់លុប {target} ទេ?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "តើអ្នកប្រាកដថាចង់កំណត់ស្ថិតិរបស់ {target} ឡើងវិញទេ?", - "message.delete-website": "លុបគេហទំព័រ", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "ទិន្នន័យដែលពាក់ព័ន្ធទាំងអស់នឹងត្រូវបានលុបផងដែរ។", "message.error": "មាន​អ្វីមួយ​មិន​ប្រក្រតី។", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "ឈ្មោះគេហទំព័រមិន​ត្រឹមត្រូវ", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "មិនមានទិន្នន័យទេ។", + "message.no-event-data": "No event data is available.", "message.no-match-password": "ពាក្យសម្ងាត់មិនត្រូវគ្នាទេ", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "រកមិនឃើញទំព័រ។", - "message.reset-website": "កំណត់ស្ថិតិឡើងវិញ", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "ស្ថិតិទាំងអស់សម្រាប់គេហទំព័រនេះនឹងត្រូវបានលុប ប៉ុន្តែកូដតាមដានរបស់អ្នកនឹងនៅដដែល។", "message.saved": "រក្សាទុកដោយជោគជ័យ។", "message.share-url": "នេះគឺជា URL ដែលបានចែករំលែកជាសាធារណៈសម្រាប់ {target}។", @@ -140,6 +184,7 @@ "message.tracking-code": "លេខកូដតាមដាន", "message.user-deleted": "User deleted.", "message.visitor-log": "អ្នកមើលពីប្រទេស {country} ប្រើប្រាស់កម្មវិធី {browser} លើឧបករណ៍ {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "អ្នកមិនទាន់បានដាក់គេហទំព័រណាមួយចូលទេ។", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/ko-KR.json b/lang/ko-KR.json index 633552a735..21fe735037 100644 --- a/lang/ko-KR.json +++ b/lang/ko-KR.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "액션", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "웹사이트 추가", "label.admin": "관리자", "label.all": "전체", @@ -40,9 +42,13 @@ "label.edit": "편집", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "URL 공유 활성화", + "label.event-data": "Event data", "label.events": "이벤트", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "합쳐서 보기", "label.filter-raw": "전체 보기", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "이 시스템은 {name}에서 구동되고 있습니다.", "label.profile": "프로필", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "실시간", "label.referrers": "리퍼러", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "필수", "label.reset": "리셋", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "저장", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "설정", @@ -104,21 +114,54 @@ "label.tracking-code": "추적 코드", "label.unique-visitors": "순방문자(UV)", "label.unknown": "알 수 없음", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "사용자명", "label.users": "Users", "label.view": "View", "label.view-details": "상세보기", + "label.view-only": "View only", "label.views": "조회수", "label.visitors": "방문객", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "웹사이트", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x}명의 사용자가 보는 중입니다.", "message.confirm-delete": "{target}을(를) 삭제하시겠습니까?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "웹사이트 삭제", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "관련된 모든 데이터도 삭제됩니다.", "message.error": "오류가 발생하였습니다.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "잘못된 도메인", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "사용 가능한 데이터가 없습니다.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "비밀번호가 일치하지 않음", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "페이지를 찾을 수 없습니다.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "성공적으로 저장되었습니다.", "message.share-url": "이것은 {target}의 공개적으로 공유된 URL입니다.", @@ -140,6 +184,7 @@ "message.tracking-code": "추적 코드", "message.user-deleted": "User deleted.", "message.visitor-log": "{os} {device}에서 {browser}을(를) 사용하는 {country}의 방문자", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "구성된 웹 사이트가 없습니다.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/lt-LT.json b/lang/lt-LT.json index 768c32dd09..e951bcd314 100644 --- a/lang/lt-LT.json +++ b/lang/lt-LT.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Veiksmai", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Pridėti svetainę", "label.admin": "Administratorius", "label.all": "Visi", @@ -40,9 +42,13 @@ "label.edit": "Redaguoti", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Įjungti bendrinimą su nuoroda", + "label.event-data": "Event data", "label.events": "Įvykiai", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombinuoti", "label.filter-raw": "Neapdoroti", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "Profilis", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Realiuoju laiku", "label.referrers": "Referrers", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Reikalinga", "label.reset": "Atstatyti", "label.reset-website": "Atstatyti statistikos duomenis", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Išsaugoti", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Nustatymai", @@ -104,21 +114,54 @@ "label.tracking-code": "Sekimo kodas", "label.unique-visitors": "Unikalūs lankytojai", "label.unknown": "Nežinoma", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Vartotojo vardas", "label.users": "Users", "label.view": "View", "label.view-details": "Peržiūrėti detaliau", + "label.view-only": "View only", "label.views": "Peržiūros", "label.visitors": "Lankytojai", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Svetainės", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x, plural, =0 {# aktyvių vartotojų} zero {# aktyvių vartotojų} one {# aktyvus vartotojas} other {# aktyvūs vartotojai}}", "message.confirm-delete": "Ar esate tikri, jog norite ištrinti svetainę {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are esate tikri, jog norite atstatyti svetainės {target} statistikos duomenis?", - "message.delete-website": "Ištrinti svetainę", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Visi susiję duomenys taip pat bus ištrinti.", "message.error": "Kažkas įvyko ne taip.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Klaidingas domenas", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Nėra jokių duomenų.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Slaptažodžiai nesutampa", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Puslapis nerastas.", - "message.reset-website": "Atstatyti statistikos duomenis", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Visi šios svetainės statistikos duomenys bus ištrinti, bet sekimo kodas išliks nepaliestas.", "message.saved": "Sėkmingai išsaugota.", "message.share-url": "Tai yra viešai prieinama {target} nuoroda (URL).", @@ -140,6 +184,7 @@ "message.tracking-code": "Sekimo kodas", "message.user-deleted": "User deleted.", "message.visitor-log": "Lankytojas iš {country}, naudojantis {browser} sistemoje {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Jūs nesate susikonfiguravę jokių svetainių.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/mn-MN.json b/lang/mn-MN.json index 9e3b4499dd..100368e358 100644 --- a/lang/mn-MN.json +++ b/lang/mn-MN.json @@ -1,7 +1,9 @@ { - "label.access-code": "Access code", + "label.access-code": "Хандалтын код", "label.actions": "Үйлдлүүд", - "label.activity-log": "Activity log", + "label.activity-log": "Үйл ажиллагааны бүртгэл", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Веб нэмэх", "label.admin": "Админ", "label.all": "Бүх", @@ -13,48 +15,52 @@ "label.browsers": "Хөтөч", "label.cancel": "Цуцлах", "label.change-password": "Нууц үг солих", - "label.cities": "Cities", - "label.clear-all": "Clear all", - "label.confirm": "Confirm", + "label.cities": "Хотууд", + "label.clear-all": "Бүгдийг арилгах", + "label.confirm": "Батлах", "label.confirm-password": "Шинэ нууц үгээ давтах", - "label.continue": "Continue", + "label.continue": "Үргэлжлүүлэх", "label.countries": "Улс", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", + "label.create-team": "Баг үүсгэх", + "label.create-user": "Хэрэглэгч үүсгэх", + "label.created": "Үүсгэсэн", "label.current-password": "Ашиглаж буй нууц үг", "label.custom-range": "Дурын хугацаа", "label.dashboard": "Хянах самбар", - "label.data": "Data", - "label.date-range": "Хугацааны мужид", + "label.data": "Өгөгдөл", + "label.date-range": "Хугацааны муж", "label.default-date-range": "Өгөгдмөл хугацааны муж", "label.delete": "Устгах", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-team": "Баг устгах", + "label.delete-user": "Хэрэглэгч устгах", "label.delete-website": "Веб устгах", "label.desktop": "Суурин компьютер", - "label.details": "Details", + "label.details": "Мэдээлэл", "label.devices": "Төхөөрөмж", "label.dismiss": "Үл хэргэсэх", "label.domain": "Домэйн", "label.edit": "Засах", "label.edit-dashboard": "Хянах самбар засах", "label.enable-share-url": "Хуваалцах холбоос идэвхжүүлэх", + "label.event-data": "Event data", "label.events": "Үйлдэл", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Нэгтгэсэн", "label.filter-raw": "Түүхий", - "label.join": "Join", - "label.join-team": "Join team", + "label.funnel": "Funnel", + "label.join": "Нэгдэх", + "label.join-team": "Багт нэгдэх", "label.language": "Хэл", "label.languages": "Хэл", "label.laptop": "Зөөврийн компьютер", "label.last-days": "Сүүлийн {x} хоног", "label.last-hours": "Сүүлийн {x} цаг", - "label.leave": "Leave", - "label.leave-team": "Leave team", + "label.leave": "Гарах", + "label.leave-team": "Багаас гарах", "label.login": "Нэвтрэх", "label.logout": "Гарах", - "label.members": "Members", + "label.members": "Гишүүд", "label.mobile": "Утас", "label.more": "Цааш", "label.name": "Нэр", @@ -67,80 +73,119 @@ "label.password": "Нууц үг", "label.powered-by": "{name} дээр суурилсан", "label.profile": "Бүртгэл", - "label.queries": "Queries", + "label.queries": "Query-нүүд", + "label.query": "Query", "label.query-parameters": "Query параметр", "label.realtime": "Яг одоо", "label.referrers": "Чиглүүлэгч", "label.refresh": "Сэргээх", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "Дахин үүсгэх", + "label.regions": "Бүсүүд", + "label.remove": "Устгах", + "label.reports": "Reports", "label.required": "Шаардлагатай", - "label.reset": "Хуучин хэвд нь оруулах", + "label.reset": "Дахин эхлүүлэх", "label.reset-website": "Тоон үзүүлэлтийг дахин эхлүүлэх", - "label.role": "Role", + "label.role": "Эрх", + "label.run-query": "Run query", "label.save": "Хадгалах", "label.screens": "Дэлгэц", - "label.select-website": "Select website", + "label.select-date": "Select date", + "label.select-website": "Веб сонгох", "label.sessions": "Sessions", "label.settings": "Тохиргоо", "label.share-url": "Хуваалцах холбоос", "label.single-day": "Нэг өдөр", "label.tablet": "Таблет", - "label.team": "Team", - "label.team-guest": "Team guest", - "label.team-id": "Team ID", - "label.team-member": "Team member", - "label.team-owner": "Team owner", - "label.teams": "Teams", + "label.team": "Баг", + "label.team-guest": "Багийн зочин", + "label.team-id": "Багийн ID", + "label.team-member": "Багийн гишүүн", + "label.team-owner": "Багийн эзэмшигч", + "label.teams": "Багууд", "label.theme": "Загвар", "label.this-month": "Энэ сар", "label.this-week": "Энэ долоо хоног", "label.this-year": "Энэ жил", "label.timezone": "Цагийн бүс", - "label.title": "Title", + "label.title": "Гарчиг", "label.today": "Өнөөдөр", "label.toggle-charts": "Графикийг харуулах/нуух", "label.tracking-code": "Мөрдөх код", "label.unique-visitors": "Зочин", "label.unknown": "Тодорхойгүй", - "label.user": "User", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "Хэрэглэгч", "label.username": "Хэрэглэгчийн нэр", - "label.users": "Users", - "label.view": "View", + "label.users": "Хэрэглэгчид", + "label.view": "Харах", "label.view-details": "Дэлгэрүүлж харах", + "label.view-only": "View only", "label.views": "Үзсэн", "label.visitors": "Зочин", - "label.website-id": "Website ID", + "label.website": "Website", + "label.website-id": "Вебийн ID", "label.websites": "Вебүүд", + "label.window": "Window", "label.yesterday": "Өчигдөр", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "одоо {x} {x, plural, one {зочин} other {зочин}} байна", "message.confirm-delete": "Та {target}-г устгахдаа итгэлтэй байна уу?", - "message.confirm-leave": "Are you sure you want to leave {target}?", + "message.confirm-leave": "Та {target}-с гарахдаа итгэлтэй байна уу?", "message.confirm-reset": "Та {target}-н тоон үзүүлэлтүүдийг устгахдаа итгэлтэй байна уу?", - "message.delete-website": "Веб устгах", - "message.delete-website-warning": "Үүнтэй холбоотой бүх өгөгдөл устах болно.", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", + "message.delete-website-warning": "Энэ вебтэй холбоотой бүх өгөгдөл устах болно.", "message.error": "Ямар нэг зүйл буруу боллоо.", - "message.event-log": "{event} on {url}", + "message.event-log": "{url}-д {event}", "message.go-to-settings": "Тохиргоо руу очих", "message.incorrect-username-password": "Буруу хэрэглэгчийн нэр/нууц үг.", "message.invalid-domain": "Буруу домэйн", - "message.min-password-length": "Minimum length of {n} characters", + "message.min-password-length": "Хамгийн багадаа {n} тэмдэгт", "message.no-data-available": "Өгөгдөл алга.", - "message.no-match-password": "Нууц үг тохирохгүй байна", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "message.no-event-data": "No event data is available.", + "message.no-match-password": "Нууц үг тохирохгүй байна.", + "message.no-teams": "Та ямар ч баг үүсгээгүй байна.", + "message.no-users": "Хэрэглэгч байхгүй байна.", "message.page-not-found": "Хуудас олдсонгүй.", - "message.reset-website": "Тоон үзүүлэлтийг дахин эхлүүлэх", + "message.reset-website": "Тоон үзүүлэлийг дахин эхлүүлэхийн тулд доорх хэсэгт {confirmation} гэж бичиж, баталгаажуулна уу.", "message.reset-website-warning": "Энэ вебийн бүх тоон үзүүлэлтүүдийг устгах болно. Гэхдээ мөрдөх код хэвэндээ үлдэнэ.", - "message.saved": "Амжилттай хадгаллаа.", - "message.share-url": "{target}-г нийтэд хуваалцах холбоос.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", - "message.tracking-code": "Мөрдөх код", - "message.user-deleted": "User deleted.", + "message.saved": "Хадгалсан.", + "message.share-url": "Таны вебийн тоон үзүүлэлтүүд доорх URL дээр нийтэд харагдах болно:", + "message.team-already-member": "Та аль хэдийн энэ багийн гишүүн болсон байна.", + "message.team-not-found": "Баг олдсонгүй.", + "message.tracking-code": "Энэ вебийн хандалтуудыг мөрдөхийн тулд доорх кодыг HTML-нхээ ... хэсэгт байрлуулна уу.", + "message.user-deleted": "Хэрэглэгч устсан.", "message.visitor-log": "{country} улсаас {os} {device} дээр {browser} хөтөч ашиглан орсон", - "messages.no-team-websites": "This team does not have any websites.", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "Энэ багт ямар ч веб алга.", "messages.no-websites-configured": "Та ямар нэгэн веб тохируулаагүй байна.", - "messages.team-websites-info": "Websites can be viewed by anyone on the team." + "messages.team-websites-info": "Вебийг багийн бүх гишүүд үзэж болно." } diff --git a/lang/ms-MY.json b/lang/ms-MY.json index a9469af846..1fc9bce056 100644 --- a/lang/ms-MY.json +++ b/lang/ms-MY.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Aksi", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Tambah laman web", "label.admin": "Pentadbir", "label.all": "Semua", @@ -40,9 +42,13 @@ "label.edit": "Edit", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Aktifkan url berkongsi", + "label.event-data": "Event data", "label.events": "Peristiwa", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Digabungkan", "label.filter-raw": "Mentah", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Disediakan oleh {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Siaran langsung", "label.referrers": "Perujuk", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Diperlukan", "label.reset": "Tetapkan semula", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Simpan", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Tetapan", @@ -104,21 +114,54 @@ "label.tracking-code": "Kod penjejakan", "label.unique-visitors": "Pelawat unik", "label.unknown": "Tidak diketahui", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Nama pengguna", "label.users": "Users", "label.view": "View", "label.view-details": "Lihat butiran", + "label.view-only": "View only", "label.views": "Lawatan", "label.visitors": "Pelawat", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Laman web", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} semasa {x, plural, one {pelawat} other {pelawat}}", "message.confirm-delete": "Pastikah anda ingin memadam {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Padam laman web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Semua data yang berkaitan juga akan dihapuskan.", "message.error": "Ada yang tidak kena.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Domain tidak sah", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Tiada data yang boleh didapati.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Kata laluan tidak sepadan", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Halaman tidak dijumpai.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Berjaya disimpan.", "message.share-url": "Ini adalah URL berkongsi untuk {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Kod penjejakan", "message.user-deleted": "User deleted.", "message.visitor-log": "Pelawat dari {country} mengguna {browser} pada {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Anda tidak ada sebarang laman web yang telah dikonfigurasikan.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/nb-NO.json b/lang/nb-NO.json index 4bb8c74f50..1887096b73 100644 --- a/lang/nb-NO.json +++ b/lang/nb-NO.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Handlinger", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Legg til nettsted", "label.admin": "Administrator", "label.all": "Alle", @@ -40,9 +42,13 @@ "label.edit": "Rediger", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Aktiver delings-URL", + "label.event-data": "Event data", "label.events": "Arrangementer", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombinert", "label.filter-raw": "Rå", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Språk", @@ -68,6 +74,7 @@ "label.powered-by": "Drevet av {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Sanntid", "label.referrers": "Referanser", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Påkrevd", "label.reset": "Nullstill", "label.reset-website": "Nullstill statistikk", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Lagre", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Innstillinger", @@ -104,21 +114,54 @@ "label.tracking-code": "Sporingskode", "label.unique-visitors": "Unike besøkende", "label.unknown": "Ukjent", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Brukernavn", "label.users": "Users", "label.view": "View", "label.view-details": "Vis detaljer", + "label.view-only": "View only", "label.views": "Visninger", "label.visitors": "Besøkende", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Nettsteder", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {besøkende} other {besøkende}} nå", "message.confirm-delete": "Er du sikker på at du vil slette {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Er du sikker på at du vil nullstille {target}'s statistikk?", - "message.delete-website": "Slett nettstedet", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Alle tilknyttede data slettes også.", "message.error": "Noe gikk galt.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Ugyldig domene", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Ingen data tilgjengelig.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Passordene er ikke like", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Side ikke funnet.", - "message.reset-website": "Nullstill statistikk", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistikk for denne nettsiden vil bli slettet, men sporingskoden din vil forbli uberørt.", "message.saved": "Lagret!", "message.share-url": "Dette er den offentlige delings-URL-en for {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Sporingskode", "message.user-deleted": "User deleted.", "message.visitor-log": "Besøkende fra {country} med {browser} på {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Du har ikke satt opp noen nettsteder.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/nl-NL.json b/lang/nl-NL.json index bac675f952..db5e4b7d82 100644 --- a/lang/nl-NL.json +++ b/lang/nl-NL.json @@ -1,9 +1,11 @@ { - "label.access-code": "Access code", + "label.access-code": "Toegangscode", "label.actions": "Acties", - "label.activity-log": "Activity log", - "label.add-website": "Website toevoegen", - "label.admin": "Administrator", + "label.activity-log": "Activiteiten logboek", + "label.add": "Add", + "label.add-description": "Add description", + "label.add-website": "Website koppelen", + "label.admin": "Beheerder", "label.all": "Alles", "label.all-time": "Onbeperkt", "label.analytics": "Analytics", @@ -13,48 +15,52 @@ "label.browsers": "Browsers", "label.cancel": "Annuleren", "label.change-password": "Wachtwoord wijzigen", - "label.cities": "Cities", - "label.clear-all": "Clear all", - "label.confirm": "Confirm", + "label.cities": "Steden", + "label.clear-all": "Filters wissen", + "label.confirm": "Bevestigen", "label.confirm-password": "Wachtwoord bevestigen", - "label.continue": "Continue", + "label.continue": "Doorgaan", "label.countries": "Landen", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", + "label.create-team": "Team aanmaken", + "label.create-user": "Gebruiker maken", + "label.created": "Gemaakt", "label.current-password": "Huidig wachtwoord", "label.custom-range": "Aangepast bereik", "label.dashboard": "Overzicht", - "label.data": "Data", + "label.data": "Gegevens", "label.date-range": "Datumbereik", "label.default-date-range": "Standaard bereik", "label.delete": "Verwijderen", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-team": "Team verwijderen", + "label.delete-user": "Verwijder gebruiker", "label.delete-website": "Website verwijderen", - "label.desktop": "Desktop", - "label.details": "Details", + "label.desktop": "Computer", + "label.details": "Informatie", "label.devices": "Apparaten", "label.dismiss": "Negeren", "label.domain": "Domein", "label.edit": "Bewerken", - "label.edit-dashboard": "Edit dashboard", + "label.edit-dashboard": "Dashboard aanpassen", "label.enable-share-url": "Sta delen via openbare URL toe", + "label.event-data": "Event data", "label.events": "Gebeurtenissen", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Gecombineerd", "label.filter-raw": "Ruw", - "label.join": "Join", - "label.join-team": "Join team", + "label.funnel": "Funnel", + "label.join": "Lid worden", + "label.join-team": "Word lid van een team", "label.language": "Taal", - "label.languages": "Languages", + "label.languages": "Talen", "label.laptop": "Laptop", "label.last-days": "Laatste {x} dagen", "label.last-hours": "Laatste {x} uur", - "label.leave": "Leave", - "label.leave-team": "Leave team", + "label.leave": "Verlaten", + "label.leave-team": "Verlaat team", "label.login": "Inloggen", "label.logout": "Uitloggen", - "label.members": "Members", + "label.members": "Gebruikers", "label.mobile": "Mobiel", "label.more": "Toon meer", "label.name": "Naam", @@ -67,80 +73,119 @@ "label.password": "Wachtwoord", "label.powered-by": "mogelijk gemaakt door {name}", "label.profile": "Profiel", - "label.queries": "Queries", - "label.query-parameters": "Query parameters", + "label.queries": "Parameters", + "label.query": "Query", + "label.query-parameters": "URL-parameters", "label.realtime": "Actueel", "label.referrers": "Verwijzers", "label.refresh": "Vernieuwen", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "Opnieuw genereren", + "label.regions": "Regio's", + "label.remove": "Verwijderen", + "label.reports": "Reports", "label.required": "Verplicht", - "label.reset": "Resetten", + "label.reset": "Opnieuw instellen", "label.reset-website": "Statistieken opnieuw instellen", - "label.role": "Role", + "label.role": "Gebruikersrol", + "label.run-query": "Run query", "label.save": "Opslaan", "label.screens": "Schermen", - "label.select-website": "Select website", - "label.sessions": "Sessions", + "label.select-date": "Select date", + "label.select-website": "Website selecteren", + "label.sessions": "Sessies", "label.settings": "Instellingen", "label.share-url": "URL delen", "label.single-day": "Enkele dag", "label.tablet": "Tablet", "label.team": "Team", - "label.team-guest": "Team guest", + "label.team-guest": "Team gast", "label.team-id": "Team ID", - "label.team-member": "Team member", - "label.team-owner": "Team owner", + "label.team-member": "Teamlid", + "label.team-owner": "Teameigenaar", "label.teams": "Teams", "label.theme": "Thema", "label.this-month": "Deze maand", "label.this-week": "Deze week", "label.this-year": "Dit jaar", "label.timezone": "Tijdzone", - "label.title": "Title", + "label.title": "Titel", "label.today": "Vandaag", "label.toggle-charts": "Grafieken tonen/verbergen", "label.tracking-code": "Volgcode", "label.unique-visitors": "Unieke bezoekers", "label.unknown": "Onbekend", - "label.user": "User", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "Gebruiker", "label.username": "Gebruikersnaam", - "label.users": "Users", - "label.view": "View", + "label.users": "Gebruikers", + "label.view": "Weergave", "label.view-details": "Meer details", + "label.view-only": "View only", "label.views": "Weergaven", "label.visitors": "Bezoekers", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Websites", - "label.yesterday": "Yesterday", + "label.window": "Window", + "label.yesterday": "Gisteren", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} actieve {x, plural, one {bezoeker} other {bezoekers}}", "message.confirm-delete": "Weet je zeker dat je {target} wilt verwijderen?", - "message.confirm-leave": "Are you sure you want to leave {target}?", + "message.confirm-leave": "Weet je zeker dat je {target} wilt verlaten?", "message.confirm-reset": "Weet je zeker dat je de statistieken van {target} opnieuw wilt instellen?", - "message.delete-website": "Website verwijderen", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Alle verwante gegezens zullen ook verwijderd worden.", "message.error": "Er is iets misgegaan.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} op {url}", "message.go-to-settings": "Naar instellingen", "message.incorrect-username-password": "Incorrecte gebruikersnaam/wachtwoord.", "message.invalid-domain": "Ongeldig domein", - "message.min-password-length": "Minimum length of {n} characters", + "message.min-password-length": "Minimale lengte van {n} tekens", "message.no-data-available": "Geen gegevens beschikbaar.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Wachtwoorden komen niet overeen", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "message.no-teams": "Er zijn nog geen teams aangemaakt.", + "message.no-users": "Er zijn geen gebruikers.", "message.page-not-found": "Pagina niet gevonden.", - "message.reset-website": "Statistieken opnieuw instellen", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Alle bijhorende statistieken van deze website worden verwijderd, maar jouw volgcode blijft gelden.", "message.saved": "Opslaan succesvol.", "message.share-url": "Met deze URL kan {target} openbaar gedeeld worden.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", + "message.team-already-member": "Je bent al lid van het team.", + "message.team-not-found": "Team niet gevonden.", "message.tracking-code": "Volgcode", - "message.user-deleted": "User deleted.", + "message.user-deleted": "Gebruiker verwijderd.", "message.visitor-log": "Bezoeker uit {country} met {browser} op een {os} {device}", - "messages.no-team-websites": "This team does not have any websites.", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "Er zijn geen websites gekoppeld aan dit team.", "messages.no-websites-configured": "Je hebt geen websites ingesteld.", - "messages.team-websites-info": "Websites can be viewed by anyone on the team." + "messages.team-websites-info": "Websites kunnen door iedereen in het team worden bekeken." } diff --git a/lang/pl-PL.json b/lang/pl-PL.json index 919fd9b3fb..38b808fc1a 100644 --- a/lang/pl-PL.json +++ b/lang/pl-PL.json @@ -2,6 +2,8 @@ "label.access-code": "Kod dostępu", "label.actions": "Działania", "label.activity-log": "Dziennik aktywności", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Dodaj witrynę", "label.admin": "Administrator", "label.all": "Wszystkie", @@ -40,9 +42,13 @@ "label.edit": "Edytuj", "label.edit-dashboard": "Edytuj panel", "label.enable-share-url": "Włącz udostępnianie adresu URL", + "label.event-data": "Event data", "label.events": "Zdarzenia", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Połączone", "label.filter-raw": "Surowe dane", + "label.funnel": "Funnel", "label.join": "Dołącz", "label.join-team": "Dołącz do zespołu", "label.language": "Język", @@ -68,6 +74,7 @@ "label.powered-by": "Obsługiwane przez {name}", "label.profile": "Profil", "label.queries": "Zapytania", + "label.query": "Query", "label.query-parameters": "Parametry query", "label.realtime": "Czas rzeczywisty", "label.referrers": "Źródła odsyłające", @@ -75,12 +82,15 @@ "label.regenerate": "Wygeneruj ponownie", "label.regions": "Regiony", "label.remove": "Usuń", + "label.reports": "Reports", "label.required": "Wymagany", "label.reset": "Zresetuj", "label.reset-website": "Zresetuj statystyki", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Zapisz", "label.screens": "Ekrany", + "label.select-date": "Select date", "label.select-website": "Wybierz witrynę", "label.sessions": "Sesje", "label.settings": "Ustawienia", @@ -104,21 +114,54 @@ "label.tracking-code": "Kod śledzenia", "label.unique-visitors": "Unikalni odwiedzający", "label.unknown": "Nieznany", + "label.url": "URL", + "label.urls": "URLs", "label.user": "Użytkownik", "label.username": "Nazwa użytkownika", "label.users": "Użytkownicy", "label.view": "Zobacz", "label.view-details": "Pokaż szczegóły", + "label.view-only": "View only", "label.views": "Wyświetlenia", "label.visitors": "Odwiedzający", + "label.website": "Website", "label.website-id": "ID witryny", "label.websites": "Witryny", + "label.window": "Window", "label.yesterday": "Wczoraj", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} aktualnie {x, plural, one {odwiedzający} other {odwiedzających}}", "message.confirm-delete": "Czy na pewno chcesz usunąć {target}?", "message.confirm-leave": "Czy na pewno chcesz opuścić {target}?", "message.confirm-reset": "Czy na pewno chcesz zresetować statystyki {target}?", - "message.delete-website": "Usuń witrynę", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Wszystkie powiązane dane również zostaną usunięte.", "message.error": "Coś poszło nie tak.", "message.event-log": "{event} na {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Nieprawidłowa witryna", "message.min-password-length": "Minimalna długość {n} znaków", "message.no-data-available": "Brak dostępnych danych.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Hasła się nie zgadzają", "message.no-teams": "Nie stworzyłeś żadnych zespołów.", "message.no-users": "Nie ma żadnych użytkowników.", "message.page-not-found": "Strona nie znaleziona.", - "message.reset-website": "Zresetuj statystyki", + "message.reset-website": "Aby zresetować tę witrynę, wpisz {confirmation} w polu poniżej, aby potwierdzić.", "message.reset-website-warning": "Wszystkie statystyki tej witryny zostaną usunięte, ale kod śledzenia pozostanie nienaruszony.", "message.saved": "Zapisano pomyślnie.", "message.share-url": "To jest publicznie udostępniany adres URL dla {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Kod śledzenia", "message.user-deleted": "Użytkownik usunięty.", "message.visitor-log": "Odwiedzający z {country} używa {browser} na {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "Ten zespół nie ma żadnych witryn internetowych.", "messages.no-websites-configured": "Nie masz skonfigurowanych żadnych witryn internetowych.", "messages.team-websites-info": "Strony internetowe mogą być przeglądane przez każdego członka zespołu." diff --git a/lang/pt-BR.json b/lang/pt-BR.json index f02b7fb9a5..9a563acdfa 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -1,27 +1,29 @@ { - "label.access-code": "Access code", + "label.access-code": "Código de acesso", "label.actions": "Ações", - "label.activity-log": "Activity log", + "label.activity-log": "Log de atividade", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Adicionar site", "label.admin": "Administrador", "label.all": "Todos", "label.all-time": "Todo o período", - "label.analytics": "Analytics", + "label.analytics": "Estatísticas", "label.average-visit-time": "Tempo médio da visita", "label.back": "Voltar", "label.bounce-rate": "Taxa de rejeição", "label.browsers": "Navegadores", "label.cancel": "Cancelar", "label.change-password": "Alterar a senha", - "label.cities": "Cities", - "label.clear-all": "Clear all", - "label.confirm": "Confirm", + "label.cities": "Cidades", + "label.clear-all": "Limpar tudo", + "label.confirm": "Confirmar", "label.confirm-password": "Confirme a nova senha", - "label.continue": "Continue", + "label.continue": "Continuar", "label.countries": "Países", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", + "label.create-team": "Criar time", + "label.create-user": "Criar usuário", + "label.created": "Criado", "label.current-password": "Senha atual", "label.custom-range": "Intervalo personalizado", "label.dashboard": "Painel", @@ -29,37 +31,41 @@ "label.date-range": "Intervalo de datas", "label.default-date-range": "Intervalo de datas predefinido", "label.delete": "Remover", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-team": "Remover time", + "label.delete-user": "Remover usuário", "label.delete-website": "Remover site", "label.desktop": "Computador", - "label.details": "Details", + "label.details": "Detalhes", "label.devices": "Dispositivos", "label.dismiss": "Dispensar", "label.domain": "Domínio", "label.edit": "Editar", - "label.edit-dashboard": "Edit dashboard", + "label.edit-dashboard": "Editar painel", "label.enable-share-url": "Ativar link de compartilhamento", + "label.event-data": "Event data", "label.events": "Eventos", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combinado", "label.filter-raw": "Dados brutos", - "label.join": "Join", - "label.join-team": "Join team", + "label.funnel": "Funnel", + "label.join": "Entrar", + "label.join-team": "Entrar no time", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Notebook", "label.last-days": "Últimos {x} dias", "label.last-hours": "Últimas {x} horas", - "label.leave": "Leave", - "label.leave-team": "Leave team", + "label.leave": "Sair", + "label.leave-team": "Sair do time", "label.login": "Iniciar sessão", "label.logout": "Sair", - "label.members": "Members", + "label.members": "Membros", "label.mobile": "Celular", "label.more": "Mais", "label.name": "Nome", "label.new-password": "Nova senha", - "label.none": "None", + "label.none": "Nenhum", "label.operating-systems": "Sistemas operacionais", "label.owner": "Proprietário", "label.page-views": "Visualizações de página", @@ -67,80 +73,119 @@ "label.password": "Senha", "label.powered-by": "Distribuído por {name}", "label.profile": "Perfil", - "label.queries": "Queries", + "label.queries": "Parâmetros", + "label.query": "Query", "label.query-parameters": "Parâmetros de Consulta", "label.realtime": "Tempo real", "label.referrers": "Referências", "label.refresh": "Atualizar", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "Regerar", + "label.regions": "Regiões", + "label.remove": "Remover", + "label.reports": "Reports", "label.required": "Obrigatório", "label.reset": "Redefinir", "label.reset-website": "Redefinir estatísticas", - "label.role": "Role", + "label.role": "Papel", + "label.run-query": "Run query", "label.save": "Salvar", "label.screens": "Telas", - "label.select-website": "Select website", - "label.sessions": "Sessions", + "label.select-date": "Select date", + "label.select-website": "Selecionar site", + "label.sessions": "Sessões", "label.settings": "Configurações", "label.share-url": "Link de compartilhamento", "label.single-day": "Dia específico", "label.tablet": "Tablet", - "label.team": "Team", - "label.team-guest": "Team guest", - "label.team-id": "Team ID", - "label.team-member": "Team member", - "label.team-owner": "Team owner", - "label.teams": "Teams", + "label.team": "Time", + "label.team-guest": "Convidado", + "label.team-id": "ID do Time", + "label.team-member": "Membro", + "label.team-owner": "Proprietário", + "label.teams": "Times", "label.theme": "Tema", "label.this-month": "Este mês", "label.this-week": "Esta semana", "label.this-year": "Este ano", "label.timezone": "Fuso horário", - "label.title": "Title", + "label.title": "Título", "label.today": "Hoje", "label.toggle-charts": "Mostrar/Esconder gráficos", "label.tracking-code": "Código de rastreamento", "label.unique-visitors": "Visitantes únicos", "label.unknown": "Desconhecido", - "label.user": "User", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "Usuário", "label.username": "Nome de usuário", - "label.users": "Users", - "label.view": "View", + "label.users": "Usuários", + "label.view": "Ver", "label.view-details": "Ver detalhes", + "label.view-only": "View only", "label.views": "Visualizações", "label.visitors": "Visitantes", - "label.website-id": "Website ID", + "label.website": "Website", + "label.website-id": "ID do Site", "label.websites": "Sites", + "label.window": "Window", "label.yesterday": "Ontem", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {visitante} other {visitantes}} neste momento", "message.confirm-delete": "Deseja realmente remover {target}?", - "message.confirm-leave": "Are you sure you want to leave {target}?", + "message.confirm-leave": "Você tem certeza que deseja sair de {target}?", "message.confirm-reset": "Você tem certeza que deseja redefinir as estatísticas de {target}?", - "message.delete-website": "Remover site", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Todos os dados associados também serão eliminados.", "message.error": "Ocorreu um erro.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} em {url}", "message.go-to-settings": "Ir para as configurações", "message.incorrect-username-password": "O nome de usuário e/ou senha está incorreto.", "message.invalid-domain": "Domínio inválido", - "message.min-password-length": "Minimum length of {n} characters", + "message.min-password-length": "Quantidade mínima de {n} caracteres", "message.no-data-available": "Sem dados disponíveis.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "As senhas não correspondem", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "message.no-teams": "Você não criou nenhum time.", + "message.no-users": "Não há nenhum usuário.", "message.page-not-found": "Página não encontrada.", - "message.reset-website": "Redefinir estatísticas", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Todas as estatísticas deste site serão removidas, mas seu código de rastreamento permanecerá intacto.", "message.saved": "Salvo com sucesso.", "message.share-url": "Este é o link público de compartilhamento para {target}.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", + "message.team-already-member": "Você já um membro do time.", + "message.team-not-found": "Time não encontrado.", "message.tracking-code": "Código de rastreamento", - "message.user-deleted": "User deleted.", + "message.user-deleted": "Usuário removido.", "message.visitor-log": "Visitante de {country} usando {browser} no {device} {os}", - "messages.no-team-websites": "This team does not have any websites.", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "Este time não possui nenhum site.", "messages.no-websites-configured": "Nenhum site foi configurado ainda.", - "messages.team-websites-info": "Websites can be viewed by anyone on the team." + "messages.team-websites-info": "Os sites podem ser visualizados por qualquer membro da equipe." } diff --git a/lang/pt-PT.json b/lang/pt-PT.json index e67479e59e..826b4dc5a9 100644 --- a/lang/pt-PT.json +++ b/lang/pt-PT.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Ações", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Adicionar website", "label.admin": "Administrador", "label.all": "Todos", @@ -40,9 +42,13 @@ "label.edit": "Editar", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Ativar link de partilha", + "label.event-data": "Event data", "label.events": "Eventos", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combinado", "label.filter-raw": "Dados brutos", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Língua", @@ -68,6 +74,7 @@ "label.powered-by": "Distribuído por {name}", "label.profile": "Perfil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Tempo real", "label.referrers": "Referenciadores", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Obrigatório", "label.reset": "Repor", "label.reset-website": "Repor estatísticas", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Guardar", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Definições", @@ -104,21 +114,54 @@ "label.tracking-code": "Código de rastreamento", "label.unique-visitors": "Visitantes únicos", "label.unknown": "Desconhecido", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Nome de utilizador", "label.users": "Users", "label.view": "View", "label.view-details": "Ver detalhes", + "label.view-only": "View only", "label.views": "Visualizações", "label.visitors": "Visitantes", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Websites", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {visitante} other {visitantes}} neste momento", "message.confirm-delete": "Tem a certeza que pretende eliminar {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Tem a certeza que pretende restaurar as estatísticas de {target}?", - "message.delete-website": "Eliminar website", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Todos os dados associados também serão eliminados.", "message.error": "Ocorreu um erro.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Domínio inválido", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Sem dados disponíveis.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "As senhas não coincidem", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Página não encontrada.", - "message.reset-website": "Repor estatísticas", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Todas as estatísticas deste site serão eliminadas, mas o seu código de rastreamento permanecerá intacto.", "message.saved": "Guardado com sucesso.", "message.share-url": "Este é o link de partilha público para {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Código de rastreamento", "message.user-deleted": "User deleted.", "message.visitor-log": "Visitante de {country} a usar {browser} no {device} {os}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Não tens nenhum website configurado.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/ro-RO.json b/lang/ro-RO.json index deefd899d6..d65fb08ff6 100644 --- a/lang/ro-RO.json +++ b/lang/ro-RO.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Acțiuni", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Adăugare site web", "label.admin": "Administrator", "label.all": "Toate", @@ -40,9 +42,13 @@ "label.edit": "Editare", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Activare adresă URL de distribuire", + "label.event-data": "Event data", "label.events": "Evenimente", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Combinat", "label.filter-raw": "Brut", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Cu sprijinul {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Realtime", "label.referrers": "Site-uri de proveniență", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Obligatoriu", "label.reset": "Resetează", "label.reset-website": "Resetează statisticile pentru site", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Salvează", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Setări", @@ -104,21 +114,54 @@ "label.tracking-code": "Cod de urmărire", "label.unique-visitors": "Vizitatori unici", "label.unknown": "Necunoscut", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Nume utilizator", "label.users": "Users", "label.view": "View", "label.view-details": "Vizualizare detalii", + "label.view-only": "View only", "label.views": "Vizualizări", "label.visitors": "Vizitatori", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Site-uri web", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {vizitator activ} other {vizitatori activi}}", "message.confirm-delete": "Sunteți sigur că doriți să ștergeți {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Sunteți sigur că doriți să resetați statisticile pentru {target}?", - "message.delete-website": "Ștergere site web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Toate datele asociate vor fi șterse, de asemenea.", "message.error": "Ceva n-a mers bine.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Domeniu nu este valid", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Nici o informație disponibilă.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Parolele nu se potrivesc", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Pagina nu a fost găsită.", - "message.reset-website": "Resetează statisticile pentru site", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Toate statisticile pentru acest site web vor fi șterse, dar codul de urmărire va rămâne intact.", "message.saved": "Salvat cu succes.", "message.share-url": "Aceasta este adresa URL de partajare pentru {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Cod de urmărire", "message.user-deleted": "User deleted.", "message.visitor-log": "Vizitator din {country} folosind {browser} pe {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Nu aveți niciun site web configurat.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/ru-RU.json b/lang/ru-RU.json index c5093c8cd3..eb56835af4 100644 --- a/lang/ru-RU.json +++ b/lang/ru-RU.json @@ -2,6 +2,8 @@ "label.access-code": "Код доступа", "label.actions": "Действия", "label.activity-log": "Журнал активности", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Добавить сайт", "label.admin": "Администратор", "label.all": "Все", @@ -37,12 +39,16 @@ "label.devices": "Устройства", "label.dismiss": "Отклонить", "label.domain": "Домен", - "label.edit": "Редактировать", + "label.edit": "Изменить", "label.edit-dashboard": "Редактировать дашборд", "label.enable-share-url": "Разрешить делиться ссылкой", + "label.event-data": "Event data", "label.events": "События", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Объединенные", "label.filter-raw": "Сырые данные", + "label.funnel": "Funnel", "label.join": "Присоединиться", "label.join-team": "Присоединиться к команде", "label.language": "Язык", @@ -68,6 +74,7 @@ "label.powered-by": "На движке {name}", "label.profile": "Профиль", "label.queries": "Запросы", + "label.query": "Query", "label.query-parameters": "Параметры запроса", "label.realtime": "Реальное время", "label.referrers": "Источники", @@ -75,12 +82,15 @@ "label.regenerate": "Обновить", "label.regions": "Регионы", "label.remove": "Удалить", + "label.reports": "Reports", "label.required": "Обязательное", "label.reset": "Сбросить", "label.reset-website": "Сбросить статистику", "label.role": "Роль", + "label.run-query": "Run query", "label.save": "Сохранить", "label.screens": "Экраны", + "label.select-date": "Select date", "label.select-website": "Выбрать сайт", "label.sessions": "Сессии", "label.settings": "Настройки", @@ -104,21 +114,54 @@ "label.tracking-code": "Код отслеживания", "label.unique-visitors": "Уникальные посетители", "label.unknown": "Неизвестно", + "label.url": "URL", + "label.urls": "URLs", "label.user": "Пользователь", "label.username": "Имя пользователя", "label.users": "Пользователи", "label.view": "Просмотреть", "label.view-details": "Посмотреть детали", + "label.view-only": "View only", "label.views": "Просмотры", "label.visitors": "Посетители", + "label.website": "Website", "label.website-id": "ID сайта", "label.websites": "Сайты", + "label.window": "Window", "label.yesterday": "Вчера", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} текущих посетителей", "message.confirm-delete": "Вы уверены, что хотите удалить {target}?", "message.confirm-leave": "Вы уверены, что хотите уйти {target}?", "message.confirm-reset": "Вы уверены, что хотите сбросить статистику {target}?", - "message.delete-website": "Удалить сайт", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "Для удаления введите DELETE", "message.delete-website-warning": "Все связанные данные будут также удалены.", "message.error": "Что-то пошло не так.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Некорректный домен", "message.min-password-length": "Минимальная длина {n} символов", "message.no-data-available": "Нет данных.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Пароли не совпадают", "message.no-teams": "Вы не создали ни одной команды.", "message.no-users": "Нет пользователей.", "message.page-not-found": "Страница не найдена.", - "message.reset-website": "Сбросить статистику", + "message.reset-website": "Для сброса введите RESET", "message.reset-website-warning": "Вся статистика для этого сайта будет удалена, но ваш код отслеживания останется нетронутым.", "message.saved": "Успешно сохранено.", "message.share-url": "Это публичная ссылка для {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Код отслеживания", "message.user-deleted": "Пользователь удален.", "message.visitor-log": "Посетитель из {country} используя {browser} на {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "У этой команды нет ни одного сайта.", "messages.no-websites-configured": "У вас нет настроенных сайтов.", "messages.team-websites-info": "Сайты могут просматривать все члены команды." diff --git a/lang/si-LK.json b/lang/si-LK.json index 3334608702..d23d008cec 100644 --- a/lang/si-LK.json +++ b/lang/si-LK.json @@ -1,120 +1,191 @@ { - "label.accounts": "ගිණුම්", - "label.add-account": "ගිණුම එකතු කරන්න", - "label.add-column": "තීරුව එක් කරන්න", - "label.add-filter": "පෙරහන එකතු කරන්න", + "label.access-code": "Access code", + "label.actions": "Actions", + "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "වෙබ් අඩවිය එක් කරන්න", - "label.administrator": "පරිපාලක", + "label.admin": "Administrator", "label.all": "සියල්ල", "label.all-time": "හැම වෙලාවෙම", - "label.all-websites": "සියලුම වෙබ් අඩවි", + "label.analytics": "Analytics", + "label.average-visit-time": "Average visit time", "label.back": "ආපසු", + "label.bounce-rate": "Bounce rate", + "label.browsers": "Browsers", "label.cancel": "අවලංගු කරන්න", "label.change-password": "මුරපදය වෙනස් කරන්න", + "label.cities": "Cities", + "label.clear-all": "Clear all", + "label.confirm": "Confirm", "label.confirm-password": "මුරපදය සත්‍යාපනය කරන්න", - "label.copy-to-clipboard": "පසුරු පුවරුවට පිටපත් කරන්න", + "label.continue": "Continue", + "label.countries": "Countries", + "label.create-team": "Create team", + "label.create-user": "Create user", + "label.created": "Created", "label.current-password": "වත්මන් මුරපදය", "label.custom-range": "අභිරුචි පරාසය", "label.dashboard": "උපකරණ පුවරුව", + "label.data": "Data", "label.date-range": "දින පරාසය", "label.default-date-range": "පෙරනිමි දින පරාසය", "label.delete": "මකන්න", - "label.delete-account": "ගිණුම මකන්න", + "label.delete-team": "Delete team", + "label.delete-user": "Delete user", "label.delete-website": "වෙබ් අඩවිය මකන්න", + "label.desktop": "Desktop", + "label.details": "Details", + "label.devices": "Devices", "label.dismiss": "මගහරින්න", "label.domain": "වසම", "label.edit": "සංස්කරණය කරන්න", - "label.edit-account": "ගිණුම සංස්කරණය කරන්න", - "label.edit-website": "වෙබ් අඩවිය සංස්කරණය කරන්න", + "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "බෙදාගැනීමේ URL සබල කරන්න", "label.event-data": "සිදුවීම් දත්ත", - "label.field-name": "ක්ෂේත්‍ර නාම", - "label.invalid": "වලංගු නැත", - "label.invalid-domain": "වලංගු නොවන වසමක්", + "label.events": "Events", + "label.field": "Field", + "label.fields": "Fields", + "label.filter-combined": "Combined", + "label.filter-raw": "Raw", + "label.funnel": "Funnel", + "label.join": "Join", + "label.join-team": "Join team", "label.language": "භාෂාව", + "label.languages": "Languages", + "label.laptop": "Laptop", "label.last-days": "අන්තිම {x} දින", "label.last-hours": "අන්තිම {x} පැය", - "label.logged-in-as": "ලොග් වී ඇත්තේ {username}", + "label.leave": "Leave", + "label.leave-team": "Leave team", "label.login": "ලොග් වෙන්න", "label.logout": "පිටවීම", + "label.members": "Members", + "label.mobile": "Mobile", "label.more": "තවත්", "label.name": "නම", "label.new-password": "අලුත් මුරපදය", "label.none": "කිසිවක් නැත", + "label.operating-systems": "Operating systems", "label.owner": "හිමිකරු", + "label.page-views": "Page views", + "label.pages": "Pages", "label.password": "මුරපදය", - "label.passwords-dont-match": "මුරපද නොගැලපේ", + "label.powered-by": "Powered by {name}", "label.profile": "පැතිකඩ", + "label.queries": "Queries", + "label.query": "Query", + "label.query-parameters": "Query parameters", "label.realtime": "තත්ය කාල", - "label.realtime-logs": "තත්‍ය කාලීන ලොග්", + "label.referrers": "Referrers", "label.refresh": "නැවුම් කරන්න", + "label.regenerate": "Regenerate", + "label.regions": "Regions", + "label.remove": "Remove", + "label.reports": "Reports", "label.required": "අවශ්‍යයි", "label.reset": "යළි පිහිටුවන්න", "label.reset-website": "සංඛ්යා ලේඛන නැවත සකසන්න", + "label.role": "Role", + "label.run-query": "Run query", "label.save": "සුරකින්න", - "label.search": "සෙවීම", + "label.screens": "Screens", + "label.select-date": "Select date", + "label.select-website": "Select website", + "label.sessions": "Sessions", "label.settings": "සැකසුම්", "label.share-url": "බෙදාගැනීමේ URL", "label.single-day": "තනි දවස", + "label.tablet": "Tablet", + "label.team": "Team", + "label.team-guest": "Team guest", + "label.team-id": "Team ID", + "label.team-member": "Team member", + "label.team-owner": "Team owner", + "label.teams": "Teams", "label.theme": "තේමාව", "label.this-month": "මෙ මාසය", "label.this-week": "මේ සතිය", "label.this-year": "මේ අවුරුද්ද", "label.timezone": "වේලා කලාපය", + "label.title": "Title", "label.today": "අද", + "label.toggle-charts": "Toggle charts", "label.tracking-code": "ලුහුබැඳීමේ කේතය", - "label.type": "වර්ගය", + "label.unique-visitors": "Unique visitors", "label.unknown": "නොදනී", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "User", "label.username": "පරිශීලක නාමය", - "label.value": "වටිනාකම", + "label.users": "Users", + "label.view": "View", "label.view-details": "තොරතුරු පෙන්වන්න", + "label.view-only": "View only", + "label.views": "Views", + "label.visitors": "Visitors", + "label.website": "Website", + "label.website-id": "Website ID", "label.websites": "වෙබ් අඩවි", + "label.window": "Window", "label.yesterday": "ඊයේ", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} දැන් {x, plural, one {අමුත්තා} other {අමුත්තන්}}", "message.confirm-delete": "{target} මකා දැමීම ගැන විශ්වාසද?", + "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "{target} ට අදාල සංඛ්‍යාලේඛන නැවත පිහිටුවීමට අවශ්‍යද?", - "message.copied": "පිටපත් කරගත්තා!", - "message.delete-warning": "සියලුම ආශ්‍රිත දත්ත ද මකා දැමෙනු ඇත.", - "message.edit-dashboard": "උපකරණ පුවරුව සංස්කරණය කරන්න", - "message.failure": "යම් ගැටලුවක් මතු වී ඇත.", - "message.get-share-url": "බෙදාගැනීමේ URL ලබා ගන්න", - "message.get-tracking-code": "ලුහුබැඳීමේ කේතය ලබා ගන්න", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", + "message.delete-website-warning": "All website data will be deleted.", + "message.error": "Something went wrong.", + "message.event-log": "{event} on {url}", "message.go-to-settings": "සැකසීම් වෙත යන්න", "message.incorrect-username-password": "වැරදි පරිශීලක නාමය/මුරපදය.", - "message.log.visitor": "{country} වලින් පැමිණි අමුත්තකු {device} එකේ, මේ {os} එකේ, මේ {browser} එකෙන් ඉන්නවා", - "message.new-version-available": "umami අලුත්ම {version} වන අනුවාදය නිකුත් උනා!", + "message.invalid-domain": "Invalid domain. Do not include http/https.", + "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "පෙන්වීමට දත්ත නොමැත.", - "message.no-websites-configured": "ඔබට වින්‍යාස කර ඇති වෙබ් අඩවි කිසිවක් නොමැත.", + "message.no-event-data": "No event data is available.", + "message.no-match-password": "Passwords do not match.", + "message.no-teams": "You have not created any teams.", + "message.no-users": "There are no users.", "message.page-not-found": "පිටුව හමු නොවීය.", - "message.powered-by": "බල ගැන්වුයේ {name}", - "message.reset-warning": "සියලුම සංඛ්‍යාලේඛන මකා දමනු ඇත. නමුත් ඔබගේ නිරීක්ෂණ කේතය නොවෙනස්ව පවතිනු ඇත.", - "message.save-success": "සාර්තකව සුරැකිණි.", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", + "message.reset-website-warning": "All statistics for this website will be deleted, but your settings will remain intact.", + "message.saved": "Saved.", "message.share-url": "මේ {target} සඳහා ප්‍රසිද්ධියේ බෙදාගත් URL එකයි.", - "message.toggle-charts": "ප්‍රස්ථාර ටොගල් කරන්න", - "message.track-stats": "{target} හි සංඛ්යාලේඛන බැලීම සදහා, පහත කේතය {head} කොටසට ඇතුලත් කරන්න.", - "message.type-delete": "සත්‍යාපනය සදහා {delete} ලෙස පහල කොටුවේ ටයිප් කරන්න", - "message.type-reset": "සත්‍යාපනය සදහා {reset} ලෙස පහල කොටුවේ ටයිප් කරන්න", - "metrics.actions": "ක්රියාවන්", - "metrics.average-visit-time": "සාමාන්‍ය සංචාර කාලය", - "metrics.bounce-rate": "හැරී යන ප්‍රමාණය", - "metrics.browsers": "බ්‍රව්සර්", - "metrics.countries": "රටවල්", - "metrics.device.desktop": "ඩෙස්ක්ටොප්", - "metrics.device.laptop": "ලැප්ටොප්", - "metrics.device.mobile": "ජංගම", - "metrics.device.tablet": "ටැබ්ලට්", - "metrics.devices": "උපකරණ", - "metrics.events": "සිද්ධීන්", - "metrics.filter.combined": "ඒකාබද්ධ", - "metrics.filter.raw": "අමු", - "metrics.languages": "භාෂා", - "metrics.operating-systems": "මෙහෙයුම් පද්ධති", - "metrics.page-views": "පිටු බැලීම්", - "metrics.pages": "පිටු", - "metrics.query-parameters": "විමසුම් පරාමිතීන්", - "metrics.referrers": "යොමු කරන්නන්", - "metrics.screens": "තිර", - "metrics.unique-visitors": "අලුත්ම අමුත්තන්", - "metrics.views": "බැලූ ගණන", - "metrics.visitors": "අමුත්තන්" + "message.team-already-member": "You are already a member of the team.", + "message.team-not-found": "Team not found.", + "message.tracking-code": "To track stats for this website, place the following code in the ... section of your HTML.", + "message.user-deleted": "User deleted.", + "message.visitor-log": "Visitor from {country} using {browser} on {os} {device}", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "This team does not have any websites.", + "messages.no-websites-configured": "You do not have any websites configured.", + "messages.team-websites-info": "Websites can be viewed by anyone on the team." } diff --git a/lang/sk-SK.json b/lang/sk-SK.json index 2d05cd2efa..4c09565f88 100644 --- a/lang/sk-SK.json +++ b/lang/sk-SK.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Akcie", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Pridať web", "label.admin": "Administrátor", "label.all": "Všetko", @@ -40,9 +42,13 @@ "label.edit": "Upraviť", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Povoliť zdielanie URL", + "label.event-data": "Event data", "label.events": "Udalosti", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombinácie", "label.filter-raw": "Nezpracované", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Powered by {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Aktuálne", "label.referrers": "Odkazy", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Povinné", "label.reset": "Reset", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Uložiť", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Nastavenia", @@ -104,21 +114,54 @@ "label.tracking-code": "Sledovací kód", "label.unique-visitors": "Jedinečné návštevy", "label.unknown": "Neznámý", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Užívateľské meno", "label.users": "Users", "label.view": "View", "label.view-details": "Zobraziť detaily", + "label.view-only": "View only", "label.views": "Zobrazení", "label.visitors": "Návštevy", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Weby", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} aktuálne {x, plural, one {návštevník} other {návštěvníci}}", "message.confirm-delete": "Naozaj zmazať {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Zmazať web", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Všetky príbuzné data budu tiež zmazané.", "message.error": "Niečo sa pokazilo.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Neplatná doména", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Žiadne data.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Hesla se nezhodujú", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Stránka sa nenašla.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Úspešne uložené.", "message.share-url": "Toto je zdielané URL pre {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Sledovací kód", "message.user-deleted": "User deleted.", "message.visitor-log": "Návštevník z {country} s prehliadačom {browser} na {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Nemáte nastavený žiadny web.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/sl-SI.json b/lang/sl-SI.json index 459e5e7f24..c991fb7550 100644 --- a/lang/sl-SI.json +++ b/lang/sl-SI.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Dejanja", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Dodaj spletno mesto", "label.admin": "Administrator", "label.all": "Vse", @@ -40,9 +42,13 @@ "label.edit": "Uredi", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Omogoči URL za skupno rabo", + "label.event-data": "Event data", "label.events": "Dogodki", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Skupno", "label.filter-raw": "Neobdelane meritve", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Zagotavlja {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "V realnem času", "label.referrers": "Viri", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Zahtevano", "label.reset": "Ponastavi", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Shrani", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Nastavitve", @@ -104,21 +114,54 @@ "label.tracking-code": "Koda za sledenje", "label.unique-visitors": "Unikatni obiskovalci", "label.unknown": "Neznano", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Uporabniško ime", "label.users": "Users", "label.view": "View", "label.view-details": "Prikaži podrobnosti", + "label.view-only": "View only", "label.views": "Ogledi", "label.visitors": "Obiskovalci", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Spletna mesta", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} trenutni {x, plural, one {obiskovalec} other {obiskovalcev}}", "message.confirm-delete": "Ste prepričani, da želite izbrisati {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Izbriši spletno mesto", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Izbrisani bodo tudi vsi povezani podatki.", "message.error": "Prišlo je do napake.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Neveljavna domena", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Podatki niso na voljo.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Gesli se ne ujemata", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Stran ni bila najdena.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Uspešno shranjeno.", "message.share-url": "To je javno dostopen naslov URL za {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Koda za sledenje", "message.user-deleted": "User deleted.", "message.visitor-log": "Obiskovalec iz {country} uporablja {browser} na {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Ni nastavljenih spletnih mest.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/sv-SE.json b/lang/sv-SE.json index 16a8d62ab8..07d1a7fe94 100644 --- a/lang/sv-SE.json +++ b/lang/sv-SE.json @@ -1,27 +1,29 @@ { "label.access-code": "Access code", "label.actions": "Händelser", - "label.activity-log": "Activity log", + "label.activity-log": "Aktivitetslogg", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Lägg till webbsajt", "label.admin": "Administratör", "label.all": "Alla", "label.all-time": "Sedan början", - "label.analytics": "Analytics", + "label.analytics": "Analys", "label.average-visit-time": "Medelbesökstid", "label.back": "Tillbaka", "label.bounce-rate": "Avvisningfrekvens", "label.browsers": "Webbläsare", "label.cancel": "Avbryt", "label.change-password": "Byt lösenord", - "label.cities": "Cities", - "label.clear-all": "Clear all", - "label.confirm": "Confirm", + "label.cities": "Städer", + "label.clear-all": "Rensa alla", + "label.confirm": "Bekräfta", "label.confirm-password": "Bekräfta lösenord", - "label.continue": "Continue", + "label.continue": "Fortsätt", "label.countries": "Länder", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", + "label.create-team": "Skapa team", + "label.create-user": "Skapa användare", + "label.created": "Skapad", "label.current-password": "Nuvarande lösenord", "label.custom-range": "Anpassat urval", "label.dashboard": "Översikt", @@ -29,37 +31,41 @@ "label.date-range": "Datumomfång", "label.default-date-range": "Standard datum-urval", "label.delete": "Radera", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-team": "Radera team", + "label.delete-user": "Radera användare", "label.delete-website": "Radera webbsajt", "label.desktop": "Stationär", - "label.details": "Details", + "label.details": "Detailjer", "label.devices": "Enheter", "label.dismiss": "Avbryt", "label.domain": "Domän", "label.edit": "Redigera", - "label.edit-dashboard": "Edit dashboard", + "label.edit-dashboard": "Redigera översikt", "label.enable-share-url": "Aktivera delnings-URL", + "label.event-data": "Event data", "label.events": "Händelser", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kombinerade", "label.filter-raw": "Rådata", - "label.join": "Join", - "label.join-team": "Join team", + "label.funnel": "Funnel", + "label.join": "Gå med", + "label.join-team": "gå med i team", "label.language": "Språk", "label.languages": "Språk", "label.laptop": "Bärbar", "label.last-days": "Senaste {x} dagarna", "label.last-hours": "Senaste {x} timmarna", - "label.leave": "Leave", - "label.leave-team": "Leave team", + "label.leave": "Lämna", + "label.leave-team": "Lämna team", "label.login": "Logga in", "label.logout": "Logga ut", - "label.members": "Members", + "label.members": "Medlemmar", "label.mobile": "Mobil", "label.more": "Mer", "label.name": "Namn", "label.new-password": "Nytt lösenord", - "label.none": "None", + "label.none": "Inga", "label.operating-systems": "Operativsystem", "label.owner": "Ägare", "label.page-views": "Sidvisningar", @@ -67,80 +73,119 @@ "label.password": "Lösenord", "label.powered-by": "Drivs av {name}", "label.profile": "Profil", - "label.queries": "Queries", - "label.query-parameters": "Query parameters", + "label.queries": "Frågor", + "label.query": "Frågor", + "label.query-parameters": "Fråge-parametrar", "label.realtime": "Realtid", "label.referrers": "Hänvisare", "label.refresh": "Uppdatera", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "Regenerera", + "label.regions": "Regioner", + "label.remove": "Ta bort", + "label.reports": "Reports", "label.required": "Krävs", "label.reset": "Återställ", "label.reset-website": "Återställ statistik", - "label.role": "Role", + "label.role": "Roll", + "label.run-query": "Run query", "label.save": "Spara", - "label.screens": "Screens", - "label.select-website": "Select website", + "label.screens": "Upplösning", + "label.select-date": "Select date", + "label.select-website": "Välj webbsajt", "label.sessions": "Sessions", "label.settings": "Inställningar", "label.share-url": "Delnings-URL", "label.single-day": "En dag", "label.tablet": "Platta", "label.team": "Team", - "label.team-guest": "Team guest", + "label.team-guest": "Team-gäst", "label.team-id": "Team ID", - "label.team-member": "Team member", - "label.team-owner": "Team owner", - "label.teams": "Teams", + "label.team-member": "Team-medlem", + "label.team-owner": "Team-ägare", + "label.teams": "Team", "label.theme": "Tema", "label.this-month": "Denna månad", "label.this-week": "Denna vecka", "label.this-year": "Detta år", "label.timezone": "Tidszon", - "label.title": "Title", + "label.title": "Titel", "label.today": "Idag", "label.toggle-charts": "Visa/göm grafer", "label.tracking-code": "Spårningskod", "label.unique-visitors": "Unika besökare", "label.unknown": "Okänd", - "label.user": "User", + "label.url": "URL", + "label.urls": "URLs", + "label.user": "Användare", "label.username": "Användarnamn", "label.users": "Users", - "label.view": "View", + "label.view": "Visa", "label.view-details": "Visa detaljer", + "label.view-only": "View only", "label.views": "Visningar", "label.visitors": "Besökare", - "label.website-id": "Website ID", + "label.website": "Website", + "label.website-id": "Webbsajt-ID", "label.websites": "Webbsajt", - "label.yesterday": "Yesterday", + "label.window": "Window", + "label.yesterday": "Igår", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} {x, plural, one {besökare} other {besökare}} just nu", "message.confirm-delete": "Är du säker på att du vill radera {target}?", - "message.confirm-leave": "Are you sure you want to leave {target}?", + "message.confirm-leave": "Är du säker på att du vill lämna {target}?", "message.confirm-reset": "Är du säker på att du vill återställa statistiken för {target}?", - "message.delete-website": "Radera webbsajt", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "All tillhörande data kommer också raderas.", "message.error": "Något gick fel.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} på {url}", "message.go-to-settings": "Gå till inställningar", "message.incorrect-username-password": "Felaktigt användarnamn/lösenord.", "message.invalid-domain": "Ogiltig domän", - "message.min-password-length": "Minimum length of {n} characters", + "message.min-password-length": "Minst {n} tecken", "message.no-data-available": "Ingen data tillgänglig.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Lösenorden är inte samma", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "message.no-teams": "Du har inte skapat några team.", + "message.no-users": "Det finns inga användare.", "message.page-not-found": "Sidan kan inte hittas.", - "message.reset-website": "Återställ statistik", + "message.reset-website": "För att återställa statistiken skriv {confirmation} i rutan nedan.", "message.reset-website-warning": "All statistik för webbsajten tas bort men spårningskoden förblir oförändrad.", "message.saved": "Sparades!", "message.share-url": "Det här är den offentliga delnings-URL:en för {target}.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", + "message.team-already-member": "Du är redan medlem i teamet.", + "message.team-not-found": "Team kan inte hittas.", "message.tracking-code": "Spårningskod", - "message.user-deleted": "User deleted.", + "message.user-deleted": "Användare raderad.", "message.visitor-log": "Besökare från {country} med {browser} på {os} {device}", - "messages.no-team-websites": "This team does not have any websites.", + "messages.no-results-found": "No results were found.", + "messages.no-team-websites": "Det här teamet har inga webbsajter.", "messages.no-websites-configured": "Du har inga webbsajter.", - "messages.team-websites-info": "Websites can be viewed by anyone on the team." + "messages.team-websites-info": "Websajter kan ses av alla i teamet." } diff --git a/lang/ta-IN.json b/lang/ta-IN.json index e4fc9f7e04..99cc1c265b 100644 --- a/lang/ta-IN.json +++ b/lang/ta-IN.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "செயல்கள்", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "வலைத்தளத்தைச் சேர்க்க", "label.admin": "நிர்வாகியைச் சேர்க்க", "label.all": "எல்லாம்", @@ -40,9 +42,13 @@ "label.edit": "திருத்துதல்", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "கள முகவரியை பகிரலாம்", + "label.event-data": "Event data", "label.events": "நிகழ்வுகள்", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "ஒருங்கிணைந்த", "label.filter-raw": "மூல", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "{name} ஆல் இயக்கப்படுகிறது", "label.profile": "சுயவிவரம்", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "தற்போதைய", "label.referrers": "குறிப்பிடுவோர்", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "தேவையானவை", "label.reset": "மீட்டமை", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "சேமி", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "அமைப்புகள்", @@ -104,21 +114,54 @@ "label.tracking-code": "கண்காணிப்பு குறியீடு", "label.unique-visitors": "தனிப்பட்ட பார்வையாளர்கள்", "label.unknown": "தெரியாத", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "பயனர்பெயர்", "label.users": "Users", "label.view": "View", "label.view-details": "விபரங்களை பார்", + "label.view-only": "View only", "label.views": "பார்வைகள்", "label.visitors": "பார்வையாளர்கள்", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "வலைத்தளங்கள்", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} தற்போதைய {x, plural, one {ஒன்று} other {மற்ற}}", "message.confirm-delete": "நீங்கள் நிச்சயமாக {target} நீக்க விரும்புகிறீர்களா?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "வலைத்தளத்தை நீக்கு", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "தொடர்புடைய எல்லா தரவும் நீக்கப்படும்.", "message.error": "ஏதோ தவறு நடந்துவிட்டது.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "தவறான கள முகவரி", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "தரவு எதுவும் கிடைக்கவில்லை.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "இருக்கடவுச்சொல் பொருந்தவில்லை", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "பக்கம் கிடைக்கவில்லை.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "வெற்றிகரமாக சேமிக்கப்பட்டது.", "message.share-url": "{target} இது பொதுவில் பகிரும் வலைத்தள முகவரி.", @@ -140,6 +184,7 @@ "message.tracking-code": "கண்காணிப்பு குறியீடு", "message.user-deleted": "User deleted.", "message.visitor-log": "{country}வில் இருந்து பார்வையாளர் {browser} ஐ {os} {device}லில் பயன்படுத்துகிறார்", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "உங்களிடம் எந்த வலைத்தளங்களும் கட்டமைக்கப்படவில்லை.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/th-TH.json b/lang/th-TH.json index d71f463352..e0111a8452 100644 --- a/lang/th-TH.json +++ b/lang/th-TH.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "การกระทำ", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "เพิ่มเว็บไซต์", "label.admin": "ผู้ดูแลระบบ", "label.all": "ทั้งหมด", @@ -40,9 +42,13 @@ "label.edit": "แก้ไข", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "เปิดใช้งานการแชร์ลิงก์", + "label.event-data": "Event data", "label.events": "เหตุการณ์", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "ข้อมูลรวม", "label.filter-raw": "ข้อมูลดิบ", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "ภาษา", @@ -68,6 +74,7 @@ "label.powered-by": "ขับเคลื่อนโดย {name}", "label.profile": "โปรไฟล์", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "เรียลไทม์", "label.referrers": "แหล่งที่มา", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "ต้องการ", "label.reset": "รีเซต", "label.reset-website": "รีเซตข้อมูลสถิติ", "label.role": "Role", + "label.run-query": "Run query", "label.save": "บันทึก", "label.screens": "ขนาดหน้าจอ", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "ตั้งค่า", @@ -104,21 +114,54 @@ "label.tracking-code": "โค้ดสำหรับใช้ติดตาม", "label.unique-visitors": "ผู้เข้าชม", "label.unknown": "ไม่รู้จัก", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "ชื่อผู้ใช้", "label.users": "Users", "label.view": "View", "label.view-details": "แสดงรายละเอียด", + "label.view-only": "View only", "label.views": "การเข้าชม", "label.visitors": "ผู้เข้าชม", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "เว็บไซต์", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "มีผู้ใช้งาน {x} {x, plural, one {คนในขณะนี้} other {คนในขณะนี้}}", "message.confirm-delete": "คุณแน่ใจหรือไม่ว่าต้องการลบ {target} ?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "คุณแน่ใจหรือไม่ว่าต้องการรีเซตข้อมูลสถิติของ {target} ?", - "message.delete-website": "ลบเว็บไซต์", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "ข้อมูลที่เกี่ยวข้องทั้งหมดจะถูกลบ.", "message.error": "เกิดข้อผิดพลาด.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "โดเมนไม่ถูกต้อง", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "ไม่มีข้อมูล.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "รหัสผ่านไม่ตรงกัน", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "ไม่พบหน้านี้.", - "message.reset-website": "รีเซตข้อมูลสถิติ", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "สถิติทั้งหมดสำหรับเว็บไซต์นี้จะถูกลบออก แต่โค้ดสำหรับใช้ติดตามของคุณจะยังคงอยู่เหมือนเดิม.", "message.saved": "บันทึกข้อมูลเรียบร้อย.", "message.share-url": "นี่คือลิงก์ที่แชร์แบบสาธารณะสำหรับ {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "โค้ดสำหรับใช้ติดตาม", "message.user-deleted": "User deleted.", "message.visitor-log": "ผู้เข้าชมจาก {country} กำลังใช้งานผ่าน {browser} บน {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "คุณยังไม่ได้ตั้งค่าเว็บไซต์ใด ๆ ไว้.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/tr-TR.json b/lang/tr-TR.json index 975563492b..686489ea8e 100644 --- a/lang/tr-TR.json +++ b/lang/tr-TR.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Hareketler", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Web sitesi ekle", "label.admin": "Yönetici", "label.all": "Tümü", @@ -40,9 +42,13 @@ "label.edit": "Düzenle", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Anonim paylaşım URL'i aktif", + "label.event-data": "Event data", "label.events": "Olaylar", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Birleşik", "label.filter-raw": "Ham", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Sağlayıcı: {name}", "label.profile": "Profil", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Gerçek Zamanlı", "label.referrers": "Yönlendirenler", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Zorunlu alan", "label.reset": "Sıfırla", "label.reset-website": "Reset statistics", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Kaydet", "label.screens": "Ekranlar", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Ayarlar", @@ -104,21 +114,54 @@ "label.tracking-code": "İzleme kodu", "label.unique-visitors": "Tekil kullanıcı", "label.unknown": "Bilinmeyen", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Kullanıcı adı", "label.users": "Users", "label.view": "View", "label.view-details": "Detayı incele", + "label.view-only": "View only", "label.views": "Görüntüleme", "label.visitors": "Ziyaretçi", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Web siteleri", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} aktif ziyaretçi", "message.confirm-delete": "{target} kaydını silmek istediğinizden emin misiniz?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are your sure you want to reset {target}'s statistics?", - "message.delete-website": "Web sitesini sil", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "İlişkili tüm veriler de silinecektir.", "message.error": "Bir şeyler ters gitti!", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Geçersiz alan adı", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Henüz hiç veri yok.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Parolalar uyuşmuyor", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Sayfa bulunamadı.", - "message.reset-website": "Reset statistics", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.", "message.saved": "Başarıyla kaydedildi.", "message.share-url": "{target} için kullanılabilir anonim paylaşım adresidir.", @@ -140,6 +184,7 @@ "message.tracking-code": "İzleme kodu", "message.user-deleted": "User deleted.", "message.visitor-log": "Yeni ziyaretçi: {country}, {os}, {device}, {browser}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Henüz hiç web sitesi tanımlamadınız", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/uk-UA.json b/lang/uk-UA.json index 413f2dadde..d116c7ef57 100644 --- a/lang/uk-UA.json +++ b/lang/uk-UA.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Дії", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Додати сайт", "label.admin": "Адміністратор", "label.all": "Всі", @@ -40,9 +42,13 @@ "label.edit": "Редагувати", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Дозволити ділитися посиланням", + "label.event-data": "Event data", "label.events": "Події", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Об'єднані", "label.filter-raw": "Сирі дані", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "На базі {name}", "label.profile": "Профіль", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "У реальному часі", "label.referrers": "Джерела", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Обов'язкове", "label.reset": "Скинути", "label.reset-website": "Скинути статистику сайту", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Зберегти", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Налаштування", @@ -104,21 +114,54 @@ "label.tracking-code": "Код для відслідковування", "label.unique-visitors": "Унікальні відвідувачі", "label.unknown": "Невідомо", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Ім'я користувача", "label.users": "Users", "label.view": "View", "label.view-details": "Переглянути деталі", + "label.view-only": "View only", "label.views": "Перегляди", "label.visitors": "Відвідувачі", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Сайти", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} поточних відвідувачів", "message.confirm-delete": "Ви впевнені, що бажаєте видалити {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Ви впевнені, що бажаєте скинути статистику для {target}?", - "message.delete-website": "Видалити сайт", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Усі пов'язані дані будуть видалені також.", "message.error": "Щось пішло не так.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Некоректний домен", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Немає даних.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Паролі не співпадають", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Сторінку не знайдено.", - "message.reset-website": "Скинути статистику сайту", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Вся статистика для цього сайту буде видалена, проте код відслідковування буде продовжувати працювати.", "message.saved": "Збережено успішно.", "message.share-url": "Це публічне посилання для {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Код для відслідковування", "message.user-deleted": "User deleted.", "message.visitor-log": "Відвідувач з {country} використовуючи {browser} на {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "У вас немає налаштованих сайтів.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/ur-PK.json b/lang/ur-PK.json index d206de6445..e2f95fb342 100644 --- a/lang/ur-PK.json +++ b/lang/ur-PK.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "اعمال", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "ویب سائٹ کا اضافہ کریں", "label.admin": "منتظم", "label.all": "تمام", @@ -40,9 +42,13 @@ "label.edit": "ترمیم", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "شیئر یو آر ایل کو فعال کریں", + "label.event-data": "Event data", "label.events": "واقعات", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "مشترکہ", "label.filter-raw": "خام", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "تقویت یافتہ بذریعہ {name}", "label.profile": "پروفائل", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "براہ راست", "label.referrers": "بھیجنے والے", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "درکار ہے", "label.reset": "دوبارہ ترتیب دیں", "label.reset-website": "اعدادوشمار کو دوبارہ ترتیب دیں", "label.role": "Role", + "label.run-query": "Run query", "label.save": "محفوظ کریں", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "ترتیبات", @@ -104,21 +114,54 @@ "label.tracking-code": "ٹریکنگ کوڈ", "label.unique-visitors": "منفرد زائرین", "label.unknown": "نامعلوم", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "صارف نام", "label.users": "Users", "label.view": "View", "label.view-details": "تفصیلات دیکھیں", + "label.view-only": "View only", "label.views": "مناظر", "label.visitors": "زائرین", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "ویب سائٹس", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} موجودہ {x, plural, one {زائر} other {زائرین}}", "message.confirm-delete": "کیا آپ واقعی {target} کو حذف کرنا چاہتے ہیں؟", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "کیا آپ واقعی {target} کے اعدادوشمار کو دوبارہ ترتیب دینا چاہتے ہیں؟", - "message.delete-website": "ویب سائٹ مٹایں", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "تمام متعلقہ ڈیٹا بھی حذف کر دیا جائے گا۔", "message.error": "کچھ غلط ہو گیا.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "غلط ڈومین", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "مواد موجود نہیں ہے.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "پاس ورڈز مماثل نہیں ہیں", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "صفحہ نہیں ملا.", - "message.reset-website": "اعدادوشمار کو دوبارہ ترتیب دیں", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "اس ویب سائٹ کے تمام اعدادوشمار کو حذف کر دیا جائے گا، لیکن آپ کا ٹریکنگ کوڈ برقرار رہے گا۔", "message.saved": "کامیابی سے محفوظ ہو گیا۔", "message.share-url": "یہ {target} کے لیے عوامی طور پر اشتراک کردہ URL ہے۔", @@ -140,6 +184,7 @@ "message.tracking-code": "ٹریکنگ کوڈ", "message.user-deleted": "User deleted.", "message.visitor-log": "{os} {device} پر {browser} کا استعمال کرتے ہوئے {country} سے آنے والا", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "آپ کے پاس کوئی ویب سائٹ کنفیگر نہیں ہے۔", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/vi-VN.json b/lang/vi-VN.json index fe27ce4d23..ff89a6016a 100644 --- a/lang/vi-VN.json +++ b/lang/vi-VN.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "Hành động", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "Thêm website", "label.admin": "Quản trị", "label.all": "Tất cả", @@ -40,9 +42,13 @@ "label.edit": "Chỉnh sửa", "label.edit-dashboard": "Edit dashboard", "label.enable-share-url": "Bật khả năng chia sẻ URL", + "label.event-data": "Event data", "label.events": "Sự kiện", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "Kết hợp", "label.filter-raw": "Gốc", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "Language", @@ -68,6 +74,7 @@ "label.powered-by": "Bản quyền thuộc về {name}", "label.profile": "Hồ sơ", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "Query parameters", "label.realtime": "Thời gian thực", "label.referrers": "Liên kết giới thiệu", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "Yêu cầu", "label.reset": "Tái thiết lập", "label.reset-website": "Tái thiết lập thống kê", "label.role": "Role", + "label.run-query": "Run query", "label.save": "Lưu", "label.screens": "Screens", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "Cài đặt", @@ -104,21 +114,54 @@ "label.tracking-code": "Mã theo dõi", "label.unique-visitors": "Khách truy cập một lần", "label.unknown": "Không rõ", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "Tên đăng nhập", "label.users": "Users", "label.view": "View", "label.view-details": "Xem chi tiết", + "label.view-only": "View only", "label.views": "Xem", "label.visitors": "Khách", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Websites", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "{x} hiện tại {x, plural, one {một} other {trên}}", "message.confirm-delete": "Bạn có chắc chắn muốn xoá {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Bạn có chắc chắn muốn tái thiết lập thống kê {target}?", - "message.delete-website": "Xóa website", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "Tất cả các dữ liệu liên quan cũng sẽ bị xoá.", "message.error": "Đã xảy ra lỗi.", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "Tên miền không hợp lệ", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "Không có dữ liệu.", + "message.no-event-data": "No event data is available.", "message.no-match-password": "Mật khẩu không đồng nhất", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "Trang không tìm thấy.", - "message.reset-website": "Tái thiết lập thống kê", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "Tất cả số liệu thống kê của website này sẽ bị xoá, nhưng mã theo dõi sẽ vẫn giữ nguyên.", "message.saved": "Đã lưu thành công.", "message.share-url": "Đây là đường dẫn URL cho {target}.", @@ -140,6 +184,7 @@ "message.tracking-code": "Mã theo dõi", "message.user-deleted": "User deleted.", "message.visitor-log": "Khách từ {country} đang dùng {browser} trên {os} {device}", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "Bạn chưa có bất cứ website nào.", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lang/zh-CN.json b/lang/zh-CN.json index 1aba3988fb..33eb5d856c 100644 --- a/lang/zh-CN.json +++ b/lang/zh-CN.json @@ -2,6 +2,8 @@ "label.access-code": "访问代码", "label.actions": "用户行为", "label.activity-log": "活动日志", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "添加网站", "label.admin": "管理员", "label.all": "所有", @@ -40,9 +42,13 @@ "label.edit": "编辑", "label.edit-dashboard": "编辑仪表板", "label.enable-share-url": "启用共享链接", + "label.event-data": "Event data", "label.events": "行为类别", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "合并", "label.filter-raw": "原始", + "label.funnel": "Funnel", "label.join": "加入", "label.join-team": "加入团队", "label.language": "语言", @@ -68,6 +74,7 @@ "label.powered-by": "由 {name} 提供支持", "label.profile": "个人资料", "label.queries": "查询", + "label.query": "Query", "label.query-parameters": "查询参数", "label.realtime": "实时", "label.referrers": "来源域名", @@ -75,12 +82,15 @@ "label.regenerate": "重新生成", "label.regions": "州/省", "label.remove": "移除", + "label.reports": "Reports", "label.required": "必填", "label.reset": "重置", "label.reset-website": "重置统计数据", "label.role": "角色", + "label.run-query": "Run query", "label.save": "保存", "label.screens": "屏幕尺寸", + "label.select-date": "Select date", "label.select-website": "选择网站", "label.sessions": "会话", "label.settings": "设置", @@ -104,21 +114,54 @@ "label.tracking-code": "跟踪代码", "label.unique-visitors": "独立访客", "label.unknown": "未知", + "label.url": "URL", + "label.urls": "URLs", "label.user": "用户", "label.username": "用户名", "label.users": "用户", "label.view": "查看", "label.view-details": "查看更多", + "label.view-only": "View only", "label.views": "浏览量", "label.visitors": "访客", + "label.website": "Website", "label.website-id": "网站 ID", "label.websites": "网站", + "label.window": "Window", "label.yesterday": "昨天", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "当前在线 {x} 人", "message.confirm-delete": "你确定要删除 {target} 吗?", "message.confirm-leave": "你确定要离开 {target} 吗?", "message.confirm-reset": "您确定要重置 {target} 的数据吗?", - "message.delete-website": "删除网站", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "所有相关数据将会被删除。", "message.error": "出现错误。", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "无效域名", "message.min-password-length": "密码最短长度为 {n} 个字符", "message.no-data-available": "无可用数据。", + "message.no-event-data": "No event data is available.", "message.no-match-password": "密码不一致", "message.no-teams": "你还没有创建任何团队。", "message.no-users": "没有任何用户。", "message.page-not-found": "网页未找到。", - "message.reset-website": "重置统计数据", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "本网站的所有统计数据将被删除,但您的跟踪代码将保持不变。", "message.saved": "保存成功。", "message.share-url": "这是 {target} 的共享链接。", @@ -140,6 +184,7 @@ "message.tracking-code": "跟踪代码", "message.user-deleted": "User detected.", "message.visitor-log": "来自{country}的访客在搭载 {os} 的{device}上使用 {browser} 浏览器进行访问。", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "这个团队没有任何网站。", "messages.no-websites-configured": "你还没有设置任何网站。", "messages.team-websites-info": "团队中的任何人都可查看网站。" diff --git a/lang/zh-TW.json b/lang/zh-TW.json index 4237005946..98dbde23fd 100644 --- a/lang/zh-TW.json +++ b/lang/zh-TW.json @@ -2,6 +2,8 @@ "label.access-code": "Access code", "label.actions": "用戶行為", "label.activity-log": "Activity log", + "label.add": "Add", + "label.add-description": "Add description", "label.add-website": "增加網站", "label.admin": "管理員", "label.all": "所有", @@ -40,9 +42,13 @@ "label.edit": "編輯", "label.edit-dashboard": "編輯管理面板", "label.enable-share-url": "啟用分享連結", + "label.event-data": "Event data", "label.events": "行為類別", + "label.field": "Field", + "label.fields": "Fields", "label.filter-combined": "總和", "label.filter-raw": "原始", + "label.funnel": "Funnel", "label.join": "Join", "label.join-team": "Join team", "label.language": "語言", @@ -68,6 +74,7 @@ "label.powered-by": "運行 {name}", "label.profile": "個人資料", "label.queries": "Queries", + "label.query": "Query", "label.query-parameters": "查詢參數", "label.realtime": "實時", "label.referrers": "指入域名", @@ -75,12 +82,15 @@ "label.regenerate": "Regenerate", "label.regions": "Regions", "label.remove": "Remove", + "label.reports": "Reports", "label.required": "必填", "label.reset": "重置", "label.reset-website": "重置統計數據", "label.role": "Role", + "label.run-query": "Run query", "label.save": "保存", "label.screens": "屏幕尺寸", + "label.select-date": "Select date", "label.select-website": "Select website", "label.sessions": "Sessions", "label.settings": "設置", @@ -104,21 +114,54 @@ "label.tracking-code": "追蹤代碼", "label.unique-visitors": "獨立訪客", "label.unknown": "未知", + "label.url": "URL", + "label.urls": "URLs", "label.user": "User", "label.username": "用户名", "label.users": "Users", "label.view": "View", "label.view-details": "查看更多", + "label.view-only": "View only", "label.views": "頁面流量", "label.visitors": "獨立訪客", + "label.website": "Website", "label.website-id": "Website ID", "label.websites": "網站", + "label.window": "Window", "label.yesterday": "Yesterday", + "labels.after": "After", + "labels.average": "Average", + "labels.before": "Before", + "labels.breakdown": "Breakdown", + "labels.contains": "Contains", + "labels.create-report": "Create report", + "labels.description": "Description", + "labels.does-not-contain": "Does not contain", + "labels.does-not-equal": "Does not equal", + "labels.equals": "Equals", + "labels.false": "False", + "labels.filters": "Filters", + "labels.greater-than": "Greater than", + "labels.greater-than-equals": "Greater than or equals", + "labels.less-than": "Less than", + "labels.less-than-equals": "Less than or equals", + "labels.max": "Max", + "labels.min": "Min", + "labels.overview": "Overview", + "labels.sum": "Sum", + "labels.total": "Total", + "labels.total-records": "Total records", + "labels.true": "True", + "labels.type": "Type", + "labels.unique": "Unique", + "labels.untitled": "Untitled", + "labels.value": "Value", "message.active-users": "當前線上 {x} 人", "message.confirm-delete": "你確定要刪除 {target} 嗎?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "您確定要重置 {target} 的數據嗎?", - "message.delete-website": "刪除網站", + "message.delete-account": "To delete this account, type {confirmation} in the box below to confirm.", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "所有相關數據將會被刪除。", "message.error": "出現錯誤。", "message.event-log": "{event} on {url}", @@ -127,11 +170,12 @@ "message.invalid-domain": "無效域名", "message.min-password-length": "Minimum length of {n} characters", "message.no-data-available": "無可用數據。", + "message.no-event-data": "No event data is available.", "message.no-match-password": "密碼不一致", "message.no-teams": "You have not created any teams.", "message.no-users": "There are no users.", "message.page-not-found": "網頁未找到。", - "message.reset-website": "重置統計數據", + "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "message.reset-website-warning": "本網站的所有統計數據將被刪除,但您的跟蹤代碼將保持不變。", "message.saved": "成功保存。", "message.share-url": "這是 {target} 的分享連結。", @@ -140,6 +184,7 @@ "message.tracking-code": "追蹤代碼", "message.user-deleted": "User deleted.", "message.visitor-log": "來自{country}的訪客在搭載 {os} 的{device}上使用 {browser} 進行訪問。", + "messages.no-results-found": "No results were found.", "messages.no-team-websites": "This team does not have any websites.", "messages.no-websites-configured": "目前無任何網站設定。", "messages.team-websites-info": "Websites can be viewed by anyone on the team." diff --git a/lib/auth.ts b/lib/auth.ts index 2195ad8f11..bf01a1ab77 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,6 +1,6 @@ -import debug from 'debug'; +import { Report } from '@prisma/client'; import redis from '@umami/redis-client'; -import cache from 'lib/cache'; +import debug from 'debug'; import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants'; import { secret } from 'lib/crypto'; import { @@ -10,11 +10,11 @@ import { parseSecureToken, parseToken, } from 'next-basics'; -import { getTeamUser, getTeamUserById } from 'queries'; +import { getTeamUser } from 'queries'; import { getTeamWebsite, getTeamWebsiteByTeamMemberId } from 'queries/admin/teamWebsite'; import { validate } from 'uuid'; -import { Auth } from './types'; import { loadWebsite } from './query'; +import { Auth } from './types'; const log = debug('umami:auth'); @@ -135,7 +135,34 @@ export async function canDeleteWebsite({ user }: Auth, websiteId: string) { return false; } -// To-do: Implement when payments are setup. +export async function canViewReport(auth: Auth, report: Report) { + if (auth.user.isAdmin) { + return true; + } + + if ((auth.user.id = report.userId)) { + return true; + } + + if (await canViewWebsite(auth, report.websiteId)) { + return true; + } + + return false; +} + +export async function canUpdateReport(auth: Auth, report: Report) { + if (auth.user.isAdmin) { + return true; + } + + if ((auth.user.id = report.userId)) { + return true; + } + + return false; +} + export async function canCreateTeam({ user }: Auth) { if (user.isAdmin) { return true; @@ -144,7 +171,6 @@ export async function canCreateTeam({ user }: Auth) { return !!user; } -// To-do: Implement when payments are setup. export async function canViewTeam({ user }: Auth, teamId: string) { if (user.isAdmin) { return true; diff --git a/lib/cache.ts b/lib/cache.ts index 2aad7ed8de..7ee7bf287e 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -2,35 +2,7 @@ import { User, Website } from '@prisma/client'; import redis from '@umami/redis-client'; import { getSession, getUser, getWebsite } from '../queries'; -const DELETED = 'DELETED'; - -async function fetchObject(key, query) { - const obj = await redis.get(key); - - if (obj === DELETED) { - return null; - } - - if (!obj) { - return query().then(async data => { - if (data) { - await redis.set(key, data); - } - - return data; - }); - } - - return obj; -} - -async function storeObject(key, data) { - return redis.set(key, data); -} - -async function deleteObject(key, soft = false) { - return soft ? redis.set(key, DELETED) : redis.del(key); -} +const { fetchObject, storeObject, deleteObject } = redis; async function fetchWebsite(id): Promise { return fetchObject(`website:${id}`, () => getWebsite({ id })); @@ -77,6 +49,16 @@ async function deleteSession(id) { return deleteObject(`session:${id}`); } +async function fetchUserBlock(userId: string) { + const key = `user:block:${userId}`; + return redis.get(key); +} + +async function incrementUserBlock(userId: string) { + const key = `user:block:${userId}`; + return redis.incr(key); +} + export default { fetchWebsite, storeWebsite, @@ -87,5 +69,7 @@ export default { fetchSession, storeSession, deleteSession, + fetchUserBlock, + incrementUserBlock, enabled: redis.enabled, }; diff --git a/lib/charts.js b/lib/charts.js new file mode 100644 index 0000000000..0571a9a9ec --- /dev/null +++ b/lib/charts.js @@ -0,0 +1,62 @@ +import { StatusLight } from 'react-basics'; +import { dateFormat } from 'lib/date'; +import { formatLongNumber } from 'lib/format'; + +export function renderNumberLabels(label) { + return +label > 1000 ? formatLongNumber(label) : label; +} + +export function renderDateLabels(unit, locale) { + return (label, index, values) => { + const d = new Date(values[index].value); + + switch (unit) { + case 'minute': + return dateFormat(d, 'h:mm', locale); + case 'hour': + return dateFormat(d, 'p', locale); + case 'day': + return dateFormat(d, 'MMM d', locale); + case 'month': + return dateFormat(d, 'MMM', locale); + case 'year': + return dateFormat(d, 'YYY', locale); + default: + return label; + } + }; +} + +export function renderStatusTooltipPopup(unit, locale) { + return (setTooltipPopup, model) => { + const { opacity, labelColors, dataPoints } = model.tooltip; + + if (!dataPoints?.length || !opacity) { + setTooltipPopup(null); + return; + } + + const formats = { + millisecond: 'T', + second: 'pp', + minute: 'p', + hour: 'h:mm aaa - PP', + day: 'PPPP', + week: 'PPPP', + month: 'LLLL yyyy', + quarter: 'qqq', + year: 'yyyy', + }; + + setTooltipPopup( + <> +
{dateFormat(new Date(dataPoints[0].raw.x), formats[unit], locale)}
+
+ + {formatLongNumber(dataPoints[0].raw.y)} {dataPoints[0].dataset.label} + +
+ , + ); + }; +} diff --git a/lib/clickhouse.ts b/lib/clickhouse.ts index 90cf6088a8..eb73d83ccf 100644 --- a/lib/clickhouse.ts +++ b/lib/clickhouse.ts @@ -2,7 +2,7 @@ import { ClickHouse } from 'clickhouse'; import dateFormat from 'dateformat'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; -import { getEventDataType } from './eventData'; +import { getDynamicDataType } from './dynamicData'; import { WebsiteMetricFilter } from './types'; import { FILTER_COLUMNS } from './constants'; @@ -74,7 +74,7 @@ function getEventDataFilterQuery( params: any, ) { const query = filters.reduce((ac, cv, i) => { - const type = getEventDataType(cv.eventValue); + const type = getDynamicDataType(cv.eventValue); let value = cv.eventValue; @@ -82,17 +82,17 @@ function getEventDataFilterQuery( switch (type) { case 'number': - ac.push(`and event_numeric_value = {eventValue${i}:UInt64})`); + ac.push(`and number_value = {eventValue${i}:UInt64})`); break; case 'string': - ac.push(`and event_string_value = {eventValue${i}:String})`); + ac.push(`and string_value = {eventValue${i}:String})`); break; case 'boolean': - ac.push(`and event_string_value = {eventValue${i}:String})`); + ac.push(`and string_value = {eventValue${i}:String})`); value = cv ? 'true' : 'false'; break; case 'date': - ac.push(`and event_date_value = {eventValue${i}:DateTime('UTC')})`); + ac.push(`and date_value = {eventValue${i}:DateTime('UTC')})`); break; } @@ -121,13 +121,36 @@ function getFilterQuery(filters = {}, params = {}) { return query.join('\n'); } +function getFunnelQuery(urls: string[]): { + columnsQuery: string; + conditionQuery: string; + urlParams: { [key: string]: string }; +} { + return urls.reduce( + (pv, cv, i) => { + pv.columnsQuery += `\n,url_path = {url${i}:String}${ + i > 0 && urls[i - 1] ? ` AND referrer_path = {url${i - 1}:String}` : '' + }`; + pv.conditionQuery += `${i > 0 ? ',' : ''} {url${i}:String}`; + pv.urlParams[`url${i}`] = cv; + + return pv; + }, + { + columnsQuery: '', + conditionQuery: '', + urlParams: {}, + }, + ); +} + function parseFilters(filters: WebsiteMetricFilter = {}, params: any = {}) { return { filterQuery: getFilterQuery(filters, params), }; } -async function rawQuery(query, params = {}) { +async function rawQuery(query, params = {}): Promise { if (process.env.LOG_QUERY) { log('QUERY:\n', query); log('PARAMETERS:\n', params); @@ -135,7 +158,7 @@ async function rawQuery(query, params = {}) { await connect(); - return clickhouse.query(query, { params }).toPromise(); + return clickhouse.query(query, { params }).toPromise() as Promise; } async function findUnique(data) { @@ -168,6 +191,7 @@ export default { getDateFormat, getBetweenDates, getFilterQuery, + getFunnelQuery, getEventDataFilterQuery, parseFilters, findUnique, diff --git a/lib/constants.ts b/lib/constants.ts index 425d729fb4..209a97b62a 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -18,6 +18,7 @@ export const DEFAULT_THEME = 'light'; export const DEFAULT_ANIMATION_DURATION = 300; export const DEFAULT_DATE_RANGE = '24hour'; export const DEFAULT_WEBSITE_LIMIT = 10; +export const DEFAULT_CREATED_AT = '2000-01-01'; export const REALTIME_RANGE = 30; export const REALTIME_INTERVAL = 5000; @@ -42,6 +43,11 @@ export const SESSION_COLUMNS = [ 'city', ]; +export const COLLECTION_TYPE = { + event: 'event', + identify: 'identify', +}; + export const FILTER_COLUMNS = { url: 'url_path', referrer: 'referrer_domain', @@ -56,7 +62,7 @@ export const EVENT_TYPE = { customEvent: 2, } as const; -export const EVENT_DATA_TYPE = { +export const DATA_TYPE = { string: 1, number: 2, boolean: 3, @@ -64,6 +70,20 @@ export const EVENT_DATA_TYPE = { array: 5, } as const; +export const DATA_TYPES = { + [DATA_TYPE.string]: 'string', + [DATA_TYPE.number]: 'number', + [DATA_TYPE.boolean]: 'boolean', + [DATA_TYPE.date]: 'date', + [DATA_TYPE.array]: 'array', +}; + +export const REPORT_PARAMETERS = { + fields: 'fields', + filters: 'filters', + groups: 'groups', +} as const; + export const KAFKA_TOPIC = { event: 'event', eventData: 'event_data', @@ -72,6 +92,7 @@ export const KAFKA_TOPIC = { export const ROLES = { admin: 'admin', user: 'user', + viewOnly: 'view-only', teamOwner: 'team-owner', teamMember: 'team-member', } as const; @@ -94,6 +115,7 @@ export const ROLE_PERMISSIONS = { PERMISSIONS.websiteDelete, PERMISSIONS.teamCreate, ], + [ROLES.viewOnly]: [], [ROLES.teamOwner]: [PERMISSIONS.teamUpdate, PERMISSIONS.teamDelete], [ROLES.teamMember]: [], } as const; @@ -144,8 +166,11 @@ export const EVENT_COLORS = [ '#ffec16', ]; -export const DOMAIN_REGEX = - /^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})$/; +export const DOMAIN_REGEX = + /^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+(-[a-z0-9-]+)*\.)+(xn--)?[a-z0-9-]{2,63})$/; + + +export const SHARE_ID_REGEX = /^[a-zA-Z0-9]{16}$/; export const DESKTOP_SCREEN_WIDTH = 1920; export const LAPTOP_SCREEN_WIDTH = 1024; @@ -158,7 +183,7 @@ export const DESKTOP_OS = [ 'BeOS', 'Chrome OS', 'Linux', - 'macOS', + 'Mac OS', 'Open BSD', 'OS/2', 'QNX', @@ -180,33 +205,34 @@ export const DESKTOP_OS = [ export const MOBILE_OS = ['Amazon OS', 'Android OS', 'BlackBerry OS', 'iOS', 'Windows Mobile']; export const BROWSERS = { + android: 'Android', aol: 'AOL', - edge: 'Edge', - 'edge-ios': 'Edge (iOS)', - yandexbrowser: 'Yandex', - kakaotalk: 'KaKaoTalk', - samsung: 'Samsung', - silk: 'Silk', - miui: 'MIUI', beaker: 'Beaker', - 'edge-chromium': 'Edge (Chromium)', + bb10: 'BlackBerry 10', chrome: 'Chrome', 'chromium-webview': 'Chrome (webview)', - phantomjs: 'PhantomJS', crios: 'Chrome (iOS)', + curl: 'Curl', + edge: 'Edge', + 'edge-chromium': 'Edge (Chromium)', + 'edge-ios': 'Edge (iOS)', + facebook: 'Facebook', firefox: 'Firefox', fxios: 'Firefox (iOS)', - 'opera-mini': 'Opera Mini', - opera: 'Opera', ie: 'IE', - bb10: 'BlackBerry 10', - android: 'Android', - ios: 'iOS', - safari: 'Safari', - facebook: 'Facebook', instagram: 'Instagram', + ios: 'iOS', 'ios-webview': 'iOS (webview)', + kakaotalk: 'KaKaoTalk', + miui: 'MIUI', + opera: 'Opera', + 'opera-mini': 'Opera Mini', + phantomjs: 'PhantomJS', + safari: 'Safari', + samsung: 'Samsung', + silk: 'Silk', searchbot: 'Searchbot', + yandexbrowser: 'Yandex', }; export const MAP_FILE = '/datamaps.world.json'; diff --git a/lib/date.js b/lib/date.js index 1cfca75d31..526354b388 100644 --- a/lib/date.js +++ b/lib/date.js @@ -40,20 +40,27 @@ export function getLocalTime(t) { export function parseDateRange(value, locale = 'en-US') { if (typeof value === 'object') { - const { startDate, endDate } = value; + return value; + } + + if (value?.startsWith?.('range')) { + const [, startAt, endAt] = value.split(':'); + + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); + return { - ...value, - startDate: typeof startDate === 'string' ? parseISO(startDate) : startDate, - endDate: typeof endDate === 'string' ? parseISO(endDate) : endDate, + ...getDateRangeValues(startDate, endDate), + value, }; } const now = new Date(); const dateLocale = getDateLocale(locale); - const match = value.match(/^(?[0-9-]+)(?hour|day|week|month|year)$/); + const match = value?.match?.(/^(?[0-9-]+)(?hour|day|week|month|year)$/); - if (!match) return {}; + if (!match) return null; const { num, unit } = match.groups; diff --git a/lib/eventData.ts b/lib/dynamicData.ts similarity index 62% rename from lib/eventData.ts rename to lib/dynamicData.ts index 4588d08174..c2c53de317 100644 --- a/lib/eventData.ts +++ b/lib/dynamicData.ts @@ -1,12 +1,12 @@ import { isValid, parseISO } from 'date-fns'; -import { EVENT_DATA_TYPE } from './constants'; -import { EventDataTypes } from './types'; +import { DATA_TYPE } from './constants'; +import { DynamicDataType } from './types'; export function flattenJSON( eventData: { [key: string]: any }, - keyValues: { key: string; value: any; eventDataType: EventDataTypes }[] = [], + keyValues: { key: string; value: any; dynamicDataType: DynamicDataType }[] = [], parentKey = '', -): { key: string; value: any; eventDataType: EventDataTypes }[] { +): { key: string; value: any; dynamicDataType: DynamicDataType }[] { return Object.keys(eventData).reduce( (acc, key) => { const value = eventData[key]; @@ -25,7 +25,7 @@ export function flattenJSON( ).keyValues; } -export function getEventDataType(value: any): string { +export function getDynamicDataType(value: any): string { let type: string = typeof value; if ((type === 'string' && isValid(value)) || isValid(parseISO(value))) { @@ -36,33 +36,34 @@ export function getEventDataType(value: any): string { } function createKey(key, value, acc: { keyValues: any[]; parentKey: string }) { - const type = getEventDataType(value); + const type = getDynamicDataType(value); - let eventDataType = null; + let dynamicDataType = null; switch (type) { case 'number': - eventDataType = EVENT_DATA_TYPE.number; + dynamicDataType = DATA_TYPE.number; break; case 'string': - eventDataType = EVENT_DATA_TYPE.string; + dynamicDataType = DATA_TYPE.string; break; case 'boolean': - eventDataType = EVENT_DATA_TYPE.boolean; + dynamicDataType = DATA_TYPE.boolean; + value = value ? 'true' : 'false'; break; case 'date': - eventDataType = EVENT_DATA_TYPE.date; + dynamicDataType = DATA_TYPE.date; break; case 'object': - eventDataType = EVENT_DATA_TYPE.array; + dynamicDataType = DATA_TYPE.array; value = JSON.stringify(value); break; default: - eventDataType = EVENT_DATA_TYPE.string; + dynamicDataType = DATA_TYPE.string; break; } - acc.keyValues.push({ key, value, eventDataType }); + acc.keyValues.push({ key, value, dynamicDataType }); } function getKeyName(key, parentKey) { diff --git a/lib/kafka.ts b/lib/kafka.ts index 3d3e281cec..8f5bb87f0f 100644 --- a/lib/kafka.ts +++ b/lib/kafka.ts @@ -61,8 +61,8 @@ async function getProducer(): Promise { return producer; } -function getDateFormat(date): string { - return dateFormat(date, 'UTC:yyyy-mm-dd HH:MM:ss'); +function getDateFormat(date, format?): string { + return dateFormat(date, format ? format : 'UTC:yyyy-mm-dd HH:MM:ss'); } async function sendMessage( diff --git a/lib/middleware.ts b/lib/middleware.ts index 79c48404e7..1fd13b095c 100644 --- a/lib/middleware.ts +++ b/lib/middleware.ts @@ -1,4 +1,10 @@ -import { createMiddleware, unauthorized, badRequest, parseSecureToken } from 'next-basics'; +import { + createMiddleware, + unauthorized, + badRequest, + parseSecureToken, + tooManyRequest, +} from 'next-basics'; import debug from 'debug'; import cors from 'cors'; import { validate } from 'uuid'; @@ -30,6 +36,9 @@ export const useSession = createMiddleware(async (req, res, next) => { (req as any).session = session; } catch (e: any) { + if (e.message === 'Usage Limit.') { + return tooManyRequest(res, e.message); + } return badRequest(res, e.message); } diff --git a/lib/prisma.ts b/lib/prisma.ts index 0a10d98165..875f589756 100644 --- a/lib/prisma.ts +++ b/lib/prisma.ts @@ -1,7 +1,7 @@ import prisma from '@umami/prisma-client'; import moment from 'moment-timezone'; import { MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db'; -import { getEventDataType } from './eventData'; +import { getDynamicDataType } from './dynamicData'; import { FILTER_COLUMNS } from './constants'; const MYSQL_DATE_FORMATS = { @@ -32,6 +32,18 @@ function toUuid(): string { } } +function getAddMinutesQuery(field: string, minutes: number) { + const db = getDatabaseType(process.env.DATABASE_URL); + + if (db === POSTGRESQL) { + return `${field} + interval '${minutes} minute'`; + } + + if (db === MYSQL) { + return `DATE_ADD(${field}, interval ${minutes} minute)`; + } +} + function getDateQuery(field: string, unit: string, timezone?: string): string { const db = getDatabaseType(process.env.DATABASE_URL); @@ -73,7 +85,7 @@ function getEventDataFilterQuery( params: any[], ) { const query = filters.reduce((ac, cv) => { - const type = getEventDataType(cv.eventValue); + const type = getDynamicDataType(cv.eventValue); let value = cv.eventValue; @@ -82,20 +94,20 @@ function getEventDataFilterQuery( switch (type) { case 'number': - ac.push(`and event_numeric_value = $${params.length + 1})`); + ac.push(`and number_value = $${params.length + 1})`); params.push(value); break; case 'string': - ac.push(`and event_string_value = $${params.length + 1})`); + ac.push(`and string_value = $${params.length + 1})`); params.push(decodeURIComponent(cv.eventValue as string)); break; case 'boolean': - ac.push(`and event_string_value = $${params.length + 1})`); + ac.push(`and string_value = $${params.length + 1})`); params.push(decodeURIComponent(cv.eventValue as string)); value = cv ? 'true' : 'false'; break; case 'date': - ac.push(`and event_date_value = $${params.length + 1})`); + ac.push(`and date_value = $${params.length + 1})`); params.push(cv.eventValue); break; } @@ -122,6 +134,53 @@ function getFilterQuery(filters = {}, params = []): string { return query.join('\n'); } +function getFunnelQuery( + urls: string[], + windowMinutes: number, +): { + levelQuery: string; + sumQuery: string; + urlFilterQuery: string; +} { + const initParamLength = 3; + + return urls.reduce( + (pv, cv, i) => { + const levelNumber = i + 1; + const start = i > 0 ? ',' : ''; + + if (levelNumber >= 2) { + pv.levelQuery += `\n + , level${levelNumber} AS ( + select cl.*, + l0.created_at level_${levelNumber}_created_at, + l0.url_path as level_${levelNumber}_url + from level${i} cl + left join website_event l0 + on cl.session_id = l0.session_id + and l0.created_at between cl.level_${i}_created_at + and ${getAddMinutesQuery(`cl.level_${i}_created_at`, windowMinutes)} + and l0.referrer_path = $${i + initParamLength} + and l0.url_path = $${levelNumber + initParamLength} + and created_at between $2 and $3 + and website_id = $1${toUuid()} + )`; + } + + pv.sumQuery += `\n${start}SUM(CASE WHEN level_${levelNumber}_url is not null THEN 1 ELSE 0 END) AS level${levelNumber}`; + + pv.urlFilterQuery += `\n${start}$${levelNumber + initParamLength} `; + + return pv; + }, + { + levelQuery: '', + sumQuery: '', + urlFilterQuery: '', + }, + ); +} + function parseFilters( filters: { [key: string]: any } = {}, params = [], @@ -152,9 +211,11 @@ async function rawQuery(query: string, params: never[] = []): Promise { export default { ...prisma, + getAddMinutesQuery, getDateQuery, getTimestampInterval, getFilterQuery, + getFunnelQuery, getEventDataFilterQuery, toUuid, parseFilters, diff --git a/lib/session.ts b/lib/session.ts index 937bfef2ea..29ff694f63 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -1,10 +1,10 @@ -import clickhouse from 'lib/clickhouse'; import { secret, uuid } from 'lib/crypto'; import { getClientInfo, getJsonBody } from 'lib/detect'; import { parseToken } from 'next-basics'; import { CollectRequestBody, NextApiRequestCollect } from 'pages/api/send'; import { createSession } from 'queries'; import { validate } from 'uuid'; +import cache from './cache'; import { loadSession, loadWebsite } from './query'; export async function findSession(req: NextApiRequestCollect) { @@ -21,6 +21,8 @@ export async function findSession(req: NextApiRequestCollect) { const result = await parseToken(cacheToken, secret()); if (result) { + await checkUserBlock(result?.ownerId); + return result; } } @@ -39,27 +41,12 @@ export async function findSession(req: NextApiRequestCollect) { throw new Error(`Website not found: ${websiteId}.`); } + await checkUserBlock(website.userId); + const { userAgent, browser, os, ip, country, subdivision1, subdivision2, city, device } = await getClientInfo(req, payload); - const sessionId = uuid(websiteId, hostname, ip, userAgent); - // Clickhouse does not require session lookup - if (clickhouse.enabled) { - return { - id: sessionId, - websiteId, - hostname, - browser, - os, - device, - screen, - language, - country, - subdivision1, - subdivision2, - city, - }; - } + const sessionId = uuid(websiteId, hostname, ip, userAgent); // Find session let session = await loadSession(sessionId); @@ -88,5 +75,13 @@ export async function findSession(req: NextApiRequestCollect) { } } - return session; + return { ...session, ownerId: website.userId }; +} + +async function checkUserBlock(userId: string) { + if (process.env.ENABLE_BLOCKER && (await cache.fetchUserBlock(userId))) { + await cache.incrementUserBlock(userId); + + throw new Error('Usage Limit.'); + } } diff --git a/lib/types.ts b/lib/types.ts index 37c1ffdc93..2e1ed986c2 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,18 +1,20 @@ import { NextApiRequest } from 'next'; -import { EVENT_DATA_TYPE, EVENT_TYPE, KAFKA_TOPIC, ROLES } from './constants'; +import { COLLECTION_TYPE, DATA_TYPE, EVENT_TYPE, KAFKA_TOPIC, ROLES } from './constants'; type ObjectValues = T[keyof T]; -export type Roles = ObjectValues; +export type CollectionType = ObjectValues; -export type EventTypes = ObjectValues; +export type Role = ObjectValues; -export type EventDataTypes = ObjectValues; +export type EventType = ObjectValues; -export type KafkaTopics = ObjectValues; +export type DynamicDataType = ObjectValues; -export interface EventData { - [key: string]: number | string | EventData | number[] | string[] | EventData[]; +export type KafkaTopic = ObjectValues; + +export interface DynamicData { + [key: string]: number | string | DynamicData | number[] | string[] | DynamicData[]; } export interface Auth { @@ -92,11 +94,17 @@ export interface WebsiteEventMetric { y: number; } -export interface WebsiteEventDataMetric { - x: string; - t: string; - eventName?: string; - urlPath?: string; +export interface WebsiteEventDataStats { + field: string; + type: number; + total: number; +} + +export interface WebsiteEventDataFields { + field: string; + type: number; + value?: string; + total: number; } export interface WebsitePageviews { diff --git a/next.config.js b/next.config.js index 8efb45bc17..0778f979e7 100644 --- a/next.config.js +++ b/next.config.js @@ -2,15 +2,13 @@ require('dotenv').config(); const pkg = require('./package.json'); -const CLOUD_URL = 'https://cloud.umami.is'; - const contentSecurityPolicy = ` default-src 'self'; img-src *; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' api.umami.is; - frame-ancestors 'self'; + frame-ancestors 'self' ${process.env.ALLOWED_FRAME_URLS}; `; const headers = [ @@ -65,7 +63,7 @@ const redirects = [ }, ]; -if (process.env.CLOUD_MODE && process.env.DISABLE_LOGIN && process.env.CLOUD_URL) { +if (process.env.CLOUD_MODE && process.env.CLOUD_URL && process.env.DISABLE_LOGIN) { redirects.push({ source: '/login', destination: process.env.CLOUD_URL, diff --git a/package.json b/package.json index 8726f0b360..78b15ee2aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "2.2.0", + "version": "2.3.0", "description": "A simple, fast, privacy-focused alternative to Google Analytics.", "author": "Mike Cao ", "license": "MIT", @@ -11,7 +11,7 @@ }, "scripts": { "dev": "next dev -p 3000", - "build": "npm-run-all build-db check-db build-tracker build-geo build-app", + "build": "npm-run-all check-env build-db check-db build-tracker build-geo build-app", "start": "next start", "build-docker": "npm-run-all build-db build-tracker build-geo build-app", "start-docker": "npm-run-all check-db update-tracker start-server", @@ -27,6 +27,7 @@ "update-tracker": "node scripts/update-tracker.js", "update-db": "prisma migrate deploy", "check-db": "node scripts/check-db.js", + "check-env": "node scripts/check-env.js", "copy-db-files": "node scripts/copy-db-files.js", "extract-messages": "formatjs extract \"{pages,components}/**/*.js\" --out-file build/messages.json", "merge-messages": "node scripts/merge-messages.js", @@ -59,10 +60,10 @@ ], "dependencies": { "@fontsource/inter": "^4.5.15", - "@prisma/client": "4.13.0", + "@prisma/client": "4.15.0", "@tanstack/react-query": "^4.16.1", "@umami/prisma-client": "^0.2.0", - "@umami/redis-client": "^0.2.0", + "@umami/redis-client": "^0.5.0", "chalk": "^4.1.1", "chart.js": "^4.2.1", "chartjs-adapter-date-fns": "^3.0.0", @@ -78,7 +79,6 @@ "del": "^6.0.0", "detect-browser": "^5.2.0", "dotenv": "^10.0.0", - "formik": "^2.2.9", "fs-extra": "^10.0.1", "immer": "^9.0.12", "ipaddr.js": "^2.0.1", @@ -89,12 +89,12 @@ "kafkajs": "^2.1.0", "maxmind": "^4.3.6", "moment-timezone": "^0.5.35", - "next": "13.2.4", - "next-basics": "^0.27.0", + "next": "13.3.1", + "next-basics": "^0.31.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", "react": "^18.2.0", - "react-basics": "^0.77.0", + "react-basics": "^0.91.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.4", @@ -109,7 +109,7 @@ "timezone-support": "^2.0.2", "uuid": "^8.3.2", "yup": "^0.32.11", - "zustand": "^3.7.2" + "zustand": "^4.3.8" }, "devDependencies": { "@formatjs/cli": "^4.2.29", @@ -141,7 +141,7 @@ "postcss-preset-env": "7.8.3", "postcss-rtlcss": "^4.0.1", "prettier": "^2.6.2", - "prisma": "4.13.0", + "prisma": "4.15.0", "prompts": "2.4.2", "rollup": "^2.70.1", "rollup-plugin-delete": "^2.0.0", @@ -150,7 +150,7 @@ "rollup-plugin-node-externals": "^5.1.2", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-terser": "^7.0.2", - "stylelint": "^14.16.1", + "stylelint": "^15.10.1", "stylelint-config-css-modules": "^4.1.0", "stylelint-config-prettier": "^9.0.3", "stylelint-config-recommended": "^9.0.0", diff --git a/pages/_app.js b/pages/_app.js index 2245821563..8d54977326 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,5 +1,6 @@ import { IntlProvider } from 'react-intl'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactBasicsProvider } from 'react-basics'; import Head from 'next/head'; import Script from 'next/script'; import { useRouter } from 'next/router'; @@ -42,37 +43,39 @@ export default function App({ Component, pageProps }) { textComponent={Wrapper} onError={() => null} > - - - - - - - - - - - - - - - - {!pathname.includes('/share/') &&