diff --git a/web/public/locales/en/common.json b/web/public/locales/en/common.json index aa841c30b5..2ae6297a12 100644 --- a/web/public/locales/en/common.json +++ b/web/public/locales/en/common.json @@ -101,7 +101,8 @@ "show": "Show {{item}}", "ID": "ID", "none": "None", - "all": "All" + "all": "All", + "other": "Other" }, "list": { "two": "{{0}} and {{1}}", diff --git a/web/public/locales/en/views/system.json b/web/public/locales/en/views/system.json index ada23cdc26..da774e3022 100644 --- a/web/public/locales/en/views/system.json +++ b/web/public/locales/en/views/system.json @@ -86,7 +86,14 @@ "otherProcesses": { "title": "Other Processes", "processCpuUsage": "Process CPU Usage", - "processMemoryUsage": "Process Memory Usage" + "processMemoryUsage": "Process Memory Usage", + "series": { + "go2rtc": "go2rtc", + "recording": "recording", + "review_segment": "review segment", + "embeddings": "embeddings", + "audio_detector": "audio detector" + } } }, "storage": { diff --git a/web/src/components/card/ClassificationCard.tsx b/web/src/components/card/ClassificationCard.tsx index a8ed12f241..ffd28a0d06 100644 --- a/web/src/components/card/ClassificationCard.tsx +++ b/web/src/components/card/ClassificationCard.tsx @@ -166,7 +166,7 @@ export const ClassificationCard = forwardRef<
{data.name == "unknown" ? t("details.unknown") - : data.name == "none" + : data.name.toLowerCase() == "none" ? t("details.none") : data.name}
diff --git a/web/src/components/overlay/SetPasswordDialog.tsx b/web/src/components/overlay/SetPasswordDialog.tsx index c6d861a6ba..7708201aa7 100644 --- a/web/src/components/overlay/SetPasswordDialog.tsx +++ b/web/src/components/overlay/SetPasswordDialog.tsx @@ -22,6 +22,7 @@ import { useTranslation } from "react-i18next"; import { useDocDomain } from "@/hooks/use-doc-domain"; import useSWR from "swr"; import { formatSecondsToDuration } from "@/utils/dateUtil"; +import { useDateLocale } from "@/hooks/use-date-locale"; import ActivityIndicator from "../indicators/activity-indicator"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; @@ -48,12 +49,13 @@ export default function SetPasswordDialog({ const { t } = useTranslation(["views/settings", "common"]); const { getLocaleDocUrl } = useDocDomain(); const isAdmin = useIsAdmin(); + const dateLocale = useDateLocale(); const { data: config } = useSWR("config"); const refreshSeconds: number | undefined = config?.auth?.refresh_time ?? undefined; const refreshTimeLabel = refreshSeconds - ? formatSecondsToDuration(refreshSeconds) + ? formatSecondsToDuration(refreshSeconds, dateLocale) : t("time.30minutes", { ns: "common" }); // visibility toggles for password fields diff --git a/web/src/components/overlay/detail/TrackingDetails.tsx b/web/src/components/overlay/detail/TrackingDetails.tsx index 80471b8bdf..f2eb111437 100644 --- a/web/src/components/overlay/detail/TrackingDetails.tsx +++ b/web/src/components/overlay/detail/TrackingDetails.tsx @@ -266,7 +266,7 @@ export function TrackingDetails({ const label = event.sub_label ? event.sub_label - : getTranslatedLabel(event.label); + : getTranslatedLabel(event.label, event.data.type); const getZoneColor = useCallback( (zoneName: string) => { @@ -998,7 +998,7 @@ function LifecycleIconRow({
{formattedEventTimestamp}
- {((isAdmin && config?.plus?.enabled) || item.data.box) && ( + {isAdmin && config?.plus?.enabled && item.data.box && (
diff --git a/web/src/components/player/LivePlayer.tsx b/web/src/components/player/LivePlayer.tsx index 9500688f57..ed359a0c92 100644 --- a/web/src/components/player/LivePlayer.tsx +++ b/web/src/components/player/LivePlayer.tsx @@ -16,7 +16,6 @@ import { } from "@/types/live"; import { getIconForLabel } from "@/utils/iconUtil"; import Chip from "../indicators/Chip"; -import { capitalizeFirstLetter } from "@/utils/stringUtil"; import { cn } from "@/lib/utils"; import { TbExclamationCircle } from "react-icons/tb"; import { TooltipPortal } from "@radix-ui/react-tooltip"; @@ -26,6 +25,8 @@ import { LuVideoOff } from "react-icons/lu"; import { Trans, useTranslation } from "react-i18next"; import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name"; import { ImageShadowOverlay } from "../overlay/ImageShadowOverlay"; +import { getTranslatedLabel } from "@/utils/i18n"; +import { formatList } from "@/utils/stringUtil"; type LivePlayerProps = { cameraRef?: (ref: HTMLDivElement | null) => void; @@ -367,20 +368,22 @@ export default function LivePlayer({
- {[ - ...new Set([ - ...(objects || []).map(({ label, sub_label }) => - label.endsWith("verified") - ? sub_label - : label.replaceAll("_", " "), - ), - ]), - ] - .filter((label) => label?.includes("-verified") == false) - .map((label) => capitalizeFirstLetter(label)) - .sort() - .join(", ") - .replaceAll("-verified", "")} + {formatList( + [ + ...new Set([ + ...(objects || []).map(({ label, sub_label }) => + label.endsWith("verified") + ? sub_label + : label.replaceAll("_", " "), + ), + ]), + ] + .filter((label) => label?.includes("-verified") == false) + .map((label) => + getTranslatedLabel(label.replace("-verified", "")), + ) + .sort(), + )} diff --git a/web/src/components/settings/wizard/Step1NameCamera.tsx b/web/src/components/settings/wizard/Step1NameCamera.tsx index eb0dbe9fe8..741aa4b052 100644 --- a/web/src/components/settings/wizard/Step1NameCamera.tsx +++ b/web/src/components/settings/wizard/Step1NameCamera.tsx @@ -417,7 +417,9 @@ export default function Step1NameCamera({ {CAMERA_BRANDS.map((brand) => ( - {brand.label} + {brand.label.toLowerCase() === "other" + ? t("label.other", { ns: "common" }) + : brand.label} ))} diff --git a/web/src/utils/dateUtil.ts b/web/src/utils/dateUtil.ts index db6e0b1cb7..b007a9573f 100644 --- a/web/src/utils/dateUtil.ts +++ b/web/src/utils/dateUtil.ts @@ -1,5 +1,5 @@ import { fromUnixTime, intervalToDuration, formatDuration } from "date-fns"; -import { Locale } from "date-fns/locale"; +import { enUS, Locale } from "date-fns/locale"; import { formatInTimeZone } from "date-fns-tz"; import i18n from "@/utils/i18n"; export const longToDate = (long: number): Date => new Date(long * 1000); @@ -293,9 +293,13 @@ export const getDurationFromTimestamps = ( /** * * @param seconds - number of seconds to convert into hours, minutes and seconds + * @param locale - the date-fns locale to use for formatting * @returns string - formatted duration in hours, minutes and seconds */ -export const formatSecondsToDuration = (seconds: number): string => { +export const formatSecondsToDuration = ( + seconds: number, + locale?: Locale, +): string => { if (isNaN(seconds) || seconds < 0) { return "Invalid duration"; } @@ -304,6 +308,7 @@ export const formatSecondsToDuration = (seconds: number): string => { return formatDuration(duration, { format: ["hours", "minutes", "seconds"], delimiter: ", ", + locale: locale ?? enUS, }); }; diff --git a/web/src/utils/lifecycleUtil.ts b/web/src/utils/lifecycleUtil.ts index 7ed90c5f8f..4e43de9c2f 100644 --- a/web/src/utils/lifecycleUtil.ts +++ b/web/src/utils/lifecycleUtil.ts @@ -12,7 +12,10 @@ export function getLifecycleItemDescription( const label = lifecycleItem.data.sub_label ? capitalizeFirstLetter(rawLabel) - : getTranslatedLabel(rawLabel); + : getTranslatedLabel( + rawLabel, + lifecycleItem.class_type === "heard" ? "audio" : "object", + ); switch (lifecycleItem.class_type) { case "visible": diff --git a/web/src/views/system/GeneralMetrics.tsx b/web/src/views/system/GeneralMetrics.tsx index a05b1b82ac..f8ce648515 100644 --- a/web/src/views/system/GeneralMetrics.tsx +++ b/web/src/views/system/GeneralMetrics.tsx @@ -855,7 +855,7 @@ export default function GeneralMetrics({