mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 13:34:13 +03:00
Fix I18n audio labels (#20508)
* ensure i18n audio label keys are translated don't assume they are in the objects namespace * add missing audio labels * Improve handling of label types * simplify * fixes --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
parent
e592c7044b
commit
75d7049b6d
@ -425,5 +425,79 @@
|
|||||||
"television": "Television",
|
"television": "Television",
|
||||||
"radio": "Radio",
|
"radio": "Radio",
|
||||||
"field_recording": "Field Recording",
|
"field_recording": "Field Recording",
|
||||||
"scream": "Scream"
|
"scream": "Scream",
|
||||||
|
"sodeling": "Sodeling",
|
||||||
|
"chird": "Chird",
|
||||||
|
"change_ringing": "Change Ringing",
|
||||||
|
"shofar": "Shofar",
|
||||||
|
"liquid": "Liquid",
|
||||||
|
"splash": "Splash",
|
||||||
|
"slosh": "Slosh",
|
||||||
|
"squish": "Squish",
|
||||||
|
"drip": "Drip",
|
||||||
|
"pour": "Pour",
|
||||||
|
"trickle": "Trickle",
|
||||||
|
"gush": "Gush",
|
||||||
|
"fill": "Fill",
|
||||||
|
"spray": "Spray",
|
||||||
|
"pump": "Pump",
|
||||||
|
"stir": "Stir",
|
||||||
|
"boiling": "Boiling",
|
||||||
|
"sonar": "Sonar",
|
||||||
|
"arrow": "Arrow",
|
||||||
|
"whoosh": "Whoosh",
|
||||||
|
"thump": "Thump",
|
||||||
|
"thunk": "Thunk",
|
||||||
|
"electronic_tuner": "Electronic Tuner",
|
||||||
|
"effects_unit": "Effects Unit",
|
||||||
|
"chorus_effect": "Chorus Effect",
|
||||||
|
"basketball_bounce": "Basketball Bounce",
|
||||||
|
"bang": "Bang",
|
||||||
|
"slap": "Slap",
|
||||||
|
"whack": "Whack",
|
||||||
|
"smash": "Smash",
|
||||||
|
"breaking": "Breaking",
|
||||||
|
"bouncing": "Bouncing",
|
||||||
|
"whip": "Whip",
|
||||||
|
"flap": "Flap",
|
||||||
|
"scratch": "Scratch",
|
||||||
|
"scrape": "Scrape",
|
||||||
|
"rub": "Rub",
|
||||||
|
"roll": "Roll",
|
||||||
|
"crushing": "Crushing",
|
||||||
|
"crumpling": "Crumpling",
|
||||||
|
"tearing": "Tearing",
|
||||||
|
"beep": "Beep",
|
||||||
|
"ping": "Ping",
|
||||||
|
"ding": "Ding",
|
||||||
|
"clang": "Clang",
|
||||||
|
"squeal": "Squeal",
|
||||||
|
"creak": "Creak",
|
||||||
|
"rustle": "Rustle",
|
||||||
|
"whir": "Whir",
|
||||||
|
"clatter": "Clatter",
|
||||||
|
"sizzle": "Sizzle",
|
||||||
|
"clicking": "Clicking",
|
||||||
|
"clickety_clack": "Clickety Clack",
|
||||||
|
"rumble": "Rumble",
|
||||||
|
"plop": "Plop",
|
||||||
|
"hum": "Hum",
|
||||||
|
"zing": "Zing",
|
||||||
|
"boing": "Boing",
|
||||||
|
"crunch": "Crunch",
|
||||||
|
"sine_wave": "Sine Wave",
|
||||||
|
"harmonic": "Harmonic",
|
||||||
|
"chirp_tone": "Chirp Tone",
|
||||||
|
"pulse": "Pulse",
|
||||||
|
"inside": "Inside",
|
||||||
|
"outside": "Outside",
|
||||||
|
"reverberation": "Reverberation",
|
||||||
|
"echo": "Echo",
|
||||||
|
"noise": "Noise",
|
||||||
|
"mains_hum": "Mains Hum",
|
||||||
|
"distortion": "Distortion",
|
||||||
|
"sidetone": "Sidetone",
|
||||||
|
"cacophony": "Cacophony",
|
||||||
|
"throbbing": "Throbbing",
|
||||||
|
"vibration": "Vibration"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,7 +150,7 @@ export default function SearchThumbnail({
|
|||||||
.filter(
|
.filter(
|
||||||
(item) => item !== undefined && !item.includes("-verified"),
|
(item) => item !== undefined && !item.includes("-verified"),
|
||||||
)
|
)
|
||||||
.map((text) => getTranslatedLabel(text))
|
.map((text) => getTranslatedLabel(text, searchResult.data.type))
|
||||||
.sort()
|
.sort()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
.replaceAll("-verified", "")}
|
.replaceAll("-verified", "")}
|
||||||
|
|||||||
@ -29,6 +29,8 @@ export type SearchSortType =
|
|||||||
| "score_desc"
|
| "score_desc"
|
||||||
| "relevance";
|
| "relevance";
|
||||||
|
|
||||||
|
export type EventType = "object" | "audio" | "manual";
|
||||||
|
|
||||||
export type SearchResult = {
|
export type SearchResult = {
|
||||||
id: string;
|
id: string;
|
||||||
camera: string;
|
camera: string;
|
||||||
@ -54,7 +56,7 @@ export type SearchResult = {
|
|||||||
box: number[];
|
box: number[];
|
||||||
area: number;
|
area: number;
|
||||||
ratio: number;
|
ratio: number;
|
||||||
type: "object" | "audio" | "manual";
|
type: EventType;
|
||||||
description?: string;
|
description?: string;
|
||||||
average_estimated_speed: number;
|
average_estimated_speed: number;
|
||||||
velocity_angle: number;
|
velocity_angle: number;
|
||||||
|
|||||||
@ -1,11 +1,29 @@
|
|||||||
import i18n, { t } from "i18next";
|
import i18n, { t } from "i18next";
|
||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
import HttpBackend from "i18next-http-backend";
|
import HttpBackend from "i18next-http-backend";
|
||||||
|
import { EventType } from "@/types/search";
|
||||||
|
|
||||||
export const getTranslatedLabel = (label: string) => {
|
export const getTranslatedLabel = (
|
||||||
|
label: string,
|
||||||
|
type: EventType = "object",
|
||||||
|
) => {
|
||||||
if (!label) return "";
|
if (!label) return "";
|
||||||
|
|
||||||
return t(`${label.replace(/\s+/g, "_").toLowerCase()}`, { ns: "objects" });
|
if (type === "manual") return label;
|
||||||
|
|
||||||
|
const normalize = (s: string) =>
|
||||||
|
s
|
||||||
|
.trim()
|
||||||
|
.replace(/[-'\s]+/g, "_")
|
||||||
|
.replace(/__+/g, "_")
|
||||||
|
.replace(/^_+|_+$/g, "")
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
const key = normalize(label);
|
||||||
|
|
||||||
|
const ns = type === "audio" ? "audio" : "objects";
|
||||||
|
|
||||||
|
return t(key, { ns });
|
||||||
};
|
};
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||||
import { SearchResult } from "@/types/search";
|
import { EventType, SearchResult } from "@/types/search";
|
||||||
import ImageLoadingIndicator from "@/components/indicators/ImageLoadingIndicator";
|
import ImageLoadingIndicator from "@/components/indicators/ImageLoadingIndicator";
|
||||||
import useImageLoaded from "@/hooks/use-image-loaded";
|
import useImageLoaded from "@/hooks/use-image-loaded";
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
@ -110,7 +110,8 @@ export default function ExploreView({
|
|||||||
key={label}
|
key={label}
|
||||||
searchResults={filteredEvents}
|
searchResults={filteredEvents}
|
||||||
isValidating={isValidating}
|
isValidating={isValidating}
|
||||||
objectType={label}
|
label={label}
|
||||||
|
labelType={filteredEvents[0]?.data?.type || "object"}
|
||||||
setSearchDetail={setSearchDetail}
|
setSearchDetail={setSearchDetail}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
setSimilaritySearch={setSimilaritySearch}
|
setSimilaritySearch={setSimilaritySearch}
|
||||||
@ -122,7 +123,8 @@ export default function ExploreView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ThumbnailRowType = {
|
type ThumbnailRowType = {
|
||||||
objectType: string;
|
label: string;
|
||||||
|
labelType: EventType;
|
||||||
searchResults?: SearchResult[];
|
searchResults?: SearchResult[];
|
||||||
isValidating: boolean;
|
isValidating: boolean;
|
||||||
setSearchDetail: (search: SearchResult | undefined) => void;
|
setSearchDetail: (search: SearchResult | undefined) => void;
|
||||||
@ -132,7 +134,8 @@ type ThumbnailRowType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function ThumbnailRow({
|
function ThumbnailRow({
|
||||||
objectType,
|
label,
|
||||||
|
labelType,
|
||||||
searchResults,
|
searchResults,
|
||||||
isValidating,
|
isValidating,
|
||||||
setSearchDetail,
|
setSearchDetail,
|
||||||
@ -153,7 +156,7 @@ function ThumbnailRow({
|
|||||||
return (
|
return (
|
||||||
<div className="rounded-lg bg-background_alt p-2 md:px-4">
|
<div className="rounded-lg bg-background_alt p-2 md:px-4">
|
||||||
<div className="flex flex-row items-center text-lg smart-capitalize">
|
<div className="flex flex-row items-center text-lg smart-capitalize">
|
||||||
{getTranslatedLabel(objectType)}
|
{getTranslatedLabel(label, labelType)}
|
||||||
{searchResults && (
|
{searchResults && (
|
||||||
<span className="ml-3 text-sm text-secondary-foreground">
|
<span className="ml-3 text-sm text-secondary-foreground">
|
||||||
{t("trackedObjectsCount", {
|
{t("trackedObjectsCount", {
|
||||||
@ -181,7 +184,7 @@ function ThumbnailRow({
|
|||||||
))}
|
))}
|
||||||
<div
|
<div
|
||||||
className="flex cursor-pointer items-center justify-center"
|
className="flex cursor-pointer items-center justify-center"
|
||||||
onClick={() => handleSearch(objectType)}
|
onClick={() => handleSearch(label)}
|
||||||
>
|
>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
@ -192,7 +195,9 @@ function ThumbnailRow({
|
|||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipPortal>
|
<TooltipPortal>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
{t("exploreMore", { label: getTranslatedLabel(objectType) })}
|
{t("exploreMore", {
|
||||||
|
label: getTranslatedLabel(label, labelType),
|
||||||
|
})}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</TooltipPortal>
|
</TooltipPortal>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@ -100,7 +100,12 @@ export default function CameraSettingsView({
|
|||||||
const alertsLabels = useMemo(() => {
|
const alertsLabels = useMemo(() => {
|
||||||
return cameraConfig?.review.alerts.labels
|
return cameraConfig?.review.alerts.labels
|
||||||
? cameraConfig.review.alerts.labels
|
? cameraConfig.review.alerts.labels
|
||||||
.map((label) => getTranslatedLabel(label))
|
.map((label) =>
|
||||||
|
getTranslatedLabel(
|
||||||
|
label,
|
||||||
|
cameraConfig?.audio?.listen?.includes(label) ? "audio" : "object",
|
||||||
|
),
|
||||||
|
)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "";
|
: "";
|
||||||
}, [cameraConfig]);
|
}, [cameraConfig]);
|
||||||
@ -108,7 +113,12 @@ export default function CameraSettingsView({
|
|||||||
const detectionsLabels = useMemo(() => {
|
const detectionsLabels = useMemo(() => {
|
||||||
return cameraConfig?.review.detections.labels
|
return cameraConfig?.review.detections.labels
|
||||||
? cameraConfig.review.detections.labels
|
? cameraConfig.review.detections.labels
|
||||||
.map((label) => getTranslatedLabel(label))
|
.map((label) =>
|
||||||
|
getTranslatedLabel(
|
||||||
|
label,
|
||||||
|
cameraConfig?.audio?.listen?.includes(label) ? "audio" : "object",
|
||||||
|
),
|
||||||
|
)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "";
|
: "";
|
||||||
}, [cameraConfig]);
|
}, [cameraConfig]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user