feat: change explore setting name

This commit is contained in:
ZhaiSoul 2025-03-15 01:32:26 +08:00
parent b9c6dfeeff
commit 18ff446465
3 changed files with 88 additions and 57 deletions

View File

@ -59,8 +59,8 @@
} }
} }
}, },
"explore": { "classification": {
"title": "Explore Settings", "title": "Classification Settings",
"semanticSearch": { "semanticSearch": {
"title": "Semantic Search", "title": "Semantic Search",
"desc": "Semantic Search in Frigate allows you to find tracked objects within your review items using either the image itself, a user-defined text description, or an automatically generated one.", "desc": "Semantic Search in Frigate allows you to find tracked objects within your review items using either the image itself, a user-defined text description, or an automatically generated one.",
@ -78,8 +78,19 @@
"large.desc": "Using <em>large</em> employs the full Jina model and will automatically run on the GPU if applicable." "large.desc": "Using <em>large</em> employs the full Jina model and will automatically run on the GPU if applicable."
} }
}, },
"faceRecognition": {
"title": "Face Recognition",
"desc": "Face recognition allows people to be assigned names and when their face is recognized Frigate will assign the person's name as a sub label. This information is included in the UI, filters, as well as in notifications.",
"readTheDocumentation": "Read the Documentation"
},
"licensePlateRecognition": {
"title": "License Plate Recognition",
"desc": "Frigate can recognize license plates on vehicles and automatically add the detected characters to the recognized_license_plate field or a known name as a sub_label to objects that are of type car. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street.",
"readTheDocumentation": "Read the Documentation"
},
"toast": { "toast": {
"success": "Explore settings have been saved." "success": "Classification settings have been saved.",
"error": "Failed to save config changes: {{errorMessage}}"
} }
}, },
"camera": { "camera": {

View File

@ -59,8 +59,8 @@
} }
} }
}, },
"explore": { "classification": {
"title": "探测设置", "title": "分类设置",
"semanticSearch": { "semanticSearch": {
"title": "语义搜索", "title": "语义搜索",
"desc": "Frigate的语义搜索能够让你使用自然语言根据图像本身、自定义的文本描述或自动生成的描述来搜索视频。", "desc": "Frigate的语义搜索能够让你使用自然语言根据图像本身、自定义的文本描述或自动生成的描述来搜索视频。",
@ -78,8 +78,19 @@
"large.desc": "使用 <strong>大</strong>模型。该模型采用了完整的Jina模型并在适用的情况下使用GPU。" "large.desc": "使用 <strong>大</strong>模型。该模型采用了完整的Jina模型并在适用的情况下使用GPU。"
} }
}, },
"faceRecognition": {
"title": "人脸识别",
"desc": "人脸识别功能允许为人物分配名称当识别到他们的面孔时Frigate 会将人物的名字作为子标签进行分配。这些信息会显示在界面、过滤器以及通知中。",
"readTheDocumentation": "阅读文档(英文)"
},
"licensePlateRecognition": {
"title": "车牌识别",
"desc": "Frigate 可以识别车辆的车牌,并自动将检测到的字符添加到 recognized_license_plate 字段中,或将已知名称作为子标签添加到汽车类型的对象中。常见的使用场景可能是读取驶入车道的汽车车牌或经过街道的汽车车牌。",
"readTheDocumentation": "阅读文档(英文)"
},
"toast": { "toast": {
"success": "探测设置已保存。" "success": "分类设置已保存。",
"error": "保存配置更改失败:{{errorMessage}}"
} }
}, },
"camera": { "camera": {
@ -146,6 +157,11 @@
"true": "启用点对齐", "true": "启用点对齐",
"false": "禁用点对齐" "false": "禁用点对齐"
}, },
"delete": {
"title": "确认删除",
"desc": "你确定要删除{{type}} <em>{{name}}</em> 吗?",
"success": "{{name}} 已被删除。"
},
"error": { "error": {
"mustBeFinished": "多边形绘制必须完成闭合后才能保存。" "mustBeFinished": "多边形绘制必须完成闭合后才能保存。"
} }

View File

