mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-30 13:48:07 +02:00
fix missing i18n keys (#18309)
This commit is contained in:
parent
8a143b4284
commit
afe513336c
@ -78,6 +78,10 @@
|
|||||||
"speed": {
|
"speed": {
|
||||||
"mph": "mph",
|
"mph": "mph",
|
||||||
"kph": "kph"
|
"kph": "kph"
|
||||||
|
},
|
||||||
|
"length": {
|
||||||
|
"feet": "feet",
|
||||||
|
"meters": "meters"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"label": {
|
"label": {
|
||||||
|
@ -39,8 +39,11 @@
|
|||||||
"document": "Read the documentation "
|
"document": "Read the documentation "
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stream": "Stream",
|
||||||
|
"placeholder": "Choose a stream",
|
||||||
"streamMethod": {
|
"streamMethod": {
|
||||||
"label": "Streaming Method",
|
"label": "Streaming Method",
|
||||||
|
"placeholder": "Choose a streaming method",
|
||||||
"method": {
|
"method": {
|
||||||
"noStreaming": {
|
"noStreaming": {
|
||||||
"label": "No Streaming",
|
"label": "No Streaming",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"copyConfig": "Copy Config",
|
"copyConfig": "Copy Config",
|
||||||
"saveAndRestart": "Save & Restart",
|
"saveAndRestart": "Save & Restart",
|
||||||
"saveOnly": "Save Only",
|
"saveOnly": "Save Only",
|
||||||
|
"confirm": "Exit without saving?",
|
||||||
"toast": {
|
"toast": {
|
||||||
"success": {
|
"success": {
|
||||||
"copyToClipboard": "Config copied to clipboard."
|
"copyToClipboard": "Config copied to clipboard."
|
||||||
|
@ -75,7 +75,10 @@
|
|||||||
"desc": "This data comes from your camera's detect feed but is overlayed on images from the the record feed. It is unlikely that the two streams are perfectly in sync. As a result, the bounding box and the footage will not line up perfectly. However, the <code>annotation_offset</code> field can be used to adjust this.",
|
"desc": "This data comes from your camera's detect feed but is overlayed on images from the the record feed. It is unlikely that the two streams are perfectly in sync. As a result, the bounding box and the footage will not line up perfectly. However, the <code>annotation_offset</code> field can be used to adjust this.",
|
||||||
"documentation": "Read the documentation ",
|
"documentation": "Read the documentation ",
|
||||||
"millisecondsToOffset": "Milliseconds to offset detect annotations by. <em>Default: 0</em>",
|
"millisecondsToOffset": "Milliseconds to offset detect annotations by. <em>Default: 0</em>",
|
||||||
"tips": "TIP: Imagine there is an event clip with a person walking from left to right. If the event timeline bounding box is consistently to the left of the person then the value should be decreased. Similarly, if a person is walking from left to right and the bounding box is consistently ahead of the person then the value should be increased."
|
"tips": "TIP: Imagine there is an event clip with a person walking from left to right. If the event timeline bounding box is consistently to the left of the person then the value should be decreased. Similarly, if a person is walking from left to right and the bounding box is consistently ahead of the person then the value should be increased.",
|
||||||
|
"toast": {
|
||||||
|
"success": "Annotation offset for {{camera}} has been saved to the config file. Restart Frigate to apply your changes."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"carousel": {
|
"carousel": {
|
||||||
|
@ -219,6 +219,11 @@
|
|||||||
"mustBeGreaterOrEqualZero": "Loitering time must be greater than or equal to 0."
|
"mustBeGreaterOrEqualZero": "Loitering time must be greater than or equal to 0."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"speed": {
|
||||||
|
"error": {
|
||||||
|
"mustBeGreaterOrEqualTo": "Speed threshold must greater than or equal to 0.1."
|
||||||
|
}
|
||||||
|
},
|
||||||
"polygonDrawing": {
|
"polygonDrawing": {
|
||||||
"removeLastPoint": "Remove last point",
|
"removeLastPoint": "Remove last point",
|
||||||
"reset": {
|
"reset": {
|
||||||
@ -271,7 +276,11 @@
|
|||||||
"speedEstimation": {
|
"speedEstimation": {
|
||||||
"title": "Speed Estimation",
|
"title": "Speed Estimation",
|
||||||
"desc": "Enable speed estimation for objects in this zone. The zone must have exactly 4 points.",
|
"desc": "Enable speed estimation for objects in this zone. The zone must have exactly 4 points.",
|
||||||
"docs": "Read the documentation"
|
"docs": "Read the documentation",
|
||||||
|
"lineADistance": "Line A distance ({{unit}})",
|
||||||
|
"lineBDistance": "Line B distance ({{unit}})",
|
||||||
|
"lineCDistance": "Line C distance ({{unit}})",
|
||||||
|
"lineDDistance": "Line D distance ({{unit}})"
|
||||||
},
|
},
|
||||||
"speedThreshold": {
|
"speedThreshold": {
|
||||||
"title": "Speed Threshold ({{unit}})",
|
"title": "Speed Threshold ({{unit}})",
|
||||||
@ -473,12 +482,14 @@
|
|||||||
"placeholder": "Re-enter new password"
|
"placeholder": "Re-enter new password"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"usernameIsRequired": "Username is required"
|
"usernameIsRequired": "Username is required",
|
||||||
|
"passwordIsRequired": "Password is required"
|
||||||
},
|
},
|
||||||
"createUser": {
|
"createUser": {
|
||||||
"title": "Create New User",
|
"title": "Create New User",
|
||||||
"desc": "Add a new user account and specify an role for access to areas of the Frigate UI.",
|
"desc": "Add a new user account and specify an role for access to areas of the Frigate UI.",
|
||||||
"usernameOnlyInclude": "Username may only include letters, numbers, . or _"
|
"usernameOnlyInclude": "Username may only include letters, numbers, . or _",
|
||||||
|
"confirmPassword": "Please confirm your password"
|
||||||
},
|
},
|
||||||
"deleteUser": {
|
"deleteUser": {
|
||||||
"title": "Delete User",
|
"title": "Delete User",
|
||||||
@ -486,12 +497,15 @@
|
|||||||
"warn": "Are you sure you want to delete <strong>{{username}}</strong>?"
|
"warn": "Are you sure you want to delete <strong>{{username}}</strong>?"
|
||||||
},
|
},
|
||||||
"passwordSetting": {
|
"passwordSetting": {
|
||||||
|
"cannotBeEmpty": "Password cannot be empty",
|
||||||
|
"doNotMatch": "Passwords do not match",
|
||||||
"updatePassword": "Update Password for {{username}}",
|
"updatePassword": "Update Password for {{username}}",
|
||||||
"setPassword": "Set Password",
|
"setPassword": "Set Password",
|
||||||
"desc": "Create a strong password to secure this account."
|
"desc": "Create a strong password to secure this account."
|
||||||
},
|
},
|
||||||
"changeRole": {
|
"changeRole": {
|
||||||
"title": "Change User Role",
|
"title": "Change User Role",
|
||||||
|
"select": "Select a role",
|
||||||
"desc": "Update permissions for <strong>{{username}}</strong>",
|
"desc": "Update permissions for <strong>{{username}}</strong>",
|
||||||
"roleInfo": {
|
"roleInfo": {
|
||||||
"intro": "Select the appropriate role for this user:",
|
"intro": "Select the appropriate role for this user:",
|
||||||
|
@ -108,6 +108,7 @@
|
|||||||
"title": "Cameras",
|
"title": "Cameras",
|
||||||
"overview": "Overview",
|
"overview": "Overview",
|
||||||
"info": {
|
"info": {
|
||||||
|
"aspectRatio": "aspect ratio",
|
||||||
"cameraProbeInfo": "{{camera}} Camera Probe Info",
|
"cameraProbeInfo": "{{camera}} Camera Probe Info",
|
||||||
"streamDataFromFFPROBE": "Stream data is obtained with <code>ffprobe</code>.",
|
"streamDataFromFFPROBE": "Stream data is obtained with <code>ffprobe</code>.",
|
||||||
"fetching": "Fetching Camera Data",
|
"fetching": "Fetching Camera Data",
|
||||||
|
@ -132,14 +132,14 @@ export default function CameraInfoDialog({
|
|||||||
/
|
/
|
||||||
{codec.height /
|
{codec.height /
|
||||||
gcd(codec.width, codec.height)}{" "}
|
gcd(codec.width, codec.height)}{" "}
|
||||||
aspect ratio)
|
{t("cameras.info.aspectRatio")})
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
{t("cameras.info.resolution")}{" "}
|
{t("cameras.info.resolution")}{" "}
|
||||||
<span className="text-primary">
|
<span className="text-primary">
|
||||||
Unknown
|
t("cameras.info.unknown")
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -56,8 +56,10 @@ export default function CreateUserDialog({
|
|||||||
.regex(/^[A-Za-z0-9._]+$/, {
|
.regex(/^[A-Za-z0-9._]+$/, {
|
||||||
message: t("users.dialog.createUser.usernameOnlyInclude"),
|
message: t("users.dialog.createUser.usernameOnlyInclude"),
|
||||||
}),
|
}),
|
||||||
password: z.string().min(1, "Password is required"),
|
password: z.string().min(1, t("users.dialog.form.passwordIsRequired")),
|
||||||
confirmPassword: z.string().min(1, "Please confirm your password"),
|
confirmPassword: z
|
||||||
|
.string()
|
||||||
|
.min(1, t("users.dialog.createUser.confirmPassword")),
|
||||||
role: z.enum(["admin", "viewer"]),
|
role: z.enum(["admin", "viewer"]),
|
||||||
})
|
})
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
.refine((data) => data.password === data.confirmPassword, {
|
||||||
|
@ -79,8 +79,8 @@ export default function FaceSelectionDialog({
|
|||||||
>
|
>
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<DrawerHeader className="sr-only">
|
<DrawerHeader className="sr-only">
|
||||||
<DrawerTitle>Log Details</DrawerTitle>
|
<DrawerTitle>Details</DrawerTitle>
|
||||||
<DrawerDescription>Log details</DrawerDescription>
|
<DrawerDescription>Details</DrawerDescription>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
)}
|
)}
|
||||||
<DropdownMenuLabel>{t("trainFaceAs")}</DropdownMenuLabel>
|
<DropdownMenuLabel>{t("trainFaceAs")}</DropdownMenuLabel>
|
||||||
|
@ -4,6 +4,7 @@ import { Button } from "../ui/button";
|
|||||||
import { FaFlag } from "react-icons/fa";
|
import { FaFlag } from "react-icons/fa";
|
||||||
import { TimelineType } from "@/types/timeline";
|
import { TimelineType } from "@/types/timeline";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type MobileTimelineDrawerProps = {
|
type MobileTimelineDrawerProps = {
|
||||||
selected: TimelineType;
|
selected: TimelineType;
|
||||||
@ -13,6 +14,7 @@ export default function MobileTimelineDrawer({
|
|||||||
selected,
|
selected,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: MobileTimelineDrawerProps) {
|
}: MobileTimelineDrawerProps) {
|
||||||
|
const { t } = useTranslation(["views/events"]);
|
||||||
const [drawer, setDrawer] = useState(false);
|
const [drawer, setDrawer] = useState(false);
|
||||||
|
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
@ -38,7 +40,7 @@ export default function MobileTimelineDrawer({
|
|||||||
setDrawer(false);
|
setDrawer(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Timeline
|
{t("timeline")}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`mx-4 w-full py-2 text-center smart-capitalize ${selected == "events" ? "rounded-lg bg-secondary" : ""}`}
|
className={`mx-4 w-full py-2 text-center smart-capitalize ${selected == "events" ? "rounded-lg bg-secondary" : ""}`}
|
||||||
@ -47,7 +49,7 @@ export default function MobileTimelineDrawer({
|
|||||||
setDrawer(false);
|
setDrawer(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Events
|
{t("events.label")}
|
||||||
</div>
|
</div>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
@ -83,7 +83,7 @@ export default function RoleChangeDialog({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder="Select a role" />
|
<SelectValue placeholder={t("users.dialog.changeRole.select")} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="admin" className="flex items-center gap-2">
|
<SelectItem value="admin" className="flex items-center gap-2">
|
||||||
|
@ -66,12 +66,12 @@ export default function SetPasswordDialog({
|
|||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (!password) {
|
if (!password) {
|
||||||
setError("Password cannot be empty");
|
setError(t("users.dialog.passwordSetting.cannotBeEmpty"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
if (password !== confirmPassword) {
|
||||||
setError("Passwords do not match");
|
setError(t("users.dialog.passwordSetting.doNotMatch"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,9 @@ export function AnnotationSettingsPane({
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success(
|
toast.success(
|
||||||
`Annotation offset for ${event?.camera} has been saved to the config file. Restart Frigate to apply your changes.`,
|
t("objectLifecycle.annotationSettings.offset.toast.success", {
|
||||||
|
camera: event?.camera,
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
position: "top-center",
|
position: "top-center",
|
||||||
},
|
},
|
||||||
|
@ -909,7 +909,9 @@ function ObjectDetailsTab({
|
|||||||
search.label,
|
search.label,
|
||||||
)) ? (
|
)) ? (
|
||||||
<>
|
<>
|
||||||
<div className="text-sm text-primary/40">Description</div>
|
<div className="text-sm text-primary/40">
|
||||||
|
{t("details.description.label")}
|
||||||
|
</div>
|
||||||
<div className="flex h-64 flex-col items-center justify-center gap-3 border p-4 text-sm text-primary/40">
|
<div className="flex h-64 flex-col items-center justify-center gap-3 border p-4 text-sm text-primary/40">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
|
@ -240,11 +240,13 @@ export function CameraStreamingDialog({
|
|||||||
Object.entries(config?.cameras[camera].live.streams).length > 0 && (
|
Object.entries(config?.cameras[camera].live.streams).length > 0 && (
|
||||||
<div className="flex flex-col items-start gap-2">
|
<div className="flex flex-col items-start gap-2">
|
||||||
<Label htmlFor="stream" className="text-right">
|
<Label htmlFor="stream" className="text-right">
|
||||||
Stream
|
{t("group.camera.setting.stream")}
|
||||||
</Label>
|
</Label>
|
||||||
<Select value={streamName} onValueChange={setStreamName}>
|
<Select value={streamName} onValueChange={setStreamName}>
|
||||||
<SelectTrigger className="">
|
<SelectTrigger className="">
|
||||||
<SelectValue placeholder="Choose a stream" />
|
<SelectValue
|
||||||
|
placeholder={t("group.camera.setting.placeholder")}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{camera !== "birdseye" &&
|
{camera !== "birdseye" &&
|
||||||
@ -305,7 +307,9 @@ export function CameraStreamingDialog({
|
|||||||
onValueChange={(value) => setStreamType(value as StreamType)}
|
onValueChange={(value) => setStreamType(value as StreamType)}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="">
|
<SelectTrigger className="">
|
||||||
<SelectValue placeholder="Choose a streaming option" />
|
<SelectValue
|
||||||
|
placeholder={t("group.camera.setting.streamMethod.placeholder")}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="no-streaming">
|
<SelectItem value="no-streaming">
|
||||||
|
@ -99,8 +99,11 @@ export default function ObjectMaskEditPane({
|
|||||||
objectType = objects;
|
objectType = objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Object Mask ${count + 1} (${objectType})`;
|
return t("masksAndZones.objectMaskLabel", {
|
||||||
}, [polygons, polygon]);
|
number: count + 1,
|
||||||
|
label: t(objectType, { ns: "objects" }),
|
||||||
|
});
|
||||||
|
}, [polygons, polygon, t]);
|
||||||
|
|
||||||
const formSchema = z
|
const formSchema = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -200,7 +200,7 @@ export default function ZoneEditPane({
|
|||||||
speed_threshold: z.coerce
|
speed_threshold: z.coerce
|
||||||
.number()
|
.number()
|
||||||
.min(0.1, {
|
.min(0.1, {
|
||||||
message: "Speed threshold must be greater than or equal to 0.1",
|
message: t("masksAndZones.form.speed.error.mustBeGreaterOrEqualTo"),
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.or(z.literal("")),
|
.or(z.literal("")),
|
||||||
@ -699,11 +699,15 @@ export default function ZoneEditPane({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Line A distance (
|
{t(
|
||||||
{config?.ui.unit_system == "imperial"
|
"masksAndZones.zones.speedEstimation.lineADistance",
|
||||||
? "feet"
|
{
|
||||||
: "meters"}
|
unit:
|
||||||
)
|
config?.ui.unit_system == "imperial"
|
||||||
|
? t("feet", { ns: "common" })
|
||||||
|
: t("meters", { ns: "common" }),
|
||||||
|
},
|
||||||
|
)}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
@ -722,11 +726,15 @@ export default function ZoneEditPane({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Line B distance (
|
{t(
|
||||||
{config?.ui.unit_system == "imperial"
|
"masksAndZones.zones.speedEstimation.lineBDistance",
|
||||||
? "feet"
|
{
|
||||||
: "meters"}
|
unit:
|
||||||
)
|
config?.ui.unit_system == "imperial"
|
||||||
|
? t("feet", { ns: "common" })
|
||||||
|
: t("meters", { ns: "common" }),
|
||||||
|
},
|
||||||
|
)}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
@ -745,11 +753,15 @@ export default function ZoneEditPane({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Line C distance (
|
{t(
|
||||||
{config?.ui.unit_system == "imperial"
|
"masksAndZones.zones.speedEstimation.lineCDistance",
|
||||||
? "feet"
|
{
|
||||||
: "meters"}
|
unit:
|
||||||
)
|
config?.ui.unit_system == "imperial"
|
||||||
|
? t("feet", { ns: "common" })
|
||||||
|
: t("meters", { ns: "common" }),
|
||||||
|
},
|
||||||
|
)}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
@ -768,11 +780,15 @@ export default function ZoneEditPane({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Line D distance (
|
{t(
|
||||||
{config?.ui.unit_system == "imperial"
|
"masksAndZones.zones.speedEstimation.lineDDistance",
|
||||||
? "feet"
|
{
|
||||||
: "meters"}
|
unit:
|
||||||
)
|
config?.ui.unit_system == "imperial"
|
||||||
|
? t("feet", { ns: "common" })
|
||||||
|
: t("meters", { ns: "common" }),
|
||||||
|
},
|
||||||
|
)}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
|
@ -197,7 +197,7 @@ function ConfigEditor() {
|
|||||||
listener = (e) => {
|
listener = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.returnValue = true;
|
e.returnValue = true;
|
||||||
return "Exit without saving?";
|
return t("confirm");
|
||||||
};
|
};
|
||||||
window.addEventListener("beforeunload", listener);
|
window.addEventListener("beforeunload", listener);
|
||||||
}
|
}
|
||||||
@ -207,7 +207,7 @@ function ConfigEditor() {
|
|||||||
window.removeEventListener("beforeunload", listener);
|
window.removeEventListener("beforeunload", listener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [hasChanges]);
|
}, [hasChanges, t]);
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return <ActivityIndicator />;
|
return <ActivityIndicator />;
|
||||||
|
@ -1044,7 +1044,7 @@ function FaceGrid({
|
|||||||
return (
|
return (
|
||||||
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center text-center">
|
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center text-center">
|
||||||
<LuFolderCheck className="size-16" />
|
<LuFolderCheck className="size-16" />
|
||||||
No faces available
|
(t("nofaces"))
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user