diff --git a/Dockerfile b/Dockerfile index 3fc32e0b..bdc678da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ RUN yarn install --frozen-lockfile FROM node:18-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules +COPY docker/middleware.js . COPY . . ARG DATABASE_TYPE diff --git a/components/input/LanguageButton.js b/components/input/LanguageButton.js index e6da5991..bc130c4c 100644 --- a/components/input/LanguageButton.js +++ b/components/input/LanguageButton.js @@ -6,7 +6,7 @@ import Icons from 'components/icons'; import styles from './LanguageButton.module.css'; export default function LanguageButton() { - const { locale, saveLocale } = useLocale(); + const { locale, saveLocale, dir } = useLocale(); const items = Object.keys(languages).map(key => ({ ...languages[key], value: key })); function handleSelect(value) { @@ -20,7 +20,7 @@ export default function LanguageButton() { - +
{items.map(({ value, label }) => { return ( diff --git a/components/input/ProfileButton.js b/components/input/ProfileButton.js index 547054e0..453f1df3 100644 --- a/components/input/ProfileButton.js +++ b/components/input/ProfileButton.js @@ -5,12 +5,14 @@ import useMessages from 'hooks/useMessages'; import useUser from 'hooks/useUser'; import useConfig from 'hooks/useConfig'; import styles from './ProfileButton.module.css'; +import useLocale from 'hooks/useLocale'; export default function ProfileButton() { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const { cloudMode } = useConfig(); const router = useRouter(); + const { dir } = useLocale(); const handleSelect = key => { if (key === 'profile') { @@ -31,7 +33,7 @@ export default function ProfileButton() { - + {user.username} diff --git a/components/metrics/BarChart.js b/components/metrics/BarChart.js index 3065247a..95f25a5e 100644 --- a/components/metrics/BarChart.js +++ b/components/metrics/BarChart.js @@ -1,5 +1,5 @@ import { useState, useRef, useEffect, useMemo, useCallback } from 'react'; -import { StatusLight } from 'react-basics'; +import { StatusLight, Loading } from 'react-basics'; import classNames from 'classnames'; import Chart from 'chart.js/auto'; import HoverTooltip from 'components/common/HoverTooltip'; @@ -39,6 +39,26 @@ export default function BarChart({ 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; @@ -115,6 +135,7 @@ export default function BarChart({ color: colors.text, autoSkip: false, maxRotation: 0, + callback: renderXLabel, }, }, y: { @@ -135,7 +156,7 @@ export default function BarChart({ }, }, }; - }, [animationDuration, renderTooltip, stacked, colors, unit]); + }, [animationDuration, renderTooltip, renderXLabel, stacked, colors, unit, locale]); const createChart = () => { Chart.defaults.font.family = 'Inter'; @@ -158,7 +179,9 @@ export default function BarChart({ chart.current.options = getOptions(); - chart.current.data.datasets = datasets; + if (datasets.length) { + chart.current.data.datasets = datasets; + } chart.current.update(); @@ -173,11 +196,12 @@ export default function BarChart({ updateChart(); } } - }, [datasets, unit, theme, animationDuration, locale, loading]); + }, [datasets, unit, theme, animationDuration, locale]); return ( <>
+ {loading && }
diff --git a/components/metrics/Legend.js b/components/metrics/Legend.js index 32267970..10848066 100644 --- a/components/metrics/Legend.js +++ b/components/metrics/Legend.js @@ -1,3 +1,4 @@ +import { useEffect } from 'react'; import { StatusLight } from 'react-basics'; import { colord } from 'colord'; import classNames from 'classnames'; @@ -9,7 +10,7 @@ export default function Legend({ chart }) { const { locale } = useLocale(); const forceUpdate = useForceUpdate(); - function handleClick(index) { + const handleClick = index => { const meta = chart.getDatasetMeta(index); meta.hidden = meta.hidden === null ? !chart.data.datasets[index].hidden : null; @@ -17,7 +18,11 @@ export default function Legend({ chart }) { chart.update(); forceUpdate(); - } + }; + + useEffect(() => { + forceUpdate(); + }, [locale]); if (!chart?.legend?.legendItems.find(({ text }) => text)) { return null; diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js index 0d83fc22..15eeb893 100644 --- a/components/metrics/MetricsTable.js +++ b/components/metrics/MetricsTable.js @@ -13,6 +13,7 @@ import { DEFAULT_ANIMATION_DURATION } from 'lib/constants'; import Icons from 'components/icons'; import useMessages from 'hooks/useMessages'; import styles from './MetricsTable.module.css'; +import useLocale from 'hooks/useLocale'; export default function MetricsTable({ websiteId, @@ -69,6 +70,7 @@ export default function MetricsTable({ } return []; }, [data, error, dataFilter, filterOptions]); + const { dir } = useLocale(); return (
@@ -80,7 +82,7 @@ export default function MetricsTable({ diff --git a/components/metrics/PageviewsChart.js b/components/metrics/PageviewsChart.js index c3c1d6fa..7370e5f9 100644 --- a/components/metrics/PageviewsChart.js +++ b/components/metrics/PageviewsChart.js @@ -4,6 +4,7 @@ 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'; export default function PageviewsChart({ websiteId, @@ -16,6 +17,7 @@ export default function PageviewsChart({ }) { const { formatMessage, labels } = useMessages(); const [theme] = useTheme(); + const { locale } = useLocale(); const colors = useMemo(() => { const primaryColor = colord(THEME_COLORS[theme].primary); @@ -52,7 +54,7 @@ export default function PageviewsChart({ ...colors.views, }, ]; - }, [data]); + }, [data, locale, colors]); return ( @@ -80,7 +82,9 @@ export default function WebsiteChart({ diff --git a/components/pages/realtime/RealtimeHeader.js b/components/pages/realtime/RealtimeHeader.js index 5a7e1c3f..a5543be4 100644 --- a/components/pages/realtime/RealtimeHeader.js +++ b/components/pages/realtime/RealtimeHeader.js @@ -9,14 +9,26 @@ export default function RealtimeHeader({ data = {} }) { return (
- + - + @@ -48,7 +48,7 @@ export default function PasswordEditForm({ onSave, onClose }) { name="confirmPassword" rules={{ required: formatMessage(labels.required), - minLength: { value: 8, message: formatMessage(messages.minPasswordLength) }, + minLength: { value: 8, message: formatMessage(messages.minPasswordLength, { n: 8 }) }, validate: samePassword, }} > diff --git a/components/pages/settings/teams/TeamSettings.js b/components/pages/settings/teams/TeamSettings.js index 3b5c2937..2fbd8d9e 100644 --- a/components/pages/settings/teams/TeamSettings.js +++ b/components/pages/settings/teams/TeamSettings.js @@ -49,7 +49,7 @@ export default function TeamSettings({ teamId }) { title={ - Teams + {formatMessage(labels.teams)} {values?.name} diff --git a/components/pages/settings/users/UserEditForm.js b/components/pages/settings/users/UserEditForm.js index 49df4b24..373d1dbe 100644 --- a/components/pages/settings/users/UserEditForm.js +++ b/components/pages/settings/users/UserEditForm.js @@ -48,7 +48,7 @@ export default function UserEditForm({ userId, data, onSave }) { diff --git a/components/pages/settings/users/UsersTable.js b/components/pages/settings/users/UsersTable.js index c60f11ff..66ef96db 100644 --- a/components/pages/settings/users/UsersTable.js +++ b/components/pages/settings/users/UsersTable.js @@ -6,10 +6,12 @@ import UserDeleteForm from './UserDeleteForm'; import { ROLES } from 'lib/constants'; import useMessages from 'hooks/useMessages'; import SettingsTable from 'components/common/SettingsTable'; +import useLocale from 'hooks/useLocale'; export default function UsersTable({ data = [], onDelete }) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); + const { dateLocale } = useLocale(); const columns = [ { name: 'username', label: formatMessage(labels.username), style: { flex: 1.5 } }, @@ -22,6 +24,7 @@ export default function UsersTable({ data = [], onDelete }) { if (key === 'created') { return formatDistance(new Date(row.createdAt), new Date(), { addSuffix: true, + locale: dateLocale, }); } if (key === 'role') { diff --git a/components/pages/settings/websites/WebsiteData.js b/components/pages/settings/websites/WebsiteData.js index 7c558542..bb951628 100644 --- a/components/pages/settings/websites/WebsiteData.js +++ b/components/pages/settings/websites/WebsiteData.js @@ -34,7 +34,7 @@ export default function WebsiteData({ websiteId, onSave }) { description={formatMessage(messages.deleteWebsiteWarning)} > - + {close => ( diff --git a/docker/middleware.js b/docker/middleware.js new file mode 100644 index 00000000..408c22a6 --- /dev/null +++ b/docker/middleware.js @@ -0,0 +1,46 @@ +import { NextResponse } from 'next/server'; + +export const config = { + matcher: '/:path*', +}; + +function customCollectEndpoint(req) { + const collectEndpoint = process.env.COLLECT_API_ENDPOINT; + + if (collectEndpoint) { + const url = req.nextUrl.clone(); + const { pathname } = url; + + if (pathname.endsWith(collectEndpoint)) { + url.pathname = '/api/send'; + return NextResponse.rewrite(url); + } + } +} + +function customScriptName(req) { + const scriptName = process.env.TRACKER_SCRIPT_NAME; + + if (scriptName) { + const url = req.nextUrl.clone(); + const { pathname } = url; + + if (pathname.endsWith(scriptName)) { + url.pathname = '/script.js'; + return NextResponse.rewrite(url); + } + } +} + +export default function middleware(req) { + const fns = [customCollectEndpoint, customScriptName]; + + for (const fn of fns) { + const res = fn(req); + if (res) { + return res; + } + } + + return NextResponse.next(); +} diff --git a/lang/ar-SA.json b/lang/ar-SA.json index 55bb7734..57cd58ad 100644 --- a/lang/ar-SA.json +++ b/lang/ar-SA.json @@ -1,145 +1,145 @@ { "label.access-code": "Access code", "label.actions": "اجراءات", - "label.activity-log": "Activity log", + "label.activity-log": "سجل الأحداث", "label.add-website": "إضافة موقع", - "label.admin": "مدير عام؟", + "label.admin": "مدير", "label.all": "الكل", - "label.all-time": "All time", - "label.analytics": "Analytics", + "label.all-time": "كل الوقت", + "label.analytics": "تحليلات", "label.average-visit-time": "متوسط وقت الزيارة", "label.back": "للخلف", "label.bounce-rate": "معدل الارتداد", "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.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.domain": "النطاق", "label.edit": "تعديل", - "label.edit-dashboard": "Edit dashboard", + "label.edit-dashboard": "تعديل لوحة التحكم", "label.enable-share-url": "تفعيل مشاركة الرابط", "label.events": "الأحداث", "label.filter-combined": "مجمعة", "label.filter-raw": "مفصلة", - "label.join": "Join", - "label.join-team": "Join team", - "label.language": "Language", - "label.languages": "Languages", + "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.logout": "تسجيل الخروج", - "label.members": "Members", + "label.members": "الأعضاء", "label.mobile": "جوال", "label.more": "المزيد", "label.name": "الإسم", "label.new-password": "كلمة مرور جديدة", - "label.none": "None", + "label.none": "لا شيء", "label.operating-systems": "نظام التشغيل", - "label.owner": "Owner", + "label.owner": "المالك", "label.page-views": "مشاهدات الصفحة", "label.pages": "الصفحات", "label.password": "كلمة المرور", "label.powered-by": "مشغل بواسطة {name}", "label.profile": "الملف الشخصي", - "label.queries": "Queries", - "label.query-parameters": "Query parameters", + "label.queries": "استعلامات", + "label.query-parameters": "متغيرات الرابط", "label.realtime": "الوقت الفعلي", "label.referrers": "التحويلات", "label.refresh": "تحديث", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "اعادة انشاء", + "label.regions": "المناطق", + "label.remove": "إزالة", "label.required": "اجباري", "label.reset": "اعادة تعيين", "label.reset-website": "اعادة تعيين الإحصائيات", - "label.role": "Role", + "label.role": "الصلاحية", "label.save": "حفظ", - "label.screens": "Screens", - "label.select-website": "Select website", - "label.sessions": "Sessions", + "label.screens": "الشاشات", + "label.select-website": "اختيار موقع", + "label.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.theme": "Theme", + "label.team": "مجموعة", + "label.team-guest": "زائر للمجموعة", + "label.team-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": "Toggle charts", + "label.toggle-charts": "تغيير الإحصائيات", "label.tracking-code": "كود التتبع", "label.unique-visitors": "زائرون فريدون", "label.unknown": "غير معروف", - "label.user": "User", + "label.user": "مستخدم", "label.username": "اسم المستخدم", - "label.users": "Users", - "label.view": "View", + "label.users": "المستخدمين", + "label.view": "عرض", "label.view-details": "عرض التفاصيل", "label.views": "مشاهدات", "label.visitors": "زوار", - "label.website-id": "Website ID", + "label.website-id": "معرف الموقع", "label.websites": "المواقع", - "label.yesterday": "Yesterday", + "label.yesterday": "الأمس", "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.error": "حدث خطأ ما.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} في {url}", "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-teams": "لم تقم بإنشاء اي مجموعة.", + "message.no-users": "لا يوجد مستخدمين.", "message.page-not-found": "الصفحة غير موجودة.", "message.reset-website": "اعادة تعيين الإحصائيات", "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.team-already-member": "أنت عضو في المجموعة", + "message.team-not-found": "لم يتم العثور على المجموعة found.", "message.tracking-code": "كود التتبع", - "message.user-deleted": "User deleted.", + "message.user-deleted": "تم حذف المستخدم.", "message.visitor-log": "زائر من {country} يستخدم {browser} على {os} {device}", - "messages.no-team-websites": "This team does not have any websites.", + "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/fr-FR.json b/lang/fr-FR.json index 8a8c4b21..bdc11e38 100644 --- a/lang/fr-FR.json +++ b/lang/fr-FR.json @@ -1,7 +1,7 @@ { - "label.access-code": "Access code", + "label.access-code": "Code d'accès", "label.actions": "Actions", - "label.activity-log": "Activity log", + "label.activity-log": "Journal d'activité", "label.add-website": "Ajouter un site", "label.admin": "Administrateur", "label.all": "Tout", @@ -13,27 +13,27 @@ "label.browsers": "Navigateurs", "label.cancel": "Annuler", "label.change-password": "Changer le mot de passe", - "label.cities": "Cities", - "label.clear-all": "Clear all", - "label.confirm": "Confirm", + "label.cities": "Villes", + "label.clear-all": "Réinitialiser", + "label.confirm": "Confirmer", "label.confirm-password": "Confirmation du mot de passe", - "label.continue": "Continue", + "label.continue": "Continuer", "label.countries": "Pays", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", + "label.create-team": "Créer une équipe", + "label.create-user": "Créer un utilisateur", + "label.created": "Créé", "label.current-password": "Mot de passe actuel", "label.custom-range": "Période personnalisée", "label.dashboard": "Tableau de bord", - "label.data": "Data", + "label.data": "Données", "label.date-range": "Période", "label.default-date-range": "Période par défaut", "label.delete": "Supprimer", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-team": "Supprimer l'équipe", + "label.delete-user": "Supprimer l'utilisateur", "label.delete-website": "Supprimer le site", "label.desktop": "Ordinateur", - "label.details": "Details", + "label.details": "Détails", "label.devices": "Appareils", "label.dismiss": "Ignorer", "label.domain": "Domaine", @@ -43,17 +43,17 @@ "label.events": "Événements", "label.filter-combined": "Combiné", "label.filter-raw": "Brut", - "label.join": "Join", - "label.join-team": "Join team", + "label.join": "Rejoindre", + "label.join-team": "Rejoindre une équipe", "label.language": "Langue", "label.languages": "Langues", "label.laptop": "Portable", "label.last-days": "{x} derniers jours", "label.last-hours": "{x} dernières heures", - "label.leave": "Leave", - "label.leave-team": "Leave team", + "label.leave": "Quitter", + "label.leave-team": "Quitter l'équipe", "label.logout": "Déconnexion", - "label.members": "Members", + "label.members": "Membres", "label.mobile": "Téléphone", "label.more": "Plus", "label.name": "Nom", @@ -71,75 +71,75 @@ "label.realtime": "Temps réel", "label.referrers": "Sources", "label.refresh": "Rafraîchir", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "Régénérer", + "label.regions": "Régions", + "label.remove": "Retirer", "label.required": "Requis", "label.reset": "Réinitialiser", "label.reset-website": "Réinitialiser les statistiques", - "label.role": "Role", - "label.save": "Sauvegarder", + "label.role": "Rôle", + "label.save": "Enregistrer", "label.screens": "Résolutions d'écran", - "label.select-website": "Select website", + "label.select-website": "Choisir un site", "label.sessions": "Sessions", "label.settings": "Paramètres", - "label.share-url": "Partager l'URL", + "label.share-url": "URL de partage", "label.single-day": "Journée", "label.tablet": "Tablette", - "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": "Équipe", + "label.team-guest": "Invité dans l'équipe", + "label.team-id": "ID d'équipe", + "label.team-member": "Membre de l'équipe", + "label.team-owner": "Propriétaire de l'équipe", + "label.teams": "Équipes", "label.theme": "Thème", "label.this-month": "Ce mois", "label.this-week": "Cette semaine", "label.this-year": "Cette année", "label.timezone": "Fuseau horaire", - "label.title": "Title", + "label.title": "Titre", "label.today": "Aujourd'hui", "label.toggle-charts": "Afficher/Masquer les graphiques", "label.tracking-code": "Code de suivi", "label.unique-visitors": "Visiteurs uniques", "label.unknown": "Inconnu", - "label.user": "User", + "label.user": "Utilisateur", "label.username": "Nom d'utilisateur", - "label.users": "Users", - "label.view": "View", - "label.view-details": "Voir les details", + "label.users": "Utilisateurs", + "label.view": "Voir", + "label.view-details": "Voir les détails", "label.views": "Vues", "label.visitors": "Visiteurs", - "label.website-id": "Website ID", + "label.website-id": "ID de site", "label.websites": "Sites", "label.yesterday": "Hier", "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": "Are you sure you want to leave {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-website": "Supprimer le site", - "message.delete-website-warning": "Toutes les données associées seront également supprimées.", + "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.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} sur {url}", "message.go-to-settings": "Aller aux paramètres", "message.incorrect-username-password": "Nom d'utilisateur/Mot de passe incorrect.", "message.invalid-domain": "Domaine invalide", - "message.min-password-length": "Minimum length of {n} characters", - "message.no-data-available": "Pas de données disponibles.", + "message.min-password-length": "Taille minimale de {n} caractères", + "message.no-data-available": "Aucune donnée disponible.", "message.no-match-password": "Les mots de passe ne correspondent pas", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "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-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": "Ceci est l'URL partagée pour {target}.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", + "message.share-url": "Les statistiques de votre site sont accessibles publiquement sur cette URL :", + "message.team-already-member": "Vous êtes déjà membre de cette équipe.", + "message.team-not-found": "Équipe non trouvée.", "message.tracking-code": "Code de suivi", - "message.user-deleted": "User deleted.", + "message.user-deleted": "Utilisateur supprimé.", "message.visitor-log": "Visiteur de {country} utilisant {browser} sur {os} {device}", - "messages.no-team-websites": "This team does not have any websites.", + "messages.no-team-websites": "Cette équipe n'a aucun site.", "messages.no-websites-configured": "Vous n'avez configuré aucun site.", - "messages.team-websites-info": "Websites can be viewed by anyone on the team." + "messages.team-websites-info": "Les sites peuvent être vus par tout utilisateur dans l'équipe." } diff --git a/lang/zh-CN.json b/lang/zh-CN.json index 140c9938..6647faf0 100644 --- a/lang/zh-CN.json +++ b/lang/zh-CN.json @@ -1,39 +1,39 @@ { - "label.access-code": "Access code", + "label.access-code": "访问代码", "label.actions": "用户行为", "label.activity-log": "Activity log", "label.add-website": "添加网站", "label.admin": "管理员", "label.all": "所有", "label.all-time": "所有时间段", - "label.analytics": "Analytics", + "label.analytics": "分析", "label.average-visit-time": "平均访问时间", "label.back": "返回", "label.bounce-rate": "跳出率", "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.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": "域名", @@ -43,17 +43,17 @@ "label.events": "行为类别", "label.filter-combined": "总和", "label.filter-raw": "原始", - "label.join": "Join", - "label.join-team": "Join team", + "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.logout": "退出", - "label.members": "Members", + "label.members": "成员", "label.mobile": "手机", "label.more": "更多", "label.name": "名字", @@ -66,56 +66,56 @@ "label.password": "密码", "label.powered-by": "由 {name} 提供支持", "label.profile": "个人资料", - "label.queries": "Queries", + "label.queries": "查询", "label.query-parameters": "查询参数", "label.realtime": "实时", "label.referrers": "来源域名", "label.refresh": "刷新", - "label.regenerate": "Regenerate", - "label.regions": "Regions", - "label.remove": "Remove", + "label.regenerate": "重新生成", + "label.regions": "地区", + "label.remove": "移除", "label.required": "必填", "label.reset": "重置", "label.reset-website": "重置统计数据", - "label.role": "Role", + "label.role": "角色", "label.save": "保存", "label.screens": "屏幕尺寸", - "label.select-website": "Select website", - "label.sessions": "Sessions", + "label.select-website": "选择网站", + "label.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.user": "用户", "label.username": "用户名", - "label.users": "Users", - "label.view": "View", + "label.users": "用户", + "label.view": "查看", "label.view-details": "查看更多", "label.views": "浏览量", "label.visitors": "访客", - "label.website-id": "Website ID", + "label.website-id": "网站 ID", "label.websites": "网站", "label.yesterday": "昨天", "message.active-users": "当前在线 {x} 人", "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": "所有相关数据将会被删除。", @@ -124,22 +124,22 @@ "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-teams": "你还没有创建任何团队。", + "message.no-users": "没有任何用户。", "message.page-not-found": "网页未找到。", "message.reset-website": "重置统计数据", "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.team-already-member": "你已经是该团队的成员。", + "message.team-not-found": "未找到团队。", "message.tracking-code": "跟踪代码", - "message.user-deleted": "User deleted.", + "message.user-deleted": "User detected.", "message.visitor-log": "来自{country}的访客在搭载 {os} 的{device}上使用 {browser} 浏览器进行访问。", - "messages.no-team-websites": "This team does not have any websites.", + "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/lib/lang.js b/lib/lang.js index 0e294887..134265d2 100644 --- a/lib/lang.js +++ b/lib/lang.js @@ -50,17 +50,20 @@ export const languages = { 'ca-ES': { label: 'Català', dateLocale: ca }, 'cs-CZ': { label: 'Čeština', dateLocale: cs }, 'da-DK': { label: 'Dansk', dateLocale: da }, + 'de-CH': { label: 'Schwiizerdütsch', dateLocale: de }, 'de-DE': { label: 'Deutsch', dateLocale: de }, 'el-GR': { label: 'Ελληνικά', dateLocale: el }, - 'en-US': { label: 'English (US)', dateLocale: enUS }, 'en-GB': { label: 'English (UK)', dateLocale: enGB }, + 'en-US': { label: 'English (US)', dateLocale: enUS }, 'es-MX': { label: 'Español', dateLocale: es }, 'fa-IR': { label: 'فارسی', dateLocale: faIR, dir: 'rtl' }, + 'fi-FI': { label: 'Suomi', dateLocale: fi }, 'fo-FO': { label: 'Føroyskt' }, 'fr-FR': { label: 'Français', dateLocale: fr }, 'ga-ES': { label: 'Galacian (Spain)', dateLocale: es }, 'he-IL': { label: 'עברית', dateLocale: he }, 'hi-IN': { label: 'हिन्दी', dateLocale: hi }, + 'hr-HR': { label: 'Hrvatski', dateLocale: hr }, 'hu-HU': { label: 'Hungarian', dateLocale: hu }, 'id-ID': { label: 'Bahasa Indonesia', dateLocale: id }, 'it-IT': { label: 'Italiano', dateLocale: it }, @@ -75,11 +78,11 @@ export const languages = { 'pl-PL': { label: 'Polski', dateLocale: pl }, 'pt-BR': { label: 'Português do Brasil', dateLocale: ptBR }, 'pt-PT': { label: 'Português', dateLocale: pt }, - 'ru-RU': { label: 'Русский', dateLocale: ru }, 'ro-RO': { label: 'Română', dateLocale: ro }, + 'ru-RU': { label: 'Русский', dateLocale: ru }, + 'si-LK': { label: 'සිංහල', dateLocale: si }, 'sk-SK': { label: 'Slovenčina', dateLocale: sk }, 'sl-SI': { label: 'Slovenščina', dateLocale: sl }, - 'fi-FI': { label: 'Suomi', dateLocale: fi }, 'sv-SE': { label: 'Svenska', dateLocale: sv }, 'de-CH': { label: 'Schwiizerdütsch', dateLocale: de }, 'ta-IN': { label: 'தமிழ்', dateLocale: ta }, diff --git a/next.config.js b/next.config.js index f45d7d81..1132cb73 100644 --- a/next.config.js +++ b/next.config.js @@ -44,6 +44,17 @@ if (process.env.COLLECT_API_ENDPOINT) { }); } +if (process.env.TRACKER_SCRIPT_NAME) { + const match = process.env.TRACKER_SCRIPT_NAME?.match(/\/?(\w+)(\.js)?/); + + if (match) { + rewrites.push({ + source: `/${match[0]}.js`, + destination: '/script.js', + }); + } +} + const redirects = []; if (process.env.CLOUD_MODE) { diff --git a/package.json b/package.json index 59fcc2e0..e1aefe55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "2.0.0", + "version": "2.0.1", "description": "A simple, fast, privacy-focused alternative to Google Analytics.", "author": "Mike Cao ", "license": "MIT", @@ -59,7 +59,7 @@ ], "dependencies": { "@fontsource/inter": "^4.5.15", - "@prisma/client": "4.11.0", + "@prisma/client": "4.13.0", "@tanstack/react-query": "^4.16.1", "@umami/prisma-client": "^0.2.0", "@umami/redis-client": "^0.2.0", @@ -134,7 +134,7 @@ "postcss-preset-env": "7.8.3", "postcss-rtlcss": "^4.0.1", "prettier": "^2.6.2", - "prisma": "4.11.0", + "prisma": "4.13.0", "prompts": "2.4.2", "rollup": "^2.70.1", "rollup-plugin-terser": "^7.0.2", diff --git a/pages/_app.js b/pages/_app.js index fcff10bf..f1a34521 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -52,6 +52,7 @@ export default function App({ Component, pageProps }) { + {!pathname.includes('/share/') &&