@ -20,6 +20,7 @@ import {
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Trans, useTranslation } from "react-i18next";
type ClassificationSettings = { type ClassificationSettings = {
search: { search: {
@ -41,6 +42,7 @@ type ClassificationSettingsViewProps = {
export default function ClassificationSettingsView({ export default function ClassificationSettingsView({
setUnsavedChanges, setUnsavedChanges,
}: ClassificationSettingsViewProps) { }: ClassificationSettingsViewProps) {
const { t } = useTranslation("views/settings");
const { data: config, mutate: updateConfig } = const { data: config, mutate: updateConfig } =
useSWR<FrigateConfig>("config"); useSWR<FrigateConfig>("config");
const [changedValue, setChangedValue] = useState(false); const [changedValue, setChangedValue] = useState(false);
@ -141,15 +143,18 @@ export default function ClassificationSettingsView({
) )
.then((res) => { .then((res) => {
if (res.status === 200) { if (res.status === 200) {
toast.success("Classification settings have been saved.", { toast.success(t("classification.toast.success"), {
position: "top-center", position: "top-center",
}); });
setChangedValue(false); setChangedValue(false);
updateConfig(); updateConfig();
} else { } else {
toast.error(`Failed to save config changes: ${res.statusText}`, { toast.error(
t("classification.toast.error", { errorMessage: res.statusText }),
{
position: "top-center", position: "top-center",
}); },
);
} }
}) })
.catch((error) => { .catch((error) => {
@ -157,14 +162,14 @@ export default function ClassificationSettingsView({
error.response?.data?.message || error.response?.data?.message ||
error.response?.data?.detail || error.response?.data?.detail ||
"Unknown error"; "Unknown error";
toast.error(`Failed to save config changes: ${errorMessage}`, { toast.error(t("toast.save.error", { errorMessage, ns: "common" }), {
position: "top-center", position: "top-center",
}); });
}) })
.finally(() => { .finally(() => {
setIsLoading(false); setIsLoading(false);
}); });
}, [updateConfig, classificationSettings.search]); }, [updateConfig, classificationSettings.search, t]);
const onCancel = useCallback(() => { const onCancel = useCallback(() => {
setClassificationSettings(origSearchSettings); setClassificationSettings(origSearchSettings);
@ -200,19 +205,15 @@ export default function ClassificationSettingsView({
<Toaster position="top-center" closeButton={true} /> <Toaster position="top-center" closeButton={true} />
<div className="scrollbar-container order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0"> <div className="scrollbar-container order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0">
<Heading as="h3" className="my-2"> <Heading as="h3" className="my-2">
Classification Settings {t("classification.title")}
</Heading> </Heading>
<Separator className="my-2 flex bg-secondary" /> <Separator className="my-2 flex bg-secondary" />
<Heading as="h4" className="my-2"> <Heading as="h4" className="my-2">
Semantic Search {t("classification.semanticSearch.title")}
</Heading> </Heading>
<div className="max-w-6xl"> <div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant"> <div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant">
<p> <p>{t("classification.semanticSearch.desc")}</p>
Semantic Search in Frigate allows you to find tracked objects
within your review items using either the image itself, a
user-defined text description, or an automatically generated one.
</p>
<div className="flex items-center text-primary"> <div className="flex items-center text-primary">
<Link <Link
@ -221,7 +222,7 @@ export default function ClassificationSettingsView({
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline" className="inline"
> >
Read the Documentation {t("classification.semanticSearch.readTheDocumentation")}
<LuExternalLink className="ml-2 inline-flex size-3" /> <LuExternalLink className="ml-2 inline-flex size-3" />
</Link> </Link>
</div> </div>
@ -242,7 +243,9 @@ export default function ClassificationSettingsView({
}} }}
/> />
<div className="space-y-0.5"> <div className="space-y-0.5">
<Label htmlFor="enabled">Enabled</Label> <Label htmlFor="enabled">
{t("button.enabled", { ns: "common" })}
</Label>
</div> </div>
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
@ -259,31 +262,38 @@ export default function ClassificationSettingsView({
}} }}
/> />
<div className="space-y-0.5"> <div className="space-y-0.5">
<Label htmlFor="reindex">Re-Index On Startup</Label> <Label htmlFor="reindex">
{t("classification.semanticSearch.reindexOnStartup.label")}
</Label>
</div> </div>
</div> </div>
<div className="mt-3 text-sm text-muted-foreground"> <div className="mt-3 text-sm text-muted-foreground">
Re-indexing will reprocess all thumbnails and descriptions (if <Trans ns="views/settings">
enabled) and apply the embeddings on each startup.{" "} classification.semanticSearch.reindexOnStartup.desc
<em>Don't forget to disable the option after restarting!</em> </Trans>
</div> </div>
</div> </div>
<div className="mt-2 flex flex-col space-y-6"> <div className="mt-2 flex flex-col space-y-6">
<div className="space-y-0.5"> <div className="space-y-0.5">
<div className="text-md">Model Size</div> <div className="text-md">
{t("classification.semanticSearch.modelSize.label")}
</div>
<div className="space-y-1 text-sm text-muted-foreground"> <div className="space-y-1 text-sm text-muted-foreground">
<p> <p>
The size of the model used for Semantic Search embeddings. <Trans ns="views/settings">
classification.semanticSearch.modelSize.desc
</Trans>
</p> </p>
<ul className="list-disc pl-5 text-sm"> <ul className="list-disc pl-5 text-sm">
<li> <li>
Using <em>small</em> employs a quantized version of the <Trans ns="views/settings">
model that uses less RAM and runs faster on CPU with a very classification.semanticSearch.modelSize.small.desc
negligible difference in embedding quality. </Trans>
</li> </li>
<li> <li>
Using <em>large</em> employs the full Jina model and will <Trans ns="views/settings">
automatically run on the GPU if applicable. classification.semanticSearch.modelSize.large.desc
</Trans>
</li> </li>
</ul> </ul>
</div> </div>
@ -309,7 +319,7 @@ export default function ClassificationSettingsView({
className="cursor-pointer" className="cursor-pointer"
value={size} value={size}
> >
{size} {t("classification.semanticSearch.modelSize." + size)}
</SelectItem> </SelectItem>
))} ))}
</SelectGroup> </SelectGroup>
@ -322,16 +332,11 @@ export default function ClassificationSettingsView({
<Separator className="my-2 flex bg-secondary" /> <Separator className="my-2 flex bg-secondary" />
<Heading as="h4" className="my-2"> <Heading as="h4" className="my-2">
Face Recognition {t("classification.faceRecognition.title")}
</Heading> </Heading>
<div className="max-w-6xl"> <div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant"> <div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant">
<p> <p>{t("classification.faceRecognition.desc")}</p>
Face recognition allows people to be assigned names and when
their face is recognized Frigate will assign the person's name
as a sub label. This information is included in the UI, filters,
as well as in notifications.
</p>
<div className="flex items-center text-primary"> <div className="flex items-center text-primary">
<Link <Link
@ -340,7 +345,7 @@ export default function ClassificationSettingsView({
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline" className="inline"
> >
Read the Documentation {t("classification.faceRecognition.readTheDocumentation")}
<LuExternalLink className="ml-2 inline-flex size-3" /> <LuExternalLink className="ml-2 inline-flex size-3" />
</Link> </Link>
</div> </div>
@ -361,7 +366,9 @@ export default function ClassificationSettingsView({
}} }}
/> />
<div className="space-y-0.5"> <div className="space-y-0.5">
<Label htmlFor="enabled">Enabled</Label> <Label htmlFor="enabled">
{t("button.enabled", { ns: "common" })}
</Label>
</div> </div>
</div> </div>
</div> </div>
@ -369,18 +376,11 @@ export default function ClassificationSettingsView({
<Separator className="my-2 flex bg-secondary" /> <Separator className="my-2 flex bg-secondary" />
<Heading as="h4" className="my-2"> <Heading as="h4" className="my-2">
License Plate Recognition {t("classification.licensePlateRecognition.title")}
</Heading> </Heading>
<div className="max-w-6xl"> <div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant"> <div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant">
<p> <p>{t("classification.licensePlateRecognition.desc")}</p>
Frigate can recognize license plates on vehicles and
automatically add the detected characters to the
recognized_license_plate field or a known name as a sub_label to
objects that are of type car. A common use case may be to read
the license plates of cars pulling into a driveway or cars
passing by on a street.
</p>
<div className="flex items-center text-primary"> <div className="flex items-center text-primary">
<Link <Link
@ -389,7 +389,9 @@ export default function ClassificationSettingsView({
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline" className="inline"
> >
Read the Documentation {t(
"classification.licensePlateRecognition.readTheDocumentation",
)}
<LuExternalLink className="ml-2 inline-flex size-3" /> <LuExternalLink className="ml-2 inline-flex size-3" />
</Link> </Link>
</div> </div>
@ -410,7 +412,9 @@ export default function ClassificationSettingsView({
}} }}
/> />
<div className="space-y-0.5"> <div className="space-y-0.5">
<Label htmlFor="enabled">Enabled</Label> <Label htmlFor="enabled">
{t("button.enabled", { ns: "common" })}
</Label>
</div> </div>
</div> </div>
</div> </div>
@ -420,10 +424,10 @@ export default function ClassificationSettingsView({
<div className="flex w-full flex-row items-center gap-2 pt-2 md:w-[25%]"> <div className="flex w-full flex-row items-center gap-2 pt-2 md:w-[25%]">
<Button <Button
className="flex flex-1" className="flex flex-1"
aria-label="Reset" aria-label={t("button.reset", { ns: "common" })}
onClick={onCancel} onClick={onCancel}
> >
Reset {t("button.reset", { ns: "common" })}
</Button> </Button>
<Button <Button
variant="select" variant="select"
@ -435,10 +439,10 @@ export default function ClassificationSettingsView({
{isLoading ? ( {isLoading ? (
<div className="flex flex-row items-center gap-2"> <div className="flex flex-row items-center gap-2">
<ActivityIndicator /> <ActivityIndicator />
<span>Saving...</span> <span>{t("button.saving", { ns: "common" })}</span>
</div> </div>
) : ( ) : (
"Save" t("button.save", { ns: "common" })
)} )}
</Button> </Button>
</div> </div>