From bcbdca841b29fc6ad1474250965b17e8fbe53536 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 18 Jul 2026 21:51:55 +0200 Subject: [PATCH] i18n observability hub: analytics pages and log tables. Translate analytics overview/activity/economy plus peak-hours/revenue charts, and audit/chat/commands/trades DataTables (en/nl/it). Includes prior events/polls detail i18n. Co-authored-by: Cursor --- src/app/admin/analytics/activity/page.tsx | 70 +++++++----- src/app/admin/analytics/economy/page.tsx | 69 ++++++------ src/app/admin/analytics/page.tsx | 56 +++++----- src/app/admin/logs/audit/audit-table.tsx | 88 ++++++++------- src/app/admin/logs/chat/chat-table.tsx | 43 +++---- .../admin/logs/commands/commands-table.tsx | 47 ++++---- src/app/admin/logs/page.tsx | 2 +- src/app/admin/logs/trades/trades-table.tsx | 61 +++++----- src/components/admin/peak-hours-heatmap.tsx | 26 +++-- src/components/admin/revenue-chart.tsx | 10 +- src/messages/en.json | 105 +++++++++++++++++- src/messages/it.json | 105 +++++++++++++++++- src/messages/nl.json | 105 +++++++++++++++++- 13 files changed, 576 insertions(+), 211 deletions(-) diff --git a/src/app/admin/analytics/activity/page.tsx b/src/app/admin/analytics/activity/page.tsx index b5831ba7..40bd94fd 100644 --- a/src/app/admin/analytics/activity/page.tsx +++ b/src/app/admin/analytics/activity/page.tsx @@ -1,4 +1,5 @@ import { MessageSquare, Shield, Terminal, Users } from "lucide-react"; +import { getLocale, getTranslations } from "next-intl/server"; import { redirect } from "next/navigation"; import { StatsCard } from "@/components/admin/stats-card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; @@ -39,7 +40,7 @@ async function safeRawCount(sql: Promise<{ count: bigint }[]>): Promise } } -async function getActivityData(): Promise { +async function getActivityData(locale: string): Promise { const now = Math.floor(Date.now() / 1000); const todayStart = now - (now % 86400); const weekAgo = now - 7 * 86400; @@ -50,7 +51,7 @@ async function getActivityData(): Promise { const dayStart = todayStart - i * 86400; const date = new Date(dayStart * 1000); emptyDays.push({ - label: date.toLocaleDateString("en", { + label: date.toLocaleDateString(locale, { weekday: "short", month: "short", day: "numeric", @@ -153,7 +154,7 @@ async function getActivityData(): Promise { const userId = Number(c.userId); return { userId, - username: userMap.get(userId) ?? `User #${userId}`, + username: userMap.get(userId) ?? "", messages: Number(c.messages), }; }); @@ -177,33 +178,35 @@ export default async function ActivityPage() { redirect("/admin"); } - const data = await getActivityData(); + const locale = await getLocale(); + const t = await getTranslations("pages.admin.analytics.activity"); + const data = await getActivityData(locale); return (
} /> } /> } /> } />
@@ -211,18 +214,26 @@ export default async function ActivityPage() {
- Daily Breakdown (7 days) + {t("dailyBreakdownTitle")}
- - - - - + + + + + @@ -230,10 +241,10 @@ export default async function ActivityPage() {
DayChatsCmdsBansRegs{t("colDay")} + {t("colChats")} + + {t("colCmds")} + + {t("colBans")} + + {t("colRegs")} +
{day.label} - {day.chats.toLocaleString()} + {day.chats.toLocaleString(locale)} - {day.commands.toLocaleString()} + {day.commands.toLocaleString(locale)} {day.bans} @@ -249,11 +260,11 @@ export default async function ActivityPage() { - Most Active Users (7 days) + {t("mostActiveUsersTitle")} {data.topChatters.length === 0 ? ( -

No chat activity

+
{t("noChatActivity")}
) : (
{data.topChatters.map((user, i) => ( @@ -265,10 +276,13 @@ export default async function ActivityPage() { {i + 1} - {user.username} + {user.username || + t("userFallback", { id: user.userId })} - {user.messages.toLocaleString()} msgs + {t("msgCount", { + count: user.messages.toLocaleString(locale), + })}
))} diff --git a/src/app/admin/analytics/economy/page.tsx b/src/app/admin/analytics/economy/page.tsx index d9cef481..26d759d5 100644 --- a/src/app/admin/analytics/economy/page.tsx +++ b/src/app/admin/analytics/economy/page.tsx @@ -1,4 +1,5 @@ import { ArrowLeftRight } from "lucide-react"; +import { getLocale, getTranslations } from "next-intl/server"; import { redirect } from "next/navigation"; import { RevenueChart } from "@/components/admin/revenue-chart"; import { StatsCard } from "@/components/admin/stats-card"; @@ -41,7 +42,7 @@ async function safeCount(fn: () => Promise): Promise { } } -async function getEconomyData(): Promise { +async function getEconomyData(locale: string): Promise { const now = Math.floor(Date.now() / 1000); const todayStart = now - (now % 86400); const weekAgo = now - 7 * 86400; @@ -95,7 +96,7 @@ async function getEconomyData(): Promise { const dayStart = todayStart - i * 86400; const date = new Date(dayStart * 1000); revenueData.push({ - label: date.toLocaleDateString("en", { weekday: "short" }), + label: date.toLocaleDateString(locale, { weekday: "short" }), credits: 0, points: 0, }); @@ -153,33 +154,37 @@ export default async function EconomyPage() { redirect("/admin"); } - const data = await getEconomyData(); + const locale = await getLocale(); + const t = await getTranslations("pages.admin.analytics.economy"); + const data = await getEconomyData(locale); return (
} /> } /> } /> } />
@@ -189,24 +194,24 @@ export default async function EconomyPage() {
- Shop Revenue (7d) + {t("shopRevenueTitle")}
- Purchases + {t("purchases")} {data.shopPurchases}
- Credits spent + {t("creditsSpent")} - {data.shopCreditsSpent.toLocaleString()} + {data.shopCreditsSpent.toLocaleString(locale)}
- Points spent + {t("pointsSpent")} - {data.shopPointsSpent.toLocaleString()} + {data.shopPointsSpent.toLocaleString(locale)}
@@ -214,16 +219,18 @@ export default async function EconomyPage() {
- Marketplace + {t("marketplaceTitle")}
- Active listings + + {t("activeListings")} + {data.marketplaceActive}
- Sold (7d) + {t("sold7d")} {data.marketplaceSold}
@@ -231,26 +238,26 @@ export default async function EconomyPage() {
- Currency Distribution + {t("currencyDistributionTitle")}
- Credits + {t("credits")} - {data.totalCredits.toLocaleString()} + {data.totalCredits.toLocaleString(locale)}
- Pixels + {t("pixels")} - {data.totalPixels.toLocaleString()} + {data.totalPixels.toLocaleString(locale)}
- Points + {t("points")} - {data.totalPoints.toLocaleString()} + {data.totalPoints.toLocaleString(locale)}
diff --git a/src/app/admin/analytics/page.tsx b/src/app/admin/analytics/page.tsx index e092ac36..9eff48dd 100644 --- a/src/app/admin/analytics/page.tsx +++ b/src/app/admin/analytics/page.tsx @@ -6,6 +6,7 @@ import { TrendingUp, Users, } from "lucide-react"; +import { getLocale, getTranslations } from "next-intl/server"; import { redirect } from "next/navigation"; import { DashboardChart } from "@/components/admin/dashboard-chart"; import { PeakHoursHeatmap } from "@/components/admin/peak-hours-heatmap"; @@ -53,7 +54,7 @@ async function safeCount(fn: () => Promise): Promise { } } -async function getAnalyticsData(): Promise { +async function getAnalyticsData(locale: string): Promise { const now = Math.floor(Date.now() / 1000); const todayStart = now - (now % 86400); const weekAgo = now - 7 * 86400; @@ -116,7 +117,7 @@ async function getAnalyticsData(): Promise { const bucket = Math.floor(dayStart / 86400); const date = new Date(dayStart * 1000); registrationChart.push({ - label: date.toLocaleDateString("en", { + label: date.toLocaleDateString(locale, { month: "short", day: "numeric", }), @@ -176,43 +177,48 @@ export default async function AnalyticsPage() { redirect("/admin"); } - const data = await getAnalyticsData(); + const locale = await getLocale(); + const t = await getTranslations("pages.admin.analytics"); + const data = await getAnalyticsData(locale); return (
} /> } /> } /> } />
@@ -221,37 +227,37 @@ export default async function AnalyticsPage() { - Chat Activity + {t("chatActivity")}
- {data.weekChatMessages.toLocaleString()} + {data.weekChatMessages.toLocaleString(locale)}

- Messages in the last 7 days + {t("messagesLast7Days")}

- Trades + {t("trades")}
- {data.weekTrades.toLocaleString()} + {data.weekTrades.toLocaleString(locale)}

- Trades in the last 7 days + {t("tradesLast7Days")}

- Retention + {t("retention")} @@ -262,7 +268,7 @@ export default async function AnalyticsPage() { %

- Users online / total + {t("usersOnlineOverTotal")}

diff --git a/src/app/admin/logs/audit/audit-table.tsx b/src/app/admin/logs/audit/audit-table.tsx index 960eca59..d5b048e4 100644 --- a/src/app/admin/logs/audit/audit-table.tsx +++ b/src/app/admin/logs/audit/audit-table.tsx @@ -1,5 +1,6 @@ "use client"; +import { useTranslations } from "next-intl"; import { useState } from "react"; import { DataTable } from "@/components/admin/data-table"; import { Badge } from "@/components/ui/badge"; @@ -17,24 +18,25 @@ interface AuditRow { } function DiffCell({ diff }: { diff: string | null }) { + const t = useTranslations("pages.admin.logs"); const [open, setOpen] = useState(false); - if (!diff) return -; + if (!diff) return {t("emptyValue")}; let parsed: Record; try { parsed = JSON.parse(diff); } catch { - return -; + return {t("emptyValue")}; } const keys = Object.keys(parsed); if (keys.length === 0) - return -; + return {t("emptyValue")}; const summary = keys.length === 1 ? `${keys[0]}: ${String(parsed[keys[0]].from ?? "∅")} → ${String(parsed[keys[0]].to ?? "∅")}` - : `${keys.length} changes`; + : t("audit.changesCount", { count: keys.length }); return (
@@ -66,51 +68,57 @@ function DiffCell({ diff }: { diff: string | null }) { ); } -const columns: DataTableColumn[] = [ - { key: "id", label: "ID", sortable: true }, - { key: "username", label: "User", sortable: true }, - { - key: "action", - label: "Action", - sortable: true, - render: (value) => { - const v = String(value); - let variant: "default" | "secondary" | "destructive" = "secondary"; - if (v.includes("create") || v.includes("add")) variant = "default"; - if (v.includes("delete") || v.includes("remove") || v.includes("ban")) - variant = "destructive"; - return {v}; - }, - }, - { key: "target", label: "Target", sortable: true }, - { - key: "targetId", - label: "Target ID", - render: (value) => {value != null ? String(value) : "-"}, - }, - { - key: "diff", - label: "Details", - render: (_value, row) => , - }, - { - key: "createdAt", - label: "Date", - sortable: true, - render: (value) => {String(value) || "N/A"}, - }, -]; - interface AuditTableProps { data: PaginatedResult; } export function AuditTable({ data }: AuditTableProps) { + const t = useTranslations("pages.admin.logs"); + + const columns: DataTableColumn[] = [ + { key: "id", label: t("colId"), sortable: true }, + { key: "username", label: t("colUser"), sortable: true }, + { + key: "action", + label: t("colAction"), + sortable: true, + render: (value) => { + const v = String(value); + let variant: "default" | "secondary" | "destructive" = "secondary"; + if (v.includes("create") || v.includes("add")) variant = "default"; + if (v.includes("delete") || v.includes("remove") || v.includes("ban")) + variant = "destructive"; + return {v}; + }, + }, + { key: "target", label: t("colTarget"), sortable: true }, + { + key: "targetId", + label: t("audit.colTargetId"), + render: (value) => ( + {value != null ? String(value) : t("emptyValue")} + ), + }, + { + key: "diff", + label: t("audit.colDetails"), + render: (_value, row) => , + }, + { + key: "createdAt", + label: t("audit.colDate"), + sortable: true, + render: (value) => ( + {String(value) || t("audit.notAvailable")} + ), + }, + ]; + return ( ); diff --git a/src/app/admin/logs/chat/chat-table.tsx b/src/app/admin/logs/chat/chat-table.tsx index b796ac50..588acc19 100644 --- a/src/app/admin/logs/chat/chat-table.tsx +++ b/src/app/admin/logs/chat/chat-table.tsx @@ -1,5 +1,6 @@ "use client"; +import { useTranslations } from "next-intl"; import { DataTable } from "@/components/admin/data-table"; import { formatTimestamp } from "@/lib/admin-helpers"; import type { DataTableColumn, PaginatedResult } from "@/types"; @@ -12,35 +13,37 @@ interface ChatLogRow { roomId: number; } -const columns: DataTableColumn[] = [ - { key: "id", label: "ID", sortable: true }, - { key: "username", label: "User" }, - { - key: "message", - label: "Message", - render: (value) => ( - {String(value)} - ), - }, - { - key: "timestamp", - label: "Time", - sortable: true, - render: (value) => {formatTimestamp(Number(value))}, - }, - { key: "roomId", label: "Room ID", sortable: true }, -]; - interface ChatTableProps { data: PaginatedResult; } export function ChatTable({ data }: ChatTableProps) { + const t = useTranslations("pages.admin.logs"); + + const columns: DataTableColumn[] = [ + { key: "id", label: t("colId"), sortable: true }, + { key: "username", label: t("colUser") }, + { + key: "message", + label: t("chat.colMessage"), + render: (value) => ( + {String(value)} + ), + }, + { + key: "timestamp", + label: t("colTime"), + sortable: true, + render: (value) => {formatTimestamp(Number(value))}, + }, + { key: "roomId", label: t("chat.colRoomId"), sortable: true }, + ]; + return ( ); diff --git a/src/app/admin/logs/commands/commands-table.tsx b/src/app/admin/logs/commands/commands-table.tsx index 5229b0d7..690d0efc 100644 --- a/src/app/admin/logs/commands/commands-table.tsx +++ b/src/app/admin/logs/commands/commands-table.tsx @@ -1,5 +1,6 @@ "use client"; +import { useTranslations } from "next-intl"; import { DataTable } from "@/components/admin/data-table"; import { formatTimestamp } from "@/lib/admin-helpers"; import type { DataTableColumn, PaginatedResult } from "@/types"; @@ -12,37 +13,39 @@ interface CommandLogRow { timestamp: number; } -const columns: DataTableColumn[] = [ - { key: "id", label: "ID", sortable: true }, - { key: "username", label: "User" }, - { key: "command", label: "Command", sortable: true }, - { - key: "params", - label: "Params", - render: (value) => ( - - {String(value) || "-"} - - ), - }, - { - key: "timestamp", - label: "Time", - sortable: true, - render: (value) => {formatTimestamp(Number(value))}, - }, -]; - interface CommandsTableProps { data: PaginatedResult; } export function CommandsTable({ data }: CommandsTableProps) { + const t = useTranslations("pages.admin.logs"); + + const columns: DataTableColumn[] = [ + { key: "id", label: t("colId"), sortable: true }, + { key: "username", label: t("colUser") }, + { key: "command", label: t("commands.colCommand"), sortable: true }, + { + key: "params", + label: t("commands.colParams"), + render: (value) => ( + + {String(value) || t("emptyValue")} + + ), + }, + { + key: "timestamp", + label: t("colTime"), + sortable: true, + render: (value) => {formatTimestamp(Number(value))}, + }, + ]; + return ( ); diff --git a/src/app/admin/logs/page.tsx b/src/app/admin/logs/page.tsx index 01f9b9d6..9f0b7483 100644 --- a/src/app/admin/logs/page.tsx +++ b/src/app/admin/logs/page.tsx @@ -88,7 +88,7 @@ export default async function AdminStaffActivities({ when: fromDate(a.createdAt), staff: t("staffWithId", { id: Number(a.userId), - name: nameById.get(Number(a.userId)) ?? "unknown", + name: nameById.get(Number(a.userId)) ?? t("unknownUser"), }), action: a.action, description: a.description, diff --git a/src/app/admin/logs/trades/trades-table.tsx b/src/app/admin/logs/trades/trades-table.tsx index c0ff921f..cd051eb8 100644 --- a/src/app/admin/logs/trades/trades-table.tsx +++ b/src/app/admin/logs/trades/trades-table.tsx @@ -1,5 +1,6 @@ "use client"; +import { useTranslations } from "next-intl"; import { DataTable } from "@/components/admin/data-table"; import { Badge } from "@/components/ui/badge"; import { formatTimestamp } from "@/lib/admin-helpers"; @@ -13,43 +14,47 @@ interface TradeLogRow { timestamp: number; } -const columns: DataTableColumn[] = [ - { key: "id", label: "ID", sortable: true }, - { key: "userOneName", label: "User One" }, - { key: "userTwoName", label: "User Two" }, - { - key: "status", - label: "Status", - sortable: true, - render: (value) => { - const v = String(value); - const variant = - v === "completed" - ? "default" - : v === "cancelled" - ? "destructive" - : "secondary"; - return {v || "unknown"}; - }, - }, - { - key: "timestamp", - label: "Time", - sortable: true, - render: (value) => {formatTimestamp(Number(value))}, - }, -]; - interface TradesTableProps { data: PaginatedResult; } export function TradesTable({ data }: TradesTableProps) { + const t = useTranslations("pages.admin.logs"); + + const columns: DataTableColumn[] = [ + { key: "id", label: t("colId"), sortable: true }, + { key: "userOneName", label: t("trades.colUserOne") }, + { key: "userTwoName", label: t("trades.colUserTwo") }, + { + key: "status", + label: t("trades.colStatus"), + sortable: true, + render: (value) => { + const v = String(value); + const variant = + v === "completed" + ? "default" + : v === "cancelled" + ? "destructive" + : "secondary"; + return ( + {v || t("trades.statusUnknown")} + ); + }, + }, + { + key: "timestamp", + label: t("colTime"), + sortable: true, + render: (value) => {formatTimestamp(Number(value))}, + }, + ]; + return ( ); } diff --git a/src/components/admin/peak-hours-heatmap.tsx b/src/components/admin/peak-hours-heatmap.tsx index a80a598b..8cc5c3e2 100644 --- a/src/components/admin/peak-hours-heatmap.tsx +++ b/src/components/admin/peak-hours-heatmap.tsx @@ -1,5 +1,6 @@ "use client"; +import { useTranslations } from "next-intl"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tooltip, @@ -14,7 +15,7 @@ interface PeakHoursData { max: number; } -const DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; +const DAY_KEYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] as const; const HOURS = Array.from({ length: 24 }, (_, i) => i); function getColor(value: number, max: number) { @@ -28,13 +29,14 @@ function getColor(value: number, max: number) { } export function PeakHoursHeatmap({ data }: { data: PeakHoursData }) { + const t = useTranslations("pages.admin.analytics.peakHours"); + const days = DAY_KEYS.map((key) => t(`days.${key}`)); + return ( - Peak Hours -

- User activity by day & hour (last 7 days) -

+ {t("title")} +

{t("description")}

@@ -51,8 +53,8 @@ export function PeakHoursHeatmap({ data }: { data: PeakHoursData }) { ))}
{/* Rows */} - {DAYS.map((day, dayIdx) => ( -
+ {days.map((day, dayIdx) => ( +
{day} @@ -67,7 +69,7 @@ export function PeakHoursHeatmap({ data }: { data: PeakHoursData }) { /> - {day} {hour}:00 — {count} users + {t("tooltip", { day, hour, count })} ); @@ -77,13 +79,17 @@ export function PeakHoursHeatmap({ data }: { data: PeakHoursData }) { ))} {/* Legend */}
- Less + + {t("less")} +
- More + + {t("more")} +
diff --git a/src/components/admin/revenue-chart.tsx b/src/components/admin/revenue-chart.tsx index 8035fc8f..440e180e 100644 --- a/src/components/admin/revenue-chart.tsx +++ b/src/components/admin/revenue-chart.tsx @@ -1,5 +1,6 @@ "use client"; +import { useTranslations } from "next-intl"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; interface RevenueDataPoint { @@ -9,6 +10,7 @@ interface RevenueDataPoint { } export function RevenueChart({ data }: { data: RevenueDataPoint[] }) { + const t = useTranslations("pages.admin.analytics.economy"); const max = Math.max( 1, ...data.flatMap((point) => [point.credits, point.points]), @@ -16,7 +18,9 @@ export function RevenueChart({ data }: { data: RevenueDataPoint[] }) { return ( - Catalog Spending + + {t("catalogSpendingTitle")} +
@@ -32,14 +36,14 @@ export function RevenueChart({ data }: { data: RevenueDataPoint[] }) { style={{ width: `${Math.max(2, (point.credits / max) * 100)}%`, }} - title={`${point.credits} credits`} + title={t("creditsTooltip", { count: point.credits })} />
diff --git a/src/messages/en.json b/src/messages/en.json index 2416fd0c..a0ef1b7b 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -2026,7 +2026,35 @@ "userPrefix": "user", "pagination": "Page {page} of {pages} · {total} total", "next": "Next", - "prev": "Previous" + "prev": "Previous", + "unknownUser": "unknown", + "emptyValue": "-", + "colTime": "Time", + "audit": { + "colTargetId": "Target ID", + "colDetails": "Details", + "colDate": "Date", + "searchPlaceholder": "Search by action or target…", + "changesCount": "{count} changes", + "notAvailable": "N/A" + }, + "chat": { + "colMessage": "Message", + "colRoomId": "Room ID", + "searchPlaceholder": "Search by username or message…" + }, + "commands": { + "colCommand": "Command", + "colParams": "Params", + "searchPlaceholder": "Search by username, command, or params…" + }, + "trades": { + "colUserOne": "User one", + "colUserTwo": "User two", + "colStatus": "Status", + "statusUnknown": "unknown", + "searchPlaceholder": "Search by username…" + } }, "hubs": { "newsMedia": "News & media", @@ -2487,6 +2515,81 @@ "saving": "Saving…", "cancel": "Cancel" } + }, + "analytics": { + "totalUsers": "Total users", + "onlineNow": "{count} online now", + "weekRegistrations": "Week registrations", + "thisMonth": "{count} this month", + "activeRooms": "Active rooms", + "totalRooms": "{count} total rooms", + "weeklyActivity": "Weekly activity", + "tradesAndBans": "{trades} trades, {bans} bans", + "registrationsChartTitle": "Registrations (30 days)", + "registrationsChartDescription": "New user registrations over the last 30 days", + "chatActivity": "Chat activity", + "messagesLast7Days": "Messages in the last 7 days", + "trades": "Trades", + "tradesLast7Days": "Trades in the last 7 days", + "retention": "Retention", + "usersOnlineOverTotal": "Users online / total", + "activity": { + "chatMessages": "Chat messages", + "commands": "Commands", + "bans": "Bans", + "registrations": "Registrations", + "last7Days": "Last 7 days", + "dailyBreakdownTitle": "Daily breakdown (7 days)", + "colDay": "Day", + "colChats": "Chats", + "colCmds": "Cmds", + "colBans": "Bans", + "colRegs": "Regs", + "mostActiveUsersTitle": "Most active users (7 days)", + "noChatActivity": "No chat activity", + "msgCount": "{count} msgs", + "userFallback": "User #{id}" + }, + "economy": { + "creditsInCirculation": "Credits in circulation", + "creditsDescription": "Total credits across all users", + "pixelsInCirculation": "Pixels in circulation", + "pixelsDescription": "Total pixels across all users", + "pointsInCirculation": "Points in circulation", + "pointsDescription": "Total points across all users", + "weeklyTrades": "Weekly trades", + "marketplaceListings": "{count} marketplace listings", + "shopRevenueTitle": "Shop revenue (7d)", + "purchases": "Purchases", + "creditsSpent": "Credits spent", + "pointsSpent": "Points spent", + "marketplaceTitle": "Marketplace", + "activeListings": "Active listings", + "sold7d": "Sold (7d)", + "currencyDistributionTitle": "Currency distribution", + "credits": "Credits", + "pixels": "Pixels", + "points": "Points", + "catalogSpendingTitle": "Catalog spending", + "creditsTooltip": "{count} credits", + "pointsTooltip": "{count} points" + }, + "peakHours": { + "title": "Peak hours", + "description": "User activity by day & hour (last 7 days)", + "days": { + "mon": "Mon", + "tue": "Tue", + "wed": "Wed", + "thu": "Thu", + "fri": "Fri", + "sat": "Sat", + "sun": "Sun" + }, + "tooltip": "{day} {hour}:00 — {count} users", + "less": "Less", + "more": "More" + } } } } diff --git a/src/messages/it.json b/src/messages/it.json index 9fc18306..b47c234a 100644 --- a/src/messages/it.json +++ b/src/messages/it.json @@ -1991,7 +1991,35 @@ "userPrefix": "user", "pagination": "Page {page} of {pages} · {total} total", "next": "Next", - "prev": "Previous" + "prev": "Previous", + "unknownUser": "sconosciuto", + "emptyValue": "-", + "colTime": "Ora", + "audit": { + "colTargetId": "ID destinazione", + "colDetails": "Dettagli", + "colDate": "Data", + "searchPlaceholder": "Cerca per azione o destinazione…", + "changesCount": "{count} modifiche", + "notAvailable": "N/D" + }, + "chat": { + "colMessage": "Messaggio", + "colRoomId": "ID stanza", + "searchPlaceholder": "Cerca per username o messaggio…" + }, + "commands": { + "colCommand": "Comando", + "colParams": "Parametri", + "searchPlaceholder": "Cerca per username, comando o parametri…" + }, + "trades": { + "colUserOne": "Utente uno", + "colUserTwo": "Utente due", + "colStatus": "Stato", + "statusUnknown": "sconosciuto", + "searchPlaceholder": "Cerca per username…" + } }, "wordfilter": { "title": "Filtro parole", @@ -2453,6 +2481,81 @@ "infoBox": "Configuration settings (ports, timeouts, IDs) do not appear here — use the full settings link. Saved changes are synced to the emulator via RCON.", "fullSettings": "Full settings" } + }, + "analytics": { + "totalUsers": "Utenti totali", + "onlineNow": "{count} online ora", + "weekRegistrations": "Registrazioni della settimana", + "thisMonth": "{count} questo mese", + "activeRooms": "Stanze attive", + "totalRooms": "{count} stanze totali", + "weeklyActivity": "Attività settimanale", + "tradesAndBans": "{trades} trade, {bans} ban", + "registrationsChartTitle": "Registrazioni (30 giorni)", + "registrationsChartDescription": "Nuove registrazioni utenti negli ultimi 30 giorni", + "chatActivity": "Attività chat", + "messagesLast7Days": "Messaggi negli ultimi 7 giorni", + "trades": "Trade", + "tradesLast7Days": "Trade negli ultimi 7 giorni", + "retention": "Retention", + "usersOnlineOverTotal": "Utenti online / totale", + "activity": { + "chatMessages": "Messaggi chat", + "commands": "Comandi", + "bans": "Ban", + "registrations": "Registrazioni", + "last7Days": "Ultimi 7 giorni", + "dailyBreakdownTitle": "Ripartizione giornaliera (7 giorni)", + "colDay": "Giorno", + "colChats": "Chat", + "colCmds": "Cmd", + "colBans": "Ban", + "colRegs": "Reg", + "mostActiveUsersTitle": "Utenti più attivi (7 giorni)", + "noChatActivity": "Nessuna attività chat", + "msgCount": "{count} msg", + "userFallback": "Utente #{id}" + }, + "economy": { + "creditsInCirculation": "Crediti in circolazione", + "creditsDescription": "Crediti totali di tutti gli utenti", + "pixelsInCirculation": "Pixel in circolazione", + "pixelsDescription": "Pixel totali di tutti gli utenti", + "pointsInCirculation": "Punti in circolazione", + "pointsDescription": "Punti totali di tutti gli utenti", + "weeklyTrades": "Trade settimanali", + "marketplaceListings": "{count} annunci marketplace", + "shopRevenueTitle": "Ricavi shop (7g)", + "purchases": "Acquisti", + "creditsSpent": "Crediti spesi", + "pointsSpent": "Punti spesi", + "marketplaceTitle": "Marketplace", + "activeListings": "Annunci attivi", + "sold7d": "Venduti (7g)", + "currencyDistributionTitle": "Distribuzione valute", + "credits": "Crediti", + "pixels": "Pixel", + "points": "Punti", + "catalogSpendingTitle": "Spesa catalogo", + "creditsTooltip": "{count} crediti", + "pointsTooltip": "{count} punti" + }, + "peakHours": { + "title": "Ore di punta", + "description": "Attività utenti per giorno e ora (ultimi 7 giorni)", + "days": { + "mon": "lun", + "tue": "mar", + "wed": "mer", + "thu": "gio", + "fri": "ven", + "sat": "sab", + "sun": "dom" + }, + "tooltip": "{day} {hour}:00 — {count} utenti", + "less": "Meno", + "more": "Più" + } } }, "client": { diff --git a/src/messages/nl.json b/src/messages/nl.json index 606ea234..1a728e75 100644 --- a/src/messages/nl.json +++ b/src/messages/nl.json @@ -1973,7 +1973,35 @@ "colTimestamp": "Tijdstempel", "filterByUser": "Filteren op gebruiker…", "filterByAction": "Filteren op actie…", - "clearFilters": "Filters wissen" + "clearFilters": "Filters wissen", + "unknownUser": "onbekend", + "emptyValue": "-", + "colTime": "Tijd", + "audit": { + "colTargetId": "Doel-ID", + "colDetails": "Details", + "colDate": "Datum", + "searchPlaceholder": "Zoek op actie of doel…", + "changesCount": "{count} wijzigingen", + "notAvailable": "n.v.t." + }, + "chat": { + "colMessage": "Bericht", + "colRoomId": "Kamer-ID", + "searchPlaceholder": "Zoek op gebruikersnaam of bericht…" + }, + "commands": { + "colCommand": "Commando", + "colParams": "Parameters", + "searchPlaceholder": "Zoek op gebruikersnaam, commando of parameters…" + }, + "trades": { + "colUserOne": "Gebruiker één", + "colUserTwo": "Gebruiker twee", + "colStatus": "Status", + "statusUnknown": "onbekend", + "searchPlaceholder": "Zoek op gebruikersnaam…" + } }, "wordfilter": { "title": "Woordfilter", @@ -2450,6 +2478,81 @@ "colReported": "Gerapporteerd", "colMod": "Mod", "colStatus": "Status" + }, + "analytics": { + "totalUsers": "Totaal gebruikers", + "onlineNow": "{count} nu online", + "weekRegistrations": "Registraties deze week", + "thisMonth": "{count} deze maand", + "activeRooms": "Actieve kamers", + "totalRooms": "{count} kamers totaal", + "weeklyActivity": "Wekelijkse activiteit", + "tradesAndBans": "{trades} trades, {bans} bans", + "registrationsChartTitle": "Registraties (30 dagen)", + "registrationsChartDescription": "Nieuwe gebruikersregistraties van de laatste 30 dagen", + "chatActivity": "Chatactiviteit", + "messagesLast7Days": "Berichten in de laatste 7 dagen", + "trades": "Trades", + "tradesLast7Days": "Trades in de laatste 7 dagen", + "retention": "Retentie", + "usersOnlineOverTotal": "Gebruikers online / totaal", + "activity": { + "chatMessages": "Chatberichten", + "commands": "Commando's", + "bans": "Bans", + "registrations": "Registraties", + "last7Days": "Laatste 7 dagen", + "dailyBreakdownTitle": "Dagelijkse uitsplitsing (7 dagen)", + "colDay": "Dag", + "colChats": "Chats", + "colCmds": "Cmds", + "colBans": "Bans", + "colRegs": "Regs", + "mostActiveUsersTitle": "Meest actieve gebruikers (7 dagen)", + "noChatActivity": "Geen chatactiviteit", + "msgCount": "{count} msgs", + "userFallback": "Gebruiker #{id}" + }, + "economy": { + "creditsInCirculation": "Credits in omloop", + "creditsDescription": "Totaal credits van alle gebruikers", + "pixelsInCirculation": "Pixels in omloop", + "pixelsDescription": "Totaal pixels van alle gebruikers", + "pointsInCirculation": "Punten in omloop", + "pointsDescription": "Totaal punten van alle gebruikers", + "weeklyTrades": "Wekelijkse trades", + "marketplaceListings": "{count} marketplace-advertenties", + "shopRevenueTitle": "Shopomzet (7d)", + "purchases": "Aankopen", + "creditsSpent": "Credits uitgegeven", + "pointsSpent": "Punten uitgegeven", + "marketplaceTitle": "Marketplace", + "activeListings": "Actieve advertenties", + "sold7d": "Verkocht (7d)", + "currencyDistributionTitle": "Valutaverdeling", + "credits": "Credits", + "pixels": "Pixels", + "points": "Punten", + "catalogSpendingTitle": "Catalogusuitgaven", + "creditsTooltip": "{count} credits", + "pointsTooltip": "{count} punten" + }, + "peakHours": { + "title": "Piekuren", + "description": "Gebruikersactiviteit per dag & uur (laatste 7 dagen)", + "days": { + "mon": "ma", + "tue": "di", + "wed": "wo", + "thu": "do", + "fri": "vr", + "sat": "za", + "sun": "zo" + }, + "tooltip": "{day} {hour}:00 — {count} gebruikers", + "less": "Minder", + "more": "Meer" + } } } }