feat: add explore model i18n keys

This commit is contained in:
ZhaiSoul 2025-03-11 19:53:44 +08:00
parent 118ef6b2fe
commit 84ba218b1b
11 changed files with 107 additions and 27 deletions

View File

@ -111,6 +111,7 @@
"restart": "Restart Frigate",
"live": "Live",
"live.allCameras": "All Cameras",
"live.cameras": "Cameras",
"review": "Review",
"explore": "Explore",
"export": "Export",

View File

@ -1,4 +1,32 @@
{
"exploreIsUnavailable": {
"title": "Explore is Unavailable",
"embeddingsReindexing": {
"context": "Explore can be used after tracked object embeddings have finished reindexing.",
"startingUp": "Starting up...",
"estimatedTime": "Estimated time remaining:",
"finishingShortly": "Finishing shortly",
"step": {
"thumbnailsEmbedded": "Thumbnails embedded: ",
"descriptionsEmbedded": "Descriptions embedded: ",
"trackedObjectsProcessed": "Tracked objects processed: "
}
},
"downloadingModels": {
"context": "Frigate is downloading the necessary embeddings models to support the Semantic Search feature. This may take several minutes depending on the speed of your network connection.",
"setup": {
"visionModel": "Vision model",
"visionModelFeatureExtractor": "Vision model feature extractor",
"textModel": "Text model",
"textTokenizer": "Text tokenizer"
},
"tips": {
"context": "You may want to reindex the embeddings of your tracked objects once the models are downloaded.",
"documentation": "Read the documentation"
},
"error": "An error has occurred. Check Frigate logs."
}
},
"trackedObjectDetails": "Tracked Object Details",
"type": {
"details": "details",

View File

@ -69,6 +69,7 @@
"stats": {
"ffmpegHighCpuUsage": "{{camera}} has high FFMPEG CPU usage ({{ffmpegAvg}}%)",
"detectHighCpuUsage": "{{camera}} has high detect CPU usage ({{detectAvg}}%)",
"healthy": "System is healthy"
"healthy": "System is healthy",
"reindexingEmbeddings": "Reindexing embeddings ({{processed}}% complete)"
}
}

View File

@ -110,6 +110,7 @@
"documentation": "文档",
"live": "实时监控",
"live.allCameras": "所有摄像头",
"live.cameras": "摄像头",
"review": "回放",
"explore": "探测",
"export": "导出",

View File

@ -1,4 +1,32 @@
{
"exploreIsUnavailable": {
"title": "探索功能不可用",
"embeddingsReindexing": {
"context": "跟踪对象嵌入重新索引完成后,可以使用探索功能。",
"startingUp": "启动中...",
"estimatedTime": "预计剩余时间:",
"finishingShortly": "即将完成",
"step": {
"thumbnailsEmbedded": "缩略图嵌入:",
"descriptionsEmbedded": "描述嵌入:",
"trackedObjectsProcessed": "跟踪对象已处理:"
}
},
"downloadingModels": {
"context": "Frigate正在下载支持语义搜索功能所需的嵌入模型。根据网络连接速度这可能需要几分钟。",
"setup": {
"visionModel": "视觉模型",
"visionModelFeatureExtractor": "视觉模型特征提取器",
"textModel": "文本模型",
"textTokenizer": "文本分词器"
},
"tips": {
"context": "模型下载完成后,您可能需要重新索引跟踪对象的嵌入。",
"documentation": "阅读文档(英文)"
},
"error": "发生错误。请检查Frigate日志。"
}
},
"trackedObjectDetails": "探测对象详情",
"type": {
"details": "详情",

View File

@ -69,6 +69,7 @@
"stats": {
"ffmpegHighCpuUsage": "{{camera}} 的 FFMPEG CPU 使用率较高({{ffmpegAvg}}%",
"detectHighCpuUsage": "{{camera}} 的 探测 CPU 使用率较高({{detectAvg}}%",
"healthy": "系统运行正常"
"healthy": "系统运行正常",
"reindexingEmbeddings": "正在重新索引嵌入(已完成 {{processed}}%"
}
}

View File

@ -54,14 +54,19 @@ export default function Statusbar() {
clearMessages("embeddings-reindex");
addMessage(
"embeddings-reindex",
`Reindexing embeddings (${Math.floor((reindexState.processed_objects / reindexState.total_objects) * 100)}% complete)`,
t("stats.reindexingEmbeddings", {
processed: Math.floor(
(reindexState.processed_objects / reindexState.total_objects) *
100,
),
}),
);
}
if (reindexState.status === "completed") {
clearMessages("embeddings-reindex");
}
}
}, [reindexState, addMessage, clearMessages]);
}, [reindexState, addMessage, clearMessages, t]);
return (
<div className="absolute bottom-0 left-0 right-0 z-10 flex h-8 w-full items-center justify-between border-t border-secondary-highlight bg-background_alt px-4 dark:text-secondary-foreground">

View File

@ -38,7 +38,7 @@ export function CamerasFilterButton({
const buttonText = useMemo(() => {
if (isMobile) {
return "Cameras";
return t("menu.live.cameras", { ns: "common" });
}
if (!selectedCameras || selectedCameras.length == 0) {

View File

@ -109,7 +109,7 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
{t("menu.user.current", {
user: profile?.username || t("menu.user.anonymous"),
})}{" "}
{t("role." + profile?.role) && `(${t("role." + profile.role)})`}
{t("role." + profile?.role) && `(${t("role." + profile?.role)})`}
</DropdownMenuLabel>
<DropdownMenuSeparator className={isDesktop ? "mt-3" : "mt-1"} />
{profile?.username && profile.username !== "anonymous" && (

View File

@ -170,7 +170,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
user: profile?.username || t("menu.user.anonymous"),
})}{" "}
{t("role." + profile?.role) &&
`(${t("role." + profile.role)})`}
`(${t("role." + profile?.role)})`}
</DropdownMenuLabel>
<DropdownMenuSeparator
className={isDesktop ? "mt-3" : "mt-1"}

View File

@ -15,6 +15,7 @@ import { formatSecondsToDuration } from "@/utils/dateUtil";
import SearchView from "@/views/search/SearchView";
import { useCallback, useEffect, useMemo, useState } from "react";
import { isMobileOnly } from "react-device-detect";
import { useTranslation } from "react-i18next";
import { LuCheck, LuExternalLink, LuX } from "react-icons/lu";
import { TbExclamationCircle } from "react-icons/tb";
import { Link } from "react-router-dom";
@ -27,6 +28,8 @@ const API_LIMIT = 25;
export default function Explore() {
// search field handler
const { t } = useTranslation(["views/explore"]);
const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false,
});
@ -353,13 +356,12 @@ export default function Explore() {
<div className="flex max-w-96 flex-col items-center justify-center space-y-3 rounded-lg bg-background/50 p-5">
<div className="my-5 flex flex-col items-center gap-2 text-xl">
<TbExclamationCircle className="mb-3 size-10" />
<div>Explore is Unavailable</div>
<div>{t("exploreIsUnavailable.title")}</div>
</div>
{embeddingsReindexing && allModelsLoaded && (
<>
<div className="text-center text-primary-variant">
Explore can be used after tracked object embeddings have
finished reindexing.
{t("exploreIsUnavailable.embeddingsReindexing.context")}
</div>
<div className="pt-5 text-center">
<AnimatedCircularProgressBar
@ -375,29 +377,35 @@ export default function Explore() {
<div className="mb-3 flex flex-col items-center justify-center gap-1">
<div className="text-primary-variant">
{reindexState.time_remaining === -1
? "Starting up..."
: "Estimated time remaining:"}
? t(
"exploreIsUnavailable.embeddingsReindexing.startingUp",
)
: t(
"exploreIsUnavailable.embeddingsReindexing.estimatedTime",
)}
</div>
{reindexState.time_remaining >= 0 &&
(formatSecondsToDuration(reindexState.time_remaining) ||
"Finishing shortly")}
t(
"exploreIsUnavailable.embeddingsReindexing.finishingShortly",
))}
</div>
)}
<div className="flex flex-row items-center justify-center gap-3">
<span className="text-primary-variant">
Thumbnails embedded:
t("exploreIsUnavailable.embeddingsReindexing.step.thumbnailsEmbedded")
</span>
{reindexState.thumbnails}
</div>
<div className="flex flex-row items-center justify-center gap-3">
<span className="text-primary-variant">
Descriptions embedded:
t("exploreIsUnavailable.embeddingsReindexing.step.descriptionsEmbedded")
</span>
{reindexState.descriptions}
</div>
<div className="flex flex-row items-center justify-center gap-3">
<span className="text-primary-variant">
Tracked objects processed:
t("exploreIsUnavailable.embeddingsReindexing.step.trackedObjectsProcessed")
</span>
{reindexState.processed_objects} /{" "}
{reindexState.total_objects}
@ -408,26 +416,32 @@ export default function Explore() {
{!allModelsLoaded && (
<>
<div className="text-center text-primary-variant">
Frigate is downloading the necessary embeddings models to
support the Semantic Search feature. This may take several
minutes depending on the speed of your network connection.
{t("exploreIsUnavailable.downloadingModels.context")}
</div>
<div className="flex w-96 flex-col gap-2 py-5">
<div className="flex flex-row items-center justify-center gap-2">
{renderModelStateIcon(visionModelState)}
Vision model
{t(
"exploreIsUnavailable.downloadingModels.setup.visionModel",
)}
</div>
<div className="flex flex-row items-center justify-center gap-2">
{renderModelStateIcon(visionFeatureExtractorState)}
Vision model feature extractor
{t(
"exploreIsUnavailable.downloadingModels.setup.visionModelFeatureExtractor",
)}
</div>
<div className="flex flex-row items-center justify-center gap-2">
{renderModelStateIcon(textModelState)}
Text model
{t(
"exploreIsUnavailable.downloadingModels.setup.textModel",
)}
</div>
<div className="flex flex-row items-center justify-center gap-2">
{renderModelStateIcon(textTokenizerState)}
Text tokenizer
{t(
"exploreIsUnavailable.downloadingModels.setup.textTokenizer",
)}
</div>
</div>
{(textModelState === "error" ||
@ -435,12 +449,11 @@ export default function Explore() {
visionModelState === "error" ||
visionFeatureExtractorState === "error") && (
<div className="my-3 max-w-96 text-center text-danger">
An error has occurred. Check Frigate logs.
{t("exploreIsUnavailable.downloadingModels.error")}
</div>
)}
<div className="text-center text-primary-variant">
You may want to reindex the embeddings of your tracked objects
once the models are downloaded.
{t("exploreIsUnavailable.downloadingModels.tips.context")}
</div>
<div className="flex items-center text-primary-variant">
<Link
@ -449,7 +462,9 @@ export default function Explore() {
rel="noopener noreferrer"
className="inline"
>
Read the documentation{" "}
{t(
"exploreIsUnavailable.downloadingModels.tips.documentation",
)}{" "}
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>