mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-08-04 13:47:37 +02:00
i18n fixes (#17725)
* fix key * add french * Fix random 0 * fix i18n in role change dialog * fix delete user dialog * fix filter tips steps * remove classes from i18n files * combine disjointed norweigan localized files * change submit to plus to use a question with yes/no --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
parent
1315a28252
commit
84c1ad59a2
@ -15,15 +15,11 @@
|
|||||||
"desc": "Objects in locations you want to avoid are not false positives. Submitting them as false positives will confuse the model."
|
"desc": "Objects in locations you want to avoid are not false positives. Submitting them as false positives will confuse the model."
|
||||||
},
|
},
|
||||||
"review": {
|
"review": {
|
||||||
"true": {
|
"question": {
|
||||||
"label": "Confirm this label for Frigate Plus",
|
"label": "Confirm this label for Frigate Plus",
|
||||||
"true_one": "This is a {{label}}",
|
"ask_a": "Is this object a <code>{{label}}</code>?",
|
||||||
"true_other": "This is an {{label}}"
|
"ask_an": "Is this object an <code>{{label}}</code>?",
|
||||||
},
|
"ask_full": "Is this object a <code>{{untranslatedLabel}}</code> ({{translatedLabel}})?"
|
||||||
"false": {
|
|
||||||
"label": "Do not confirm this label for Frigate Plus",
|
|
||||||
"false_one": "This is not a {{label}}",
|
|
||||||
"false_other": "This is not an {{label}}"
|
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"submitted": "Submitted"
|
"submitted": "Submitted"
|
||||||
|
@ -46,8 +46,13 @@
|
|||||||
"title": "How to use text filters",
|
"title": "How to use text filters",
|
||||||
"desc": {
|
"desc": {
|
||||||
"text": "Filters help you narrow down your search results. Here's how to use them in the input field:",
|
"text": "Filters help you narrow down your search results. Here's how to use them in the input field:",
|
||||||
"step": "<ul className=\"list-disc pl-5 text-sm text-primary-variant\"><li>Type a filter name followed by a colon (e.g., \"cameras:\").</li><li>Select a value from the suggestions or type your own.</li><li>Use multiple filters by adding them one after another with a space in between.</li><li>Date filters (before: and after:) use <em>{{DateFormat}}</em> format.</li><li>Time range filter uses <em>{{exampleTime}}</em> format.</li><li>Remove filters by clicking the 'x' next to them.</li></ul>",
|
"step1": "Type a filter key name followed by a colon (e.g., \"cameras:\").",
|
||||||
"example": "Example: <code className=\"text-primary\">cameras:front_door label:person before:01012024 time_range:3:00PM-4:00PM </code>"
|
"step2": "Select a value from the suggestions or type your own.",
|
||||||
|
"step3": "Use multiple filters by adding them one after another with a space in between.",
|
||||||
|
"step4": "Date filters (before: and after:) use {{DateFormat}} format.",
|
||||||
|
"step5": "Time range filter uses {{exampleTime}} format.",
|
||||||
|
"step6": "Remove filters by clicking the 'x' next to them.",
|
||||||
|
"exampleLabel": "Example:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
|
@ -385,12 +385,12 @@
|
|||||||
"motion": {
|
"motion": {
|
||||||
"title": "Motion boxes",
|
"title": "Motion boxes",
|
||||||
"desc": "Show boxes around areas where motion is detected",
|
"desc": "Show boxes around areas where motion is detected",
|
||||||
"tips": "<p className=\"mb-2\"><strong>Motion Boxes</strong></p><br><p>Red boxes will be overlaid on areas of the frame where motion is currently being detected</p>"
|
"tips": "<p><strong>Motion Boxes</strong></p><br><p>Red boxes will be overlaid on areas of the frame where motion is currently being detected</p>"
|
||||||
},
|
},
|
||||||
"regions": {
|
"regions": {
|
||||||
"title": "Regions",
|
"title": "Regions",
|
||||||
"desc": "Show a box of the region of interest sent to the object detector",
|
"desc": "Show a box of the region of interest sent to the object detector",
|
||||||
"tips": "<p className=\"mb-2\"><strong>Region Boxes</strong></p><br><p>Bright green boxes will be overlaid on areas of interest in the frame that are being sent to the object detector.</p>"
|
"tips": "<p><strong>Region Boxes</strong></p><br><p>Bright green boxes will be overlaid on areas of interest in the frame that are being sent to the object detector.</p>"
|
||||||
},
|
},
|
||||||
"objectShapeFilterDrawing": {
|
"objectShapeFilterDrawing": {
|
||||||
"title": "Object Shape Filter Drawing",
|
"title": "Object Shape Filter Drawing",
|
||||||
@ -474,7 +474,7 @@
|
|||||||
"deleteUser": {
|
"deleteUser": {
|
||||||
"title": "Delete User",
|
"title": "Delete User",
|
||||||
"desc": "This action cannot be undone. This will permanently delete the user account and remove all associated data.",
|
"desc": "This action cannot be undone. This will permanently delete the user account and remove all associated data.",
|
||||||
"warn": "Are you sure you want to delete <span className=\"font-bold\">{{username}}</span>?"
|
"warn": "Are you sure you want to delete"
|
||||||
},
|
},
|
||||||
"passwordSetting": {
|
"passwordSetting": {
|
||||||
"updatePassword": "Update Password for {{username}}",
|
"updatePassword": "Update Password for {{username}}",
|
||||||
@ -483,8 +483,14 @@
|
|||||||
},
|
},
|
||||||
"changeRole": {
|
"changeRole": {
|
||||||
"title": "Change User Role",
|
"title": "Change User Role",
|
||||||
"desc": "Update permissions for <span className=\"font-medium\">{{username}}</span>",
|
"desc": "Update permissions for",
|
||||||
"roleInfo": "<p>Select the appropriate role for this user:</p><ul className=\"mt-2 space-y-1 pl-5\"><li> • <span className=\"font-medium\">Admin:</span> Full access to all features. </li><li> • <span className=\"font-medium\">Viewer:</span> Limited to Live dashboards, Review, Explore, and Exports only.</li></ul>"
|
"roleInfo": {
|
||||||
|
"intro": "Select the appropriate role for this user:",
|
||||||
|
"admin": "Admin",
|
||||||
|
"adminDesc": "Full access to all features.",
|
||||||
|
"viewer": "Viewer",
|
||||||
|
"viewerDesc": "Limited to Live dashboards, Review, Explore, and Exports only."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -51,7 +51,7 @@ import { toast } from "sonner";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { MdImageSearch } from "react-icons/md";
|
import { MdImageSearch } from "react-icons/md";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type InputWithTagsProps = {
|
type InputWithTagsProps = {
|
||||||
inputFocused: boolean;
|
inputFocused: boolean;
|
||||||
@ -729,20 +729,31 @@ export default function InputWithTags({
|
|||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{t("filter.tips.desc.text")}
|
{t("filter.tips.desc.text")}
|
||||||
</p>
|
</p>
|
||||||
<Trans
|
<ul className="list-disc pl-5 text-sm text-primary-variant">
|
||||||
ns="views/search"
|
<li>{t("filter.tips.desc.step1")}</li>
|
||||||
values={{
|
<li>{t("filter.tips.desc.step2")}</li>
|
||||||
DateFormat: getIntlDateFormat(),
|
<li>{t("filter.tips.desc.step3")}</li>
|
||||||
exampleTime:
|
<li>
|
||||||
config?.ui.time_format == "24hour"
|
{t("filter.tips.desc.step4", {
|
||||||
? "15:00-16:00"
|
DateFormat: getIntlDateFormat(),
|
||||||
: "3:00PM-4:00PM",
|
})}
|
||||||
}}
|
</li>
|
||||||
>
|
<li>
|
||||||
filter.tips.desc.step
|
{t("filter.tips.desc.step5", {
|
||||||
</Trans>
|
exampleTime:
|
||||||
|
config?.ui.time_format == "24hour"
|
||||||
|
? "15:00-16:00"
|
||||||
|
: "3:00PM-4:00PM",
|
||||||
|
})}
|
||||||
|
</li>
|
||||||
|
<li>{t("filter.tips.desc.step6")}</li>
|
||||||
|
</ul>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<Trans ns="views/search">filter.tips.desc.example</Trans>
|
{t("filter.tips.desc.exampleLabel")}{" "}
|
||||||
|
<code className="text-primary">
|
||||||
|
cameras:front_door label:person before:01012024
|
||||||
|
time_range:3:00PM-4:00PM
|
||||||
|
</code>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
|
@ -78,6 +78,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
|||||||
const languages = [
|
const languages = [
|
||||||
{ code: "en", label: t("menu.language.en") },
|
{ code: "en", label: t("menu.language.en") },
|
||||||
{ code: "es", label: t("menu.language.es") },
|
{ code: "es", label: t("menu.language.es") },
|
||||||
|
{ code: "fr", label: t("menu.language.fr") },
|
||||||
{ code: "zh-CN", label: t("menu.language.zhCN") },
|
{ code: "zh-CN", label: t("menu.language.zhCN") },
|
||||||
{ code: "tr", label: t("menu.language.tr") },
|
{ code: "tr", label: t("menu.language.tr") },
|
||||||
{ code: "nl", label: t("menu.language.nl") },
|
{ code: "nl", label: t("menu.language.nl") },
|
||||||
|
@ -35,7 +35,8 @@ export default function DeleteUserDialog({
|
|||||||
|
|
||||||
<div className="my-4 rounded-md border border-destructive/20 bg-destructive/5 p-4 text-center text-sm">
|
<div className="my-4 rounded-md border border-destructive/20 bg-destructive/5 p-4 text-center text-sm">
|
||||||
<p className="font-medium text-destructive">
|
<p className="font-medium text-destructive">
|
||||||
{t("users.dialog.deleteUser.warn", { username })}
|
{t("users.dialog.deleteUser.warn")}
|
||||||
|
<span className="font-medium"> {username}</span>?
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Trans, useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -46,13 +46,28 @@ export default function RoleChangeDialog({
|
|||||||
{t("users.dialog.changeRole.title")}
|
{t("users.dialog.changeRole.title")}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{t("users.dialog.changeRole.desc", { username })}
|
{t("users.dialog.changeRole.desc")}
|
||||||
|
<span className="font-medium"> {username}</span>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="py-6">
|
<div className="py-3">
|
||||||
<div className="mb-4 text-sm text-muted-foreground">
|
<div className="mb-4 text-sm text-muted-foreground">
|
||||||
<Trans ns="views/settings">users.dialog.changeRole.roleInfo</Trans>
|
<p>{t("users.dialog.changeRole.roleInfo.intro")}</p>
|
||||||
|
<ul className="mt-2 list-disc space-y-1 pl-5">
|
||||||
|
<li>
|
||||||
|
<span className="font-medium">
|
||||||
|
{t("users.dialog.changeRole.roleInfo.admin")}
|
||||||
|
</span>
|
||||||
|
: {t("users.dialog.changeRole.roleInfo.adminDesc")}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-medium">
|
||||||
|
{t("users.dialog.changeRole.roleInfo.viewer")}
|
||||||
|
</span>
|
||||||
|
: {t("users.dialog.changeRole.roleInfo.viewerDesc")}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
|
@ -73,7 +73,7 @@ import { LuInfo, LuSearch } from "react-icons/lu";
|
|||||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||||
import { FaPencilAlt } from "react-icons/fa";
|
import { FaPencilAlt } from "react-icons/fa";
|
||||||
import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog";
|
import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog";
|
||||||
import { useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { TbFaceId } from "react-icons/tb";
|
import { TbFaceId } from "react-icons/tb";
|
||||||
import { useIsAdmin } from "@/hooks/use-is-admin";
|
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||||
import FaceSelectionDialog from "../FaceSelectionDialog";
|
import FaceSelectionDialog from "../FaceSelectionDialog";
|
||||||
@ -366,7 +366,7 @@ function ObjectDetailsTab({
|
|||||||
|
|
||||||
const snapScore = useMemo(() => {
|
const snapScore = useMemo(() => {
|
||||||
if (!search?.has_snapshot) {
|
if (!search?.has_snapshot) {
|
||||||
return 0;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = search.data.score ?? search.score ?? 0;
|
const value = search.data.score ?? search.score ?? 0;
|
||||||
@ -1017,7 +1017,7 @@ export function ObjectSnapshotTab({
|
|||||||
search,
|
search,
|
||||||
onEventUploaded,
|
onEventUploaded,
|
||||||
}: ObjectSnapshotTabProps) {
|
}: ObjectSnapshotTabProps) {
|
||||||
const { t } = useTranslation(["components/dialog"]);
|
const { t, i18n } = useTranslation(["components/dialog"]);
|
||||||
type SubmissionState = "reviewing" | "uploading" | "submitted";
|
type SubmissionState = "reviewing" | "uploading" | "submitted";
|
||||||
|
|
||||||
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();
|
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();
|
||||||
@ -1128,42 +1128,67 @@ export function ObjectSnapshotTab({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row justify-center gap-2 md:justify-end">
|
<div className="flex w-full flex-1 flex-col justify-center gap-2 md:ml-8 md:w-auto md:justify-end">
|
||||||
{state == "reviewing" && (
|
{state == "reviewing" && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<div>
|
||||||
className="bg-success"
|
{i18n.language === "en" ? (
|
||||||
aria-label={t("explore.plus.review.true.label")}
|
// English with a/an logic plus label
|
||||||
onClick={() => {
|
<>
|
||||||
setState("uploading");
|
{/^[aeiou]/i.test(search?.label || "") ? (
|
||||||
onSubmitToPlus(false);
|
<Trans
|
||||||
}}
|
ns="components/dialog"
|
||||||
>
|
values={{ label: search?.label }}
|
||||||
{/^[aeiou]/i.test(search?.label || "")
|
>
|
||||||
? t("explore.plus.review.true.true_other", {
|
explore.plus.review.question.ask_an
|
||||||
label: search?.label,
|
</Trans>
|
||||||
})
|
) : (
|
||||||
: t("explore.plus.review.true.true_one", {
|
<Trans
|
||||||
label: search?.label,
|
ns="components/dialog"
|
||||||
})}
|
values={{ label: search?.label }}
|
||||||
</Button>
|
>
|
||||||
<Button
|
explore.plus.review.question.ask_a
|
||||||
className="text-white"
|
</Trans>
|
||||||
aria-label={t("explore.plus.review.false.label")}
|
)}
|
||||||
variant="destructive"
|
</>
|
||||||
onClick={() => {
|
) : (
|
||||||
setState("uploading");
|
// For other languages
|
||||||
onSubmitToPlus(true);
|
<Trans
|
||||||
}}
|
ns="components/dialog"
|
||||||
>
|
values={{
|
||||||
{/^[aeiou]/i.test(search?.label || "")
|
untranslatedLabel: search?.label,
|
||||||
? t("explore.plus.review.false.false_other", {
|
translatedLabel: t(
|
||||||
label: search?.label,
|
"filter.label." + search?.label,
|
||||||
})
|
),
|
||||||
: t("explore.plus.review.false.false_one", {
|
}}
|
||||||
label: search?.label,
|
>
|
||||||
})}
|
explore.plus.review.question.ask_full
|
||||||
</Button>
|
</Trans>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full flex-row gap-2">
|
||||||
|
<Button
|
||||||
|
className="flex-1 bg-success"
|
||||||
|
aria-label={t("button.yes", { ns: "common" })}
|
||||||
|
onClick={() => {
|
||||||
|
setState("uploading");
|
||||||
|
onSubmitToPlus(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("button.yes", { ns: "common" })}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="flex-1 text-white"
|
||||||
|
aria-label={t("button.no", { ns: "common" })}
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => {
|
||||||
|
setState("uploading");
|
||||||
|
onSubmitToPlus(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("button.no", { ns: "common" })}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{state == "uploading" && <ActivityIndicator />}
|
{state == "uploading" && <ActivityIndicator />}
|
||||||
|
@ -242,7 +242,9 @@ export default function MotionMaskEditPane({
|
|||||||
</Heading>
|
</Heading>
|
||||||
<div className="my-3 space-y-3 text-sm text-muted-foreground">
|
<div className="my-3 space-y-3 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<Trans ns="views/settings">masksAndZones.motionMasks.context</Trans>
|
<Trans ns="views/settings">
|
||||||
|
masksAndZones.motionMasks.context.title
|
||||||
|
</Trans>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex items-center text-primary">
|
<div className="flex items-center text-primary">
|
||||||
|
Loading…
Reference in New Issue
Block a user