mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-01-22 12:08:29 +03:00
I18N Miscellaneous Fixes (#21573)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* fix: fix classification none tag i18n wrong * fix: fix set password dialog jwt time i18n wrong * fix: fix wizard other camera i18n * fix: fix explore tracking detail audio i18n * feat: add system processes info i18n * fix: fix live page label i18n
This commit is contained in:
parent
74d14cb8ca
commit
f3543cfee2
@ -101,7 +101,8 @@
|
|||||||
"show": "Show {{item}}",
|
"show": "Show {{item}}",
|
||||||
"ID": "ID",
|
"ID": "ID",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"all": "All"
|
"all": "All",
|
||||||
|
"other": "Other"
|
||||||
},
|
},
|
||||||
"list": {
|
"list": {
|
||||||
"two": "{{0}} and {{1}}",
|
"two": "{{0}} and {{1}}",
|
||||||
|
|||||||
@ -86,7 +86,14 @@
|
|||||||
"otherProcesses": {
|
"otherProcesses": {
|
||||||
"title": "Other Processes",
|
"title": "Other Processes",
|
||||||
"processCpuUsage": "Process CPU Usage",
|
"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": {
|
"storage": {
|
||||||
|
|||||||
@ -166,7 +166,7 @@ export const ClassificationCard = forwardRef<
|
|||||||
<div className="break-all smart-capitalize">
|
<div className="break-all smart-capitalize">
|
||||||
{data.name == "unknown"
|
{data.name == "unknown"
|
||||||
? t("details.unknown")
|
? t("details.unknown")
|
||||||
: data.name == "none"
|
: data.name.toLowerCase() == "none"
|
||||||
? t("details.none")
|
? t("details.none")
|
||||||
: data.name}
|
: data.name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { formatSecondsToDuration } from "@/utils/dateUtil";
|
import { formatSecondsToDuration } from "@/utils/dateUtil";
|
||||||
|
import { useDateLocale } from "@/hooks/use-date-locale";
|
||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@ -48,12 +49,13 @@ export default function SetPasswordDialog({
|
|||||||
const { t } = useTranslation(["views/settings", "common"]);
|
const { t } = useTranslation(["views/settings", "common"]);
|
||||||
const { getLocaleDocUrl } = useDocDomain();
|
const { getLocaleDocUrl } = useDocDomain();
|
||||||
const isAdmin = useIsAdmin();
|
const isAdmin = useIsAdmin();
|
||||||
|
const dateLocale = useDateLocale();
|
||||||
|
|
||||||
const { data: config } = useSWR("config");
|
const { data: config } = useSWR("config");
|
||||||
const refreshSeconds: number | undefined =
|
const refreshSeconds: number | undefined =
|
||||||
config?.auth?.refresh_time ?? undefined;
|
config?.auth?.refresh_time ?? undefined;
|
||||||
const refreshTimeLabel = refreshSeconds
|
const refreshTimeLabel = refreshSeconds
|
||||||
? formatSecondsToDuration(refreshSeconds)
|
? formatSecondsToDuration(refreshSeconds, dateLocale)
|
||||||
: t("time.30minutes", { ns: "common" });
|
: t("time.30minutes", { ns: "common" });
|
||||||
|
|
||||||
// visibility toggles for password fields
|
// visibility toggles for password fields
|
||||||
|
|||||||
@ -266,7 +266,7 @@ export function TrackingDetails({
|
|||||||
|
|
||||||
const label = event.sub_label
|
const label = event.sub_label
|
||||||
? event.sub_label
|
? event.sub_label
|
||||||
: getTranslatedLabel(event.label);
|
: getTranslatedLabel(event.label, event.data.type);
|
||||||
|
|
||||||
const getZoneColor = useCallback(
|
const getZoneColor = useCallback(
|
||||||
(zoneName: string) => {
|
(zoneName: string) => {
|
||||||
@ -998,7 +998,7 @@ function LifecycleIconRow({
|
|||||||
<div className="ml-3 flex-shrink-0 px-1 text-right text-xs text-primary-variant">
|
<div className="ml-3 flex-shrink-0 px-1 text-right text-xs text-primary-variant">
|
||||||
<div className="flex flex-row items-center gap-3">
|
<div className="flex flex-row items-center gap-3">
|
||||||
<div className="whitespace-nowrap">{formattedEventTimestamp}</div>
|
<div className="whitespace-nowrap">{formattedEventTimestamp}</div>
|
||||||
{((isAdmin && config?.plus?.enabled) || item.data.box) && (
|
{isAdmin && config?.plus?.enabled && item.data.box && (
|
||||||
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
|
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DropdownMenuTrigger>
|
<DropdownMenuTrigger>
|
||||||
<div className="rounded p-1 pr-2" role="button">
|
<div className="rounded p-1 pr-2" role="button">
|
||||||
|
|||||||
@ -16,7 +16,6 @@ import {
|
|||||||
} from "@/types/live";
|
} from "@/types/live";
|
||||||
import { getIconForLabel } from "@/utils/iconUtil";
|
import { getIconForLabel } from "@/utils/iconUtil";
|
||||||
import Chip from "../indicators/Chip";
|
import Chip from "../indicators/Chip";
|
||||||
import { capitalizeFirstLetter } from "@/utils/stringUtil";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { TbExclamationCircle } from "react-icons/tb";
|
import { TbExclamationCircle } from "react-icons/tb";
|
||||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||||
@ -26,6 +25,8 @@ import { LuVideoOff } from "react-icons/lu";
|
|||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
|
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
|
||||||
import { ImageShadowOverlay } from "../overlay/ImageShadowOverlay";
|
import { ImageShadowOverlay } from "../overlay/ImageShadowOverlay";
|
||||||
|
import { getTranslatedLabel } from "@/utils/i18n";
|
||||||
|
import { formatList } from "@/utils/stringUtil";
|
||||||
|
|
||||||
type LivePlayerProps = {
|
type LivePlayerProps = {
|
||||||
cameraRef?: (ref: HTMLDivElement | null) => void;
|
cameraRef?: (ref: HTMLDivElement | null) => void;
|
||||||
@ -367,20 +368,22 @@ export default function LivePlayer({
|
|||||||
</div>
|
</div>
|
||||||
<TooltipPortal>
|
<TooltipPortal>
|
||||||
<TooltipContent className="smart-capitalize">
|
<TooltipContent className="smart-capitalize">
|
||||||
{[
|
{formatList(
|
||||||
...new Set([
|
[
|
||||||
...(objects || []).map(({ label, sub_label }) =>
|
...new Set([
|
||||||
label.endsWith("verified")
|
...(objects || []).map(({ label, sub_label }) =>
|
||||||
? sub_label
|
label.endsWith("verified")
|
||||||
: label.replaceAll("_", " "),
|
? sub_label
|
||||||
),
|
: label.replaceAll("_", " "),
|
||||||
]),
|
),
|
||||||
]
|
]),
|
||||||
.filter((label) => label?.includes("-verified") == false)
|
]
|
||||||
.map((label) => capitalizeFirstLetter(label))
|
.filter((label) => label?.includes("-verified") == false)
|
||||||
.sort()
|
.map((label) =>
|
||||||
.join(", ")
|
getTranslatedLabel(label.replace("-verified", "")),
|
||||||
.replaceAll("-verified", "")}
|
)
|
||||||
|
.sort(),
|
||||||
|
)}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</TooltipPortal>
|
</TooltipPortal>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@ -417,7 +417,9 @@ export default function Step1NameCamera({
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
{CAMERA_BRANDS.map((brand) => (
|
{CAMERA_BRANDS.map((brand) => (
|
||||||
<SelectItem key={brand.value} value={brand.value}>
|
<SelectItem key={brand.value} value={brand.value}>
|
||||||
{brand.label}
|
{brand.label.toLowerCase() === "other"
|
||||||
|
? t("label.other", { ns: "common" })
|
||||||
|
: brand.label}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { fromUnixTime, intervalToDuration, formatDuration } from "date-fns";
|
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 { formatInTimeZone } from "date-fns-tz";
|
||||||
import i18n from "@/utils/i18n";
|
import i18n from "@/utils/i18n";
|
||||||
export const longToDate = (long: number): Date => new Date(long * 1000);
|
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 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
|
* @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) {
|
if (isNaN(seconds) || seconds < 0) {
|
||||||
return "Invalid duration";
|
return "Invalid duration";
|
||||||
}
|
}
|
||||||
@ -304,6 +308,7 @@ export const formatSecondsToDuration = (seconds: number): string => {
|
|||||||
return formatDuration(duration, {
|
return formatDuration(duration, {
|
||||||
format: ["hours", "minutes", "seconds"],
|
format: ["hours", "minutes", "seconds"],
|
||||||
delimiter: ", ",
|
delimiter: ", ",
|
||||||
|
locale: locale ?? enUS,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,10 @@ export function getLifecycleItemDescription(
|
|||||||
|
|
||||||
const label = lifecycleItem.data.sub_label
|
const label = lifecycleItem.data.sub_label
|
||||||
? capitalizeFirstLetter(rawLabel)
|
? capitalizeFirstLetter(rawLabel)
|
||||||
: getTranslatedLabel(rawLabel);
|
: getTranslatedLabel(
|
||||||
|
rawLabel,
|
||||||
|
lifecycleItem.class_type === "heard" ? "audio" : "object",
|
||||||
|
);
|
||||||
|
|
||||||
switch (lifecycleItem.class_type) {
|
switch (lifecycleItem.class_type) {
|
||||||
case "visible":
|
case "visible":
|
||||||
|
|||||||
@ -855,7 +855,7 @@ export default function GeneralMetrics({
|
|||||||
<ThresholdBarGraph
|
<ThresholdBarGraph
|
||||||
key={series.name}
|
key={series.name}
|
||||||
graphId={`${series.name}-cpu`}
|
graphId={`${series.name}-cpu`}
|
||||||
name={series.name.replaceAll("_", " ")}
|
name={t(`general.otherProcesses.series.${series.name}`)}
|
||||||
unit="%"
|
unit="%"
|
||||||
threshold={DetectorCpuThreshold}
|
threshold={DetectorCpuThreshold}
|
||||||
updateTimes={updateTimes}
|
updateTimes={updateTimes}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